sigev_thread.c revision 156529
1235288Sadrian/*
2235288Sadrian * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3235288Sadrian * All rights reserved.
4235288Sadrian *
5235288Sadrian * Redistribution and use in source and binary forms, with or without
6235288Sadrian * modification, are permitted provided that the following conditions
7235288Sadrian * are met:
8235288Sadrian * 1. Redistributions of source code must retain the above copyright
9235288Sadrian *    notice unmodified, this list of conditions, and the following
10235288Sadrian *    disclaimer.
11235288Sadrian * 2. Redistributions in binary form must reproduce the above copyright
12235288Sadrian *    notice, this list of conditions and the following disclaimer in the
13235288Sadrian *    documentation and/or other materials provided with the distribution.
14235288Sadrian *
15235288Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16235288Sadrian * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17235288Sadrian * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18235288Sadrian * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19235288Sadrian * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20235288Sadrian * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21235288Sadrian * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22235288Sadrian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23235288Sadrian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24235288Sadrian * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25235288Sadrian *
26235288Sadrian * $FreeBSD: head/lib/librt/sigev_thread.c 156529 2006-03-10 08:01:23Z davidxu $
27235288Sadrian *
28235288Sadrian */
29235288Sadrian
30235288Sadrian#include <sys/types.h>
31235288Sadrian#include <machine/atomic.h>
32235288Sadrian
33235288Sadrian#include "namespace.h"
34257284Sglebius#include <err.h>
35235288Sadrian#include <errno.h>
36235288Sadrian#include <ucontext.h>
37235288Sadrian#include <sys/thr.h>
38235288Sadrian#include <stdio.h>
39235288Sadrian#include <stdlib.h>
40235288Sadrian#include <string.h>
41235288Sadrian#include <signal.h>
42257284Sglebius#include <pthread.h>
43235288Sadrian#include "un-namespace.h"
44235288Sadrian
45235288Sadrian#include "sigev_thread.h"
46235288Sadrian
47235288SadrianLIST_HEAD(sigev_list_head, sigev_node);
48235288Sadrian#define HASH_QUEUES		17
49235288Sadrian#define	HASH(t, id)		((((id) << 3) + (t)) % HASH_QUEUES)
50235288Sadrian
51235288Sadrianstatic struct sigev_list_head	sigev_hash[HASH_QUEUES];
52235288Sadrianstatic struct sigev_list_head	sigev_all;
53235288Sadrianstatic LIST_HEAD(,sigev_thread)	sigev_threads;
54235288Sadrianstatic int			sigev_generation;
55235288Sadrianstatic pthread_mutex_t		*sigev_list_mtx;
56235288Sadrianstatic pthread_once_t		sigev_once = PTHREAD_ONCE_INIT;
57235288Sadrianstatic pthread_once_t		sigev_once_default = PTHREAD_ONCE_INIT;
58235288Sadrianstatic struct sigev_thread	*sigev_default_thread;
59235288Sadrianstatic pthread_attr_t		sigev_default_attr;
60235288Sadrianstatic int			atfork_registered;
61235288Sadrian
62235288Sadrianstatic void	__sigev_fork_prepare(void);
63253572Sloosstatic void	__sigev_fork_parent(void);
64235288Sadrianstatic void	__sigev_fork_child(void);
65235323Sadrianstatic struct sigev_thread	*sigev_thread_create(int);
66235288Sadrianstatic void	*sigev_service_loop(void *);
67235288Sadrianstatic void	*worker_routine(void *);
68235288Sadrianstatic void	worker_cleanup(void *);
69256582Sadrian
70235288Sadrian#pragma weak pthread_create
71235288Sadrian
72235288Sadrianstatic void
73235288Sadrianattrcopy(pthread_attr_t *src, pthread_attr_t *dst)
74235288Sadrian{
75235288Sadrian	struct sched_param sched;
76235288Sadrian	void *a;
77235288Sadrian	size_t u;
78235288Sadrian	int v;
79235288Sadrian
80235288Sadrian	_pthread_attr_getschedpolicy(src, &v);
81235288Sadrian	_pthread_attr_setschedpolicy(dst, v);
82235288Sadrian
83235288Sadrian	_pthread_attr_getinheritsched(src, &v);
84235288Sadrian	_pthread_attr_setinheritsched(dst, v);
85235288Sadrian
86235288Sadrian	_pthread_attr_getschedparam(src, &sched);
87235288Sadrian	_pthread_attr_setschedparam(dst, &sched);
88235288Sadrian
89235288Sadrian	_pthread_attr_getscope(src, &v);
90235288Sadrian	_pthread_attr_setscope(dst, v);
91235288Sadrian
92235288Sadrian	_pthread_attr_getstacksize(src, &u);
93235288Sadrian	_pthread_attr_setstacksize(dst, u);
94235323Sadrian
95235323Sadrian	_pthread_attr_getstackaddr(src, &a);
96235323Sadrian	_pthread_attr_setstackaddr(src, a);
97235323Sadrian
98235323Sadrian	_pthread_attr_getguardsize(src, &u);
99256582Sadrian	_pthread_attr_setguardsize(dst, u);
100241577Sray}
101235323Sadrian
102235323Sadrianstatic __inline int
103235323Sadrianhave_threads(void)
104256582Sadrian{
105256582Sadrian	return (&pthread_create != NULL);
106256582Sadrian}
107256582Sadrian
108256582Sadrianvoid
109256582Sadrian__sigev_thread_init(void)
110256582Sadrian{
111256582Sadrian	static int inited = 0;
112256582Sadrian	pthread_mutexattr_t mattr;
113235323Sadrian	int i;
114235288Sadrian
115262201Sadrian	_pthread_mutexattr_init(&mattr);
116262201Sadrian	_pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL);
117256582Sadrian	sigev_list_mtx = malloc(sizeof(pthread_mutex_t));
118256582Sadrian	_pthread_mutex_init(sigev_list_mtx, &mattr);
119235288Sadrian	_pthread_mutexattr_destroy(&mattr);
120235288Sadrian
121235288Sadrian	for (i = 0; i < HASH_QUEUES; ++i)
122256582Sadrian		LIST_INIT(&sigev_hash[i]);
123235288Sadrian	LIST_INIT(&sigev_all);
124235288Sadrian	LIST_INIT(&sigev_threads);
125235288Sadrian	sigev_default_thread = NULL;
126256582Sadrian	if (atfork_registered == 0) {
127256582Sadrian		_pthread_atfork(
128256582Sadrian			__sigev_fork_prepare,
129235288Sadrian			__sigev_fork_parent,
130235288Sadrian			__sigev_fork_child);
131235288Sadrian		atfork_registered = 1;
132262202Sadrian	}
133262202Sadrian	if (!inited) {
134262202Sadrian		_pthread_attr_init(&sigev_default_attr);
135262202Sadrian		_pthread_attr_setscope(&sigev_default_attr,
136262202Sadrian			PTHREAD_SCOPE_SYSTEM);
137235288Sadrian		_pthread_attr_setdetachstate(&sigev_default_attr,
138235288Sadrian			PTHREAD_CREATE_DETACHED);
139235288Sadrian		inited = 1;
140235323Sadrian	}
141235323Sadrian	sigev_default_thread = sigev_thread_create(0);
142256582Sadrian}
143256582Sadrian
144235288Sadrianint
145235288Sadrian__sigev_check_init(void)
146235288Sadrian{
147235288Sadrian	if (!have_threads())
148235288Sadrian		return (-1);
149235288Sadrian
150235288Sadrian	_pthread_once(&sigev_once, __sigev_thread_init);
151235288Sadrian	return (sigev_default_thread != NULL) ? 0 : -1;
152235288Sadrian}
153235288Sadrian
154235288Sadrianstatic void
155235288Sadrian__sigev_fork_prepare(void)
156235288Sadrian{
157235288Sadrian}
158235288Sadrian
159235288Sadrianstatic void
160235288Sadrian__sigev_fork_parent(void)
161235288Sadrian{
162235288Sadrian}
163235288Sadrian
164235288Sadrianstatic void
165235288Sadrian__sigev_fork_child(void)
166235288Sadrian{
167235288Sadrian	/*
168235288Sadrian	 * This is a hack, the thread libraries really should
169235288Sadrian	 * check if the handlers were already registered in
170235288Sadrian	 * pthread_atfork().
171235288Sadrian	 */
172235288Sadrian	atfork_registered = 1;
173235288Sadrian	memcpy(&sigev_once, &sigev_once_default, sizeof(sigev_once));
174235288Sadrian	__sigev_thread_init();
175235288Sadrian}
176235288Sadrian
177235288Sadrianvoid
178235288Sadrian__sigev_list_lock(void)
179235288Sadrian{
180235288Sadrian	_pthread_mutex_lock(sigev_list_mtx);
181235288Sadrian}
182235288Sadrian
183235288Sadrianvoid
184235288Sadrian__sigev_list_unlock(void)
185235288Sadrian{
186235288Sadrian	_pthread_mutex_unlock(sigev_list_mtx);
187253572Sloos}
188253572Sloos
189253572Sloosstruct sigev_node *
190253572Sloos__sigev_alloc(int type, const struct sigevent *evp, struct sigev_node *prev,
191253572Sloos	int usedefault)
192253572Sloos{
193253572Sloos	struct sigev_node *sn;
194253572Sloos
195253572Sloos	sn = calloc(1, sizeof(*sn));
196253572Sloos	if (sn != NULL) {
197253572Sloos		sn->sn_value = evp->sigev_value;
198253572Sloos		sn->sn_func  = evp->sigev_notify_function;
199253572Sloos		sn->sn_gen   = atomic_fetchadd_int(&sigev_generation, 1);
200253572Sloos		sn->sn_type  = type;
201253572Sloos		_pthread_attr_init(&sn->sn_attr);
202253572Sloos		_pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED);
203253572Sloos		if (evp->sigev_notify_attributes)
204253572Sloos			attrcopy(evp->sigev_notify_attributes, &sn->sn_attr);
205253572Sloos		if (prev) {
206253572Sloos			__sigev_list_lock();
207253572Sloos			prev->sn_tn->tn_refcount++;
208253572Sloos			__sigev_list_unlock();
209253572Sloos			sn->sn_tn = prev->sn_tn;
210253572Sloos		} else {
211253572Sloos			sn->sn_tn = sigev_thread_create(usedefault);
212253572Sloos			if (sn->sn_tn == NULL) {
213253572Sloos				_pthread_attr_destroy(&sn->sn_attr);
214253572Sloos				free(sn);
215253572Sloos				sn = NULL;
216253572Sloos			}
217253572Sloos		}
218253572Sloos	}
219253572Sloos	return (sn);
220253572Sloos}
221253572Sloos
222253572Sloosvoid
223253572Sloos__sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp,
224253572Sloos	sigev_id_t id)
225253572Sloos{
226253572Sloos	/*
227262203Sadrian	 * Build a new sigevent, and tell kernel to deliver SIGSERVICE
228253572Sloos	 * signal to the new thread.
229253572Sloos	 */
230253572Sloos	newevp->sigev_notify = SIGEV_THREAD_ID;
231262203Sadrian	newevp->sigev_signo  = SIGSERVICE;
232262203Sadrian	newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid;
233262203Sadrian	newevp->sigev_value.sival_ptr = (void *)id;
234262203Sadrian}
235262203Sadrian
236262203Sadrianvoid
237262203Sadrian__sigev_free(struct sigev_node *sn)
238262203Sadrian{
239262203Sadrian	_pthread_attr_destroy(&sn->sn_attr);
240262203Sadrian	free(sn);
241262203Sadrian}
242262203Sadrian
243262203Sadrianstruct sigev_node *
244253572Sloos__sigev_find(int type, sigev_id_t id)
245253572Sloos{
246253572Sloos	struct sigev_node *sn;
247253572Sloos	int chain = HASH(type, id);
248253572Sloos
249253572Sloos	LIST_FOREACH(sn, &sigev_hash[chain], sn_link) {
250253572Sloos		if (sn->sn_type == type && sn->sn_id == id)
251253572Sloos			break;
252253572Sloos	}
253253572Sloos	return (sn);
254235288Sadrian}
255235288Sadrian
256235288Sadrianint
257235288Sadrian__sigev_register(struct sigev_node *sn)
258262203Sadrian{
259235288Sadrian	int chain = HASH(sn->sn_type, sn->sn_id);
260235288Sadrian
261235288Sadrian	LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link);
262235288Sadrian	return (0);
263235288Sadrian}
264235288Sadrian
265235288Sadrianint
266235288Sadrian__sigev_delete(int type, sigev_id_t id)
267235288Sadrian{
268235288Sadrian	struct sigev_node *sn;
269235288Sadrian
270235288Sadrian	sn = __sigev_find(type, id);
271235288Sadrian	if (sn != NULL)
272235323Sadrian		return (__sigev_delete_node(sn));
273235323Sadrian	return (0);
274256582Sadrian}
275256582Sadrian
276235323Sadrianint
277235288Sadrian__sigev_delete_node(struct sigev_node *sn)
278235288Sadrian{
279235288Sadrian	LIST_REMOVE(sn, sn_link);
280235288Sadrian
281235288Sadrian	if (--sn->sn_tn->tn_refcount == 0)
282235288Sadrian		_pthread_kill(sn->sn_tn->tn_thread, SIGSERVICE);
283235288Sadrian	if (sn->sn_flags & SNF_WORKING)
284235288Sadrian		sn->sn_flags |= SNF_REMOVED;
285253572Sloos	else
286235288Sadrian		__sigev_free(sn);
287235288Sadrian	return (0);
288235288Sadrian}
289235288Sadrian
290235288Sadrianstatic sigev_id_t
291235288Sadriansigev_get_id(siginfo_t *si)
292235288Sadrian{
293256582Sadrian	switch(si->si_code) {
294235288Sadrian	case SI_TIMER:
295235288Sadrian		return (si->si_timerid);
296235288Sadrian	case SI_MESGQ:
297235288Sadrian		return (si->si_mqd);
298235288Sadrian	case SI_ASYNCIO:
299235288Sadrian		return (sigev_id_t)si->si_value.sival_ptr;
300235288Sadrian	}
301235288Sadrian	return (-1);
302235288Sadrian}
303256582Sadrian
304256582Sadrianstatic struct sigev_thread *
305235288Sadriansigev_thread_create(int usedefault)
306253572Sloos{
307253572Sloos	struct sigev_thread *tn;
308253572Sloos	sigset_t set, oset;
309253572Sloos	int ret;
310253572Sloos
311235288Sadrian	if (usedefault && sigev_default_thread) {
312235288Sadrian		__sigev_list_lock();
313256582Sadrian		sigev_default_thread->tn_refcount++;
314256582Sadrian		__sigev_list_unlock();
315256582Sadrian		return (sigev_default_thread);
316256582Sadrian	}
317235288Sadrian
318235288Sadrian	tn = malloc(sizeof(*tn));
319235288Sadrian	tn->tn_cur = NULL;
320235288Sadrian	tn->tn_lwpid = -1;
321253572Sloos	tn->tn_refcount = 1;
322262203Sadrian	_pthread_cond_init(&tn->tn_cv, NULL);
323262203Sadrian
324262203Sadrian	/* for debug */
325253572Sloos	__sigev_list_lock();
326235288Sadrian	LIST_INSERT_HEAD(&sigev_threads, tn, tn_link);
327235288Sadrian	__sigev_list_unlock();
328235288Sadrian
329235288Sadrian	sigfillset(&set);	/* SIGSERVICE is masked. */
330235288Sadrian	sigdelset(&set, SIGBUS);
331235288Sadrian	sigdelset(&set, SIGILL);
332235288Sadrian	sigdelset(&set, SIGFPE);
333253572Sloos	sigdelset(&set, SIGSEGV);
334253572Sloos	sigdelset(&set, SIGTRAP);
335253572Sloos	_sigprocmask(SIG_SETMASK, &set, &oset);
336253572Sloos	ret = pthread_create(&tn->tn_thread, &sigev_default_attr,
337253572Sloos		 sigev_service_loop, tn);
338235288Sadrian	_sigprocmask(SIG_SETMASK, &oset, NULL);
339235288Sadrian
340235288Sadrian	if (ret != 0) {
341235288Sadrian		__sigev_list_lock();
342235288Sadrian		LIST_REMOVE(tn, tn_link);
343235288Sadrian		__sigev_list_unlock();
344235288Sadrian		free(tn);
345241578Sray		tn = NULL;
346241578Sray	} else {
347235288Sadrian		/* wait the thread to get its lwpid */
348241578Sray
349235288Sadrian		__sigev_list_lock();
350235288Sadrian		while (tn->tn_lwpid == -1)
351235288Sadrian			_pthread_cond_wait(&tn->tn_cv, sigev_list_mtx);
352235288Sadrian		__sigev_list_unlock();
353235288Sadrian	}
354235288Sadrian	return (tn);
355235288Sadrian}
356235288Sadrian
357235288Sadrian/*
358235288Sadrian * The thread receives notification from kernel and creates
359235288Sadrian * a thread to call user callback function.
360235288Sadrian */
361235288Sadrianstatic void *
362235288Sadriansigev_service_loop(void *arg)
363235288Sadrian{
364235288Sadrian	static int failure;
365235288Sadrian
366235288Sadrian	siginfo_t si;
367235288Sadrian	sigset_t set;
368235288Sadrian	struct sigev_thread *tn;
369235288Sadrian	struct sigev_node *sn;
370235288Sadrian	sigev_id_t id;
371235288Sadrian	pthread_t td;
372235288Sadrian	int ret;
373235288Sadrian
374235288Sadrian	tn = arg;
375235288Sadrian	thr_self(&tn->tn_lwpid);
376235288Sadrian	__sigev_list_lock();
377235288Sadrian	_pthread_cond_broadcast(&tn->tn_cv);
378235288Sadrian	__sigev_list_unlock();
379235288Sadrian
380235288Sadrian	sigemptyset(&set);
381235288Sadrian	sigaddset(&set, SIGSERVICE);
382235288Sadrian	for (;;) {
383235288Sadrian		ret = sigwaitinfo(&set, &si);
384235288Sadrian
385235288Sadrian		__sigev_list_lock();
386235288Sadrian		if (tn->tn_refcount == 0) {
387235288Sadrian			LIST_REMOVE(tn, tn_link);
388235288Sadrian			__sigev_list_unlock();
389235288Sadrian			free(tn);
390235288Sadrian			break;
391235288Sadrian		}
392235288Sadrian
393235288Sadrian		if (ret == -1) {
394235288Sadrian			__sigev_list_unlock();
395235288Sadrian			continue;
396235288Sadrian		}
397235288Sadrian
398235288Sadrian		id = sigev_get_id(&si);
399235288Sadrian		sn = __sigev_find(si.si_code, id);
400235288Sadrian		if (sn == NULL) {
401235288Sadrian			__sigev_list_unlock();
402235288Sadrian			continue;
403235288Sadrian		}
404235288Sadrian
405235288Sadrian		sn->sn_info = si;
406235288Sadrian		if (sn->sn_flags & SNF_SYNC)
407235288Sadrian			tn->tn_cur = sn;
408235288Sadrian		else
409235288Sadrian			tn->tn_cur = NULL;
410235288Sadrian		sn->sn_flags |= SNF_WORKING;
411235288Sadrian		__sigev_list_unlock();
412235288Sadrian
413235288Sadrian		ret = pthread_create(&td, &sn->sn_attr, worker_routine, sn);
414235288Sadrian		if (ret != 0) {
415235288Sadrian			if (failure++ < 5)
416235288Sadrian				warnc(ret, "%s:%s failed to create thread.\n",
417235288Sadrian					__FILE__, __func__);
418235288Sadrian
419235288Sadrian			__sigev_list_lock();
420235288Sadrian			sn->sn_flags &= ~SNF_WORKING;
421235288Sadrian			if (sn->sn_flags & SNF_REMOVED)
422235288Sadrian				__sigev_free(sn);
423235288Sadrian			__sigev_list_unlock();
424235288Sadrian		} else if (tn->tn_cur) {
425235288Sadrian			__sigev_list_lock();
426235288Sadrian			while (tn->tn_cur)
427235288Sadrian				_pthread_cond_wait(&tn->tn_cv, sigev_list_mtx);
428235288Sadrian			__sigev_list_unlock();
429235288Sadrian		}
430235288Sadrian	}
431235288Sadrian	return (0);
432235288Sadrian}
433235288Sadrian
434235288Sadrian/*
435235288Sadrian * newly created worker thread to call user callback function.
436235288Sadrian */
437235288Sadrianstatic void *
438235288Sadrianworker_routine(void *arg)
439235288Sadrian{
440235288Sadrian	struct sigev_node *sn = arg;
441235288Sadrian
442235288Sadrian	_pthread_cleanup_push(worker_cleanup, sn);
443235288Sadrian	sn->sn_dispatch(sn);
444235288Sadrian	_pthread_cleanup_pop(1);
445235288Sadrian
446235288Sadrian	return (0);
447235288Sadrian}
448235288Sadrian
449235288Sadrian/* clean up a notification after dispatch. */
450235288Sadrianstatic void
451235288Sadrianworker_cleanup(void *arg)
452235288Sadrian{
453235288Sadrian	struct sigev_node *sn = arg;
454235288Sadrian
455235288Sadrian	__sigev_list_lock();
456241578Sray	if (sn->sn_flags & SNF_SYNC) {
457241578Sray		sn->sn_tn->tn_cur = NULL;
458235288Sadrian		_pthread_cond_broadcast(&sn->sn_tn->tn_cv);
459235288Sadrian	}
460235288Sadrian	if (sn->sn_flags & SNF_REMOVED)
461235288Sadrian		__sigev_free(sn);
462235288Sadrian	else
463235288Sadrian		sn->sn_flags &= ~SNF_WORKING;
464235288Sadrian	__sigev_list_unlock();
465235288Sadrian}
466256582Sadrian