xref: /freebsd-src/lib/librt/sigev_thread.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
15e53a4f9SPedro F. Giffuni /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
35e53a4f9SPedro F. Giffuni  *
44acaec8fSDavid Xu  * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
54acaec8fSDavid Xu  * All rights reserved.
64acaec8fSDavid Xu  *
74acaec8fSDavid Xu  * Redistribution and use in source and binary forms, with or without
84acaec8fSDavid Xu  * modification, are permitted provided that the following conditions
94acaec8fSDavid Xu  * are met:
104acaec8fSDavid Xu  * 1. Redistributions of source code must retain the above copyright
114acaec8fSDavid Xu  *    notice unmodified, this list of conditions, and the following
124acaec8fSDavid Xu  *    disclaimer.
134acaec8fSDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
144acaec8fSDavid Xu  *    notice, this list of conditions and the following disclaimer in the
154acaec8fSDavid Xu  *    documentation and/or other materials provided with the distribution.
164acaec8fSDavid Xu  *
174acaec8fSDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
184acaec8fSDavid Xu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
194acaec8fSDavid Xu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
204acaec8fSDavid Xu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
214acaec8fSDavid Xu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
224acaec8fSDavid Xu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
234acaec8fSDavid Xu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
244acaec8fSDavid Xu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
254acaec8fSDavid Xu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
264acaec8fSDavid Xu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
274acaec8fSDavid Xu  *
284acaec8fSDavid Xu  */
294acaec8fSDavid Xu 
304acaec8fSDavid Xu #include <sys/types.h>
314acaec8fSDavid Xu 
324acaec8fSDavid Xu #include "namespace.h"
334acaec8fSDavid Xu #include <err.h>
346348ace8SDavid Xu #include <errno.h>
354acaec8fSDavid Xu #include <ucontext.h>
364acaec8fSDavid Xu #include <sys/thr.h>
37cd8b04c9SEd Schouten #include <stdatomic.h>
384acaec8fSDavid Xu #include <stdio.h>
394acaec8fSDavid Xu #include <stdlib.h>
404acaec8fSDavid Xu #include <string.h>
414acaec8fSDavid Xu #include <signal.h>
424acaec8fSDavid Xu #include <pthread.h>
434acaec8fSDavid Xu #include "un-namespace.h"
444acaec8fSDavid Xu 
454acaec8fSDavid Xu #include "sigev_thread.h"
464acaec8fSDavid Xu 
474acaec8fSDavid Xu LIST_HEAD(sigev_list_head, sigev_node);
484acaec8fSDavid Xu #define HASH_QUEUES		17
494acaec8fSDavid Xu #define	HASH(t, id)		((((id) << 3) + (t)) % HASH_QUEUES)
506348ace8SDavid Xu 
514acaec8fSDavid Xu static struct sigev_list_head	sigev_hash[HASH_QUEUES];
524acaec8fSDavid Xu static struct sigev_list_head	sigev_all;
53c5a4f4efSDavid Xu static LIST_HEAD(,sigev_thread)	sigev_threads;
54cd8b04c9SEd Schouten static atomic_int		sigev_generation;
554acaec8fSDavid Xu static pthread_mutex_t		*sigev_list_mtx;
564acaec8fSDavid Xu static pthread_once_t		sigev_once = PTHREAD_ONCE_INIT;
576348ace8SDavid Xu static pthread_once_t		sigev_once_default = PTHREAD_ONCE_INIT;
58c5a4f4efSDavid Xu static struct sigev_thread	*sigev_default_thread;
596348ace8SDavid Xu static pthread_attr_t		sigev_default_attr;
606348ace8SDavid Xu static int			atfork_registered;
614acaec8fSDavid Xu 
624acaec8fSDavid Xu static void	__sigev_fork_prepare(void);
634acaec8fSDavid Xu static void	__sigev_fork_parent(void);
644acaec8fSDavid Xu static void	__sigev_fork_child(void);
65c5a4f4efSDavid Xu static struct sigev_thread	*sigev_thread_create(int);
664acaec8fSDavid Xu static void	*sigev_service_loop(void *);
67c5a4f4efSDavid Xu static void	*worker_routine(void *);
68c5a4f4efSDavid Xu static void	worker_cleanup(void *);
694acaec8fSDavid Xu 
70d688b9ceSDaniel Eischen #pragma weak _pthread_create
714acaec8fSDavid Xu 
72c5a4f4efSDavid Xu static void
attrcopy(pthread_attr_t * src,pthread_attr_t * dst)73c5a4f4efSDavid Xu attrcopy(pthread_attr_t *src, pthread_attr_t *dst)
744acaec8fSDavid Xu {
75c5a4f4efSDavid Xu 	struct sched_param sched;
76c5a4f4efSDavid Xu 	void *a;
77c5a4f4efSDavid Xu 	size_t u;
78c5a4f4efSDavid Xu 	int v;
794acaec8fSDavid Xu 
80c5a4f4efSDavid Xu 	_pthread_attr_getschedpolicy(src, &v);
81c5a4f4efSDavid Xu 	_pthread_attr_setschedpolicy(dst, v);
824acaec8fSDavid Xu 
83c5a4f4efSDavid Xu 	_pthread_attr_getinheritsched(src, &v);
84c5a4f4efSDavid Xu 	_pthread_attr_setinheritsched(dst, v);
85c5a4f4efSDavid Xu 
86c5a4f4efSDavid Xu 	_pthread_attr_getschedparam(src, &sched);
87c5a4f4efSDavid Xu 	_pthread_attr_setschedparam(dst, &sched);
88c5a4f4efSDavid Xu 
89c5a4f4efSDavid Xu 	_pthread_attr_getscope(src, &v);
90c5a4f4efSDavid Xu 	_pthread_attr_setscope(dst, v);
91c5a4f4efSDavid Xu 
92c5a4f4efSDavid Xu 	_pthread_attr_getstacksize(src, &u);
93c5a4f4efSDavid Xu 	_pthread_attr_setstacksize(dst, u);
94c5a4f4efSDavid Xu 
95c5a4f4efSDavid Xu 	_pthread_attr_getstackaddr(src, &a);
96c5a4f4efSDavid Xu 	_pthread_attr_setstackaddr(src, a);
97c5a4f4efSDavid Xu 
98c5a4f4efSDavid Xu 	_pthread_attr_getguardsize(src, &u);
99c5a4f4efSDavid Xu 	_pthread_attr_setguardsize(dst, u);
1004acaec8fSDavid Xu }
1014acaec8fSDavid Xu 
1024acaec8fSDavid Xu static __inline int
have_threads(void)1034acaec8fSDavid Xu have_threads(void)
1044acaec8fSDavid Xu {
105d688b9ceSDaniel Eischen 	return (&_pthread_create != NULL);
1064acaec8fSDavid Xu }
1074acaec8fSDavid Xu 
1084acaec8fSDavid Xu void
__sigev_thread_init(void)1094acaec8fSDavid Xu __sigev_thread_init(void)
1104acaec8fSDavid Xu {
1116348ace8SDavid Xu 	static int inited = 0;
112c5a4f4efSDavid Xu 	pthread_mutexattr_t mattr;
1134acaec8fSDavid Xu 	int i;
1144acaec8fSDavid Xu 
115c5a4f4efSDavid Xu 	_pthread_mutexattr_init(&mattr);
116c5a4f4efSDavid Xu 	_pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL);
1174acaec8fSDavid Xu 	sigev_list_mtx = malloc(sizeof(pthread_mutex_t));
118c5a4f4efSDavid Xu 	_pthread_mutex_init(sigev_list_mtx, &mattr);
119c5a4f4efSDavid Xu 	_pthread_mutexattr_destroy(&mattr);
120c5a4f4efSDavid Xu 
1214acaec8fSDavid Xu 	for (i = 0; i < HASH_QUEUES; ++i)
1224acaec8fSDavid Xu 		LIST_INIT(&sigev_hash[i]);
1234acaec8fSDavid Xu 	LIST_INIT(&sigev_all);
124c5a4f4efSDavid Xu 	LIST_INIT(&sigev_threads);
1256348ace8SDavid Xu 	sigev_default_thread = NULL;
1266348ace8SDavid Xu 	if (atfork_registered == 0) {
127c5a4f4efSDavid Xu 		_pthread_atfork(
1286348ace8SDavid Xu 			__sigev_fork_prepare,
1296348ace8SDavid Xu 			__sigev_fork_parent,
1306348ace8SDavid Xu 			__sigev_fork_child);
1316348ace8SDavid Xu 		atfork_registered = 1;
1326348ace8SDavid Xu 	}
1336348ace8SDavid Xu 	if (!inited) {
134c5a4f4efSDavid Xu 		_pthread_attr_init(&sigev_default_attr);
135c5a4f4efSDavid Xu 		_pthread_attr_setscope(&sigev_default_attr,
1366348ace8SDavid Xu 			PTHREAD_SCOPE_SYSTEM);
137c5a4f4efSDavid Xu 		_pthread_attr_setdetachstate(&sigev_default_attr,
1384acaec8fSDavid Xu 			PTHREAD_CREATE_DETACHED);
1396348ace8SDavid Xu 		inited = 1;
1404acaec8fSDavid Xu 	}
141c5a4f4efSDavid Xu 	sigev_default_thread = sigev_thread_create(0);
1424acaec8fSDavid Xu }
1434acaec8fSDavid Xu 
1444acaec8fSDavid Xu int
__sigev_check_init(void)1454acaec8fSDavid Xu __sigev_check_init(void)
1464acaec8fSDavid Xu {
1474acaec8fSDavid Xu 	if (!have_threads())
1484acaec8fSDavid Xu 		return (-1);
1494acaec8fSDavid Xu 
1504acaec8fSDavid Xu 	_pthread_once(&sigev_once, __sigev_thread_init);
1516348ace8SDavid Xu 	return (sigev_default_thread != NULL) ? 0 : -1;
1524acaec8fSDavid Xu }
1534acaec8fSDavid Xu 
1546348ace8SDavid Xu static void
__sigev_fork_prepare(void)1554acaec8fSDavid Xu __sigev_fork_prepare(void)
1564acaec8fSDavid Xu {
1574acaec8fSDavid Xu }
1584acaec8fSDavid Xu 
1596348ace8SDavid Xu static void
__sigev_fork_parent(void)1604acaec8fSDavid Xu __sigev_fork_parent(void)
1614acaec8fSDavid Xu {
1624acaec8fSDavid Xu }
1634acaec8fSDavid Xu 
1646348ace8SDavid Xu static void
__sigev_fork_child(void)1654acaec8fSDavid Xu __sigev_fork_child(void)
1664acaec8fSDavid Xu {
1676348ace8SDavid Xu 	/*
1686348ace8SDavid Xu 	 * This is a hack, the thread libraries really should
1696348ace8SDavid Xu 	 * check if the handlers were already registered in
1706348ace8SDavid Xu 	 * pthread_atfork().
1716348ace8SDavid Xu 	 */
1726348ace8SDavid Xu 	atfork_registered = 1;
1736348ace8SDavid Xu 	memcpy(&sigev_once, &sigev_once_default, sizeof(sigev_once));
1744acaec8fSDavid Xu 	__sigev_thread_init();
1754acaec8fSDavid Xu }
1764acaec8fSDavid Xu 
177c5a4f4efSDavid Xu void
__sigev_list_lock(void)1784acaec8fSDavid Xu __sigev_list_lock(void)
1794acaec8fSDavid Xu {
180c5a4f4efSDavid Xu 	_pthread_mutex_lock(sigev_list_mtx);
1814acaec8fSDavid Xu }
1824acaec8fSDavid Xu 
183c5a4f4efSDavid Xu void
__sigev_list_unlock(void)1844acaec8fSDavid Xu __sigev_list_unlock(void)
1854acaec8fSDavid Xu {
186c5a4f4efSDavid Xu 	_pthread_mutex_unlock(sigev_list_mtx);
1874acaec8fSDavid Xu }
1884acaec8fSDavid Xu 
1894acaec8fSDavid Xu struct sigev_node *
__sigev_alloc(int type,const struct sigevent * evp,struct sigev_node * prev,int usedefault)1906348ace8SDavid Xu __sigev_alloc(int type, const struct sigevent *evp, struct sigev_node *prev,
191c5a4f4efSDavid Xu 	int usedefault)
1924acaec8fSDavid Xu {
1934acaec8fSDavid Xu 	struct sigev_node *sn;
1944acaec8fSDavid Xu 
1954acaec8fSDavid Xu 	sn = calloc(1, sizeof(*sn));
1964acaec8fSDavid Xu 	if (sn != NULL) {
1974acaec8fSDavid Xu 		sn->sn_value = evp->sigev_value;
1984acaec8fSDavid Xu 		sn->sn_func  = evp->sigev_notify_function;
199cd8b04c9SEd Schouten 		sn->sn_gen   = atomic_fetch_add_explicit(&sigev_generation, 1,
200cd8b04c9SEd Schouten 		    memory_order_relaxed);
2014acaec8fSDavid Xu 		sn->sn_type  = type;
202c5a4f4efSDavid Xu 		_pthread_attr_init(&sn->sn_attr);
203c5a4f4efSDavid Xu 		_pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED);
204c5a4f4efSDavid Xu 		if (evp->sigev_notify_attributes)
205c5a4f4efSDavid Xu 			attrcopy(evp->sigev_notify_attributes, &sn->sn_attr);
206c5a4f4efSDavid Xu 		if (prev) {
207c5a4f4efSDavid Xu 			__sigev_list_lock();
208c5a4f4efSDavid Xu 			prev->sn_tn->tn_refcount++;
209c5a4f4efSDavid Xu 			__sigev_list_unlock();
210c5a4f4efSDavid Xu 			sn->sn_tn = prev->sn_tn;
211c5a4f4efSDavid Xu 		} else {
212c5a4f4efSDavid Xu 			sn->sn_tn = sigev_thread_create(usedefault);
2134acaec8fSDavid Xu 			if (sn->sn_tn == NULL) {
214c5a4f4efSDavid Xu 				_pthread_attr_destroy(&sn->sn_attr);
2154acaec8fSDavid Xu 				free(sn);
2164acaec8fSDavid Xu 				sn = NULL;
2174acaec8fSDavid Xu 			}
2184acaec8fSDavid Xu 		}
219c5a4f4efSDavid Xu 	}
2204acaec8fSDavid Xu 	return (sn);
2214acaec8fSDavid Xu }
2224acaec8fSDavid Xu 
2234acaec8fSDavid Xu void
__sigev_get_sigevent(struct sigev_node * sn,struct sigevent * newevp,sigev_id_t id)2244acaec8fSDavid Xu __sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp,
2254acaec8fSDavid Xu 	sigev_id_t id)
2264acaec8fSDavid Xu {
2274acaec8fSDavid Xu 	/*
22863eebf9cSRobert Millan 	 * Build a new sigevent, and tell kernel to deliver SIGLIBRT
2294acaec8fSDavid Xu 	 * signal to the new thread.
2304acaec8fSDavid Xu 	 */
2314acaec8fSDavid Xu 	newevp->sigev_notify = SIGEV_THREAD_ID;
23263eebf9cSRobert Millan 	newevp->sigev_signo  = SIGLIBRT;
2334acaec8fSDavid Xu 	newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid;
2344acaec8fSDavid Xu 	newevp->sigev_value.sival_ptr = (void *)id;
2354acaec8fSDavid Xu }
2364acaec8fSDavid Xu 
2374acaec8fSDavid Xu void
__sigev_free(struct sigev_node * sn)2384acaec8fSDavid Xu __sigev_free(struct sigev_node *sn)
2394acaec8fSDavid Xu {
240c5a4f4efSDavid Xu 	_pthread_attr_destroy(&sn->sn_attr);
2414acaec8fSDavid Xu 	free(sn);
2424acaec8fSDavid Xu }
2434acaec8fSDavid Xu 
2444acaec8fSDavid Xu struct sigev_node *
__sigev_find(int type,sigev_id_t id)2454acaec8fSDavid Xu __sigev_find(int type, sigev_id_t id)
2464acaec8fSDavid Xu {
2474acaec8fSDavid Xu 	struct sigev_node *sn;
2484acaec8fSDavid Xu 	int chain = HASH(type, id);
2494acaec8fSDavid Xu 
2504acaec8fSDavid Xu 	LIST_FOREACH(sn, &sigev_hash[chain], sn_link) {
2514acaec8fSDavid Xu 		if (sn->sn_type == type && sn->sn_id == id)
2524acaec8fSDavid Xu 			break;
2534acaec8fSDavid Xu 	}
2544acaec8fSDavid Xu 	return (sn);
2554acaec8fSDavid Xu }
2564acaec8fSDavid Xu 
2574acaec8fSDavid Xu int
__sigev_register(struct sigev_node * sn)2584acaec8fSDavid Xu __sigev_register(struct sigev_node *sn)
2594acaec8fSDavid Xu {
2604acaec8fSDavid Xu 	int chain = HASH(sn->sn_type, sn->sn_id);
2614acaec8fSDavid Xu 
2624acaec8fSDavid Xu 	LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link);
2634acaec8fSDavid Xu 	return (0);
2644acaec8fSDavid Xu }
2654acaec8fSDavid Xu 
2664acaec8fSDavid Xu int
__sigev_delete(int type,sigev_id_t id)2674acaec8fSDavid Xu __sigev_delete(int type, sigev_id_t id)
2684acaec8fSDavid Xu {
2694acaec8fSDavid Xu 	struct sigev_node *sn;
2704acaec8fSDavid Xu 
2714acaec8fSDavid Xu 	sn = __sigev_find(type, id);
2724acaec8fSDavid Xu 	if (sn != NULL)
2734acaec8fSDavid Xu 		return (__sigev_delete_node(sn));
2744acaec8fSDavid Xu 	return (0);
2754acaec8fSDavid Xu }
2764acaec8fSDavid Xu 
2774acaec8fSDavid Xu int
__sigev_delete_node(struct sigev_node * sn)2784acaec8fSDavid Xu __sigev_delete_node(struct sigev_node *sn)
2794acaec8fSDavid Xu {
2804acaec8fSDavid Xu 	LIST_REMOVE(sn, sn_link);
2814acaec8fSDavid Xu 
2826348ace8SDavid Xu 	if (--sn->sn_tn->tn_refcount == 0)
28363eebf9cSRobert Millan 		_pthread_kill(sn->sn_tn->tn_thread, SIGLIBRT);
2844acaec8fSDavid Xu 	if (sn->sn_flags & SNF_WORKING)
2854acaec8fSDavid Xu 		sn->sn_flags |= SNF_REMOVED;
286c5a4f4efSDavid Xu 	else
2874acaec8fSDavid Xu 		__sigev_free(sn);
2884acaec8fSDavid Xu 	return (0);
2894acaec8fSDavid Xu }
2904acaec8fSDavid Xu 
291c5a4f4efSDavid Xu static sigev_id_t
sigev_get_id(siginfo_t * si)2924acaec8fSDavid Xu sigev_get_id(siginfo_t *si)
2934acaec8fSDavid Xu {
2944acaec8fSDavid Xu 	switch(si->si_code) {
2954acaec8fSDavid Xu 	case SI_TIMER:
2964acaec8fSDavid Xu 		return (si->si_timerid);
2974acaec8fSDavid Xu 	case SI_MESGQ:
2984acaec8fSDavid Xu 		return (si->si_mqd);
2994acaec8fSDavid Xu 	case SI_ASYNCIO:
3004acaec8fSDavid Xu 		return (sigev_id_t)si->si_value.sival_ptr;
3014acaec8fSDavid Xu 	}
3024acaec8fSDavid Xu 	return (-1);
3034acaec8fSDavid Xu }
3044acaec8fSDavid Xu 
305c5a4f4efSDavid Xu static struct sigev_thread *
sigev_thread_create(int usedefault)306c5a4f4efSDavid Xu sigev_thread_create(int usedefault)
3074acaec8fSDavid Xu {
308c5a4f4efSDavid Xu 	struct sigev_thread *tn;
30904f5329fSDavid Xu 	sigset_t set, oset;
3104acaec8fSDavid Xu 	int ret;
3114acaec8fSDavid Xu 
312c5a4f4efSDavid Xu 	if (usedefault && sigev_default_thread) {
313c5a4f4efSDavid Xu 		__sigev_list_lock();
3146348ace8SDavid Xu 		sigev_default_thread->tn_refcount++;
315c5a4f4efSDavid Xu 		__sigev_list_unlock();
3166348ace8SDavid Xu 		return (sigev_default_thread);
3176348ace8SDavid Xu 	}
3186348ace8SDavid Xu 
3194acaec8fSDavid Xu 	tn = malloc(sizeof(*tn));
3204acaec8fSDavid Xu 	tn->tn_cur = NULL;
3216348ace8SDavid Xu 	tn->tn_lwpid = -1;
3226348ace8SDavid Xu 	tn->tn_refcount = 1;
323c5a4f4efSDavid Xu 	_pthread_cond_init(&tn->tn_cv, NULL);
324c5a4f4efSDavid Xu 
325c5a4f4efSDavid Xu 	/* for debug */
326c5a4f4efSDavid Xu 	__sigev_list_lock();
327c5a4f4efSDavid Xu 	LIST_INSERT_HEAD(&sigev_threads, tn, tn_link);
328c5a4f4efSDavid Xu 	__sigev_list_unlock();
329c5a4f4efSDavid Xu 
33063eebf9cSRobert Millan 	sigfillset(&set);	/* SIGLIBRT is masked. */
33104f5329fSDavid Xu 	sigdelset(&set, SIGBUS);
33204f5329fSDavid Xu 	sigdelset(&set, SIGILL);
33304f5329fSDavid Xu 	sigdelset(&set, SIGFPE);
33404f5329fSDavid Xu 	sigdelset(&set, SIGSEGV);
33504f5329fSDavid Xu 	sigdelset(&set, SIGTRAP);
33604f5329fSDavid Xu 	_sigprocmask(SIG_SETMASK, &set, &oset);
337d688b9ceSDaniel Eischen 	ret = _pthread_create(&tn->tn_thread, &sigev_default_attr,
338c5a4f4efSDavid Xu 		 sigev_service_loop, tn);
33904f5329fSDavid Xu 	_sigprocmask(SIG_SETMASK, &oset, NULL);
340c5a4f4efSDavid Xu 
3414acaec8fSDavid Xu 	if (ret != 0) {
342c5a4f4efSDavid Xu 		__sigev_list_lock();
343c5a4f4efSDavid Xu 		LIST_REMOVE(tn, tn_link);
344c5a4f4efSDavid Xu 		__sigev_list_unlock();
3454acaec8fSDavid Xu 		free(tn);
3464acaec8fSDavid Xu 		tn = NULL;
3474acaec8fSDavid Xu 	} else {
3484acaec8fSDavid Xu 		/* wait the thread to get its lwpid */
349c5a4f4efSDavid Xu 
350c5a4f4efSDavid Xu 		__sigev_list_lock();
3516348ace8SDavid Xu 		while (tn->tn_lwpid == -1)
352c5a4f4efSDavid Xu 			_pthread_cond_wait(&tn->tn_cv, sigev_list_mtx);
353c5a4f4efSDavid Xu 		__sigev_list_unlock();
3544acaec8fSDavid Xu 	}
3554acaec8fSDavid Xu 	return (tn);
3564acaec8fSDavid Xu }
3574acaec8fSDavid Xu 
3584acaec8fSDavid Xu /*
359c5a4f4efSDavid Xu  * The thread receives notification from kernel and creates
360c5a4f4efSDavid Xu  * a thread to call user callback function.
3616348ace8SDavid Xu  */
3624acaec8fSDavid Xu static void *
sigev_service_loop(void * arg)3634acaec8fSDavid Xu sigev_service_loop(void *arg)
3644acaec8fSDavid Xu {
365c5a4f4efSDavid Xu 	static int failure;
366c5a4f4efSDavid Xu 
3674acaec8fSDavid Xu 	siginfo_t si;
3684acaec8fSDavid Xu 	sigset_t set;
369c5a4f4efSDavid Xu 	struct sigev_thread *tn;
3704acaec8fSDavid Xu 	struct sigev_node *sn;
3714acaec8fSDavid Xu 	sigev_id_t id;
372c5a4f4efSDavid Xu 	pthread_t td;
3736348ace8SDavid Xu 	int ret;
3744acaec8fSDavid Xu 
3754acaec8fSDavid Xu 	tn = arg;
3764acaec8fSDavid Xu 	thr_self(&tn->tn_lwpid);
377c5a4f4efSDavid Xu 	__sigev_list_lock();
378c5a4f4efSDavid Xu 	_pthread_cond_broadcast(&tn->tn_cv);
379c5a4f4efSDavid Xu 	__sigev_list_unlock();
3804acaec8fSDavid Xu 
3814acaec8fSDavid Xu 	sigemptyset(&set);
38263eebf9cSRobert Millan 	sigaddset(&set, SIGLIBRT);
3834acaec8fSDavid Xu 	for (;;) {
3846348ace8SDavid Xu 		ret = sigwaitinfo(&set, &si);
385c5a4f4efSDavid Xu 
386c5a4f4efSDavid Xu 		__sigev_list_lock();
3876348ace8SDavid Xu 		if (tn->tn_refcount == 0) {
388c5a4f4efSDavid Xu 			LIST_REMOVE(tn, tn_link);
389c5a4f4efSDavid Xu 			__sigev_list_unlock();
3906348ace8SDavid Xu 			free(tn);
3916348ace8SDavid Xu 			break;
3926348ace8SDavid Xu 		}
393c5a4f4efSDavid Xu 
394c5a4f4efSDavid Xu 		if (ret == -1) {
395c5a4f4efSDavid Xu 			__sigev_list_unlock();
3964acaec8fSDavid Xu 			continue;
397c5a4f4efSDavid Xu 		}
398c5a4f4efSDavid Xu 
3994acaec8fSDavid Xu 		id = sigev_get_id(&si);
4004acaec8fSDavid Xu 		sn = __sigev_find(si.si_code, id);
401c5a4f4efSDavid Xu 		if (sn == NULL) {
402c5a4f4efSDavid Xu 			__sigev_list_unlock();
403c5a4f4efSDavid Xu 			continue;
404c5a4f4efSDavid Xu 		}
405c5a4f4efSDavid Xu 
4066348ace8SDavid Xu 		sn->sn_info = si;
407c5a4f4efSDavid Xu 		if (sn->sn_flags & SNF_SYNC)
4084acaec8fSDavid Xu 			tn->tn_cur = sn;
409c5a4f4efSDavid Xu 		else
4104acaec8fSDavid Xu 			tn->tn_cur = NULL;
4116348ace8SDavid Xu 		sn->sn_flags |= SNF_WORKING;
4126348ace8SDavid Xu 		__sigev_list_unlock();
4136348ace8SDavid Xu 
414d688b9ceSDaniel Eischen 		ret = _pthread_create(&td, &sn->sn_attr, worker_routine, sn);
415c5a4f4efSDavid Xu 		if (ret != 0) {
416c5a4f4efSDavid Xu 			if (failure++ < 5)
417c5a4f4efSDavid Xu 				warnc(ret, "%s:%s failed to create thread.\n",
418c5a4f4efSDavid Xu 					__FILE__, __func__);
4196348ace8SDavid Xu 
4206348ace8SDavid Xu 			__sigev_list_lock();
4216348ace8SDavid Xu 			sn->sn_flags &= ~SNF_WORKING;
4226348ace8SDavid Xu 			if (sn->sn_flags & SNF_REMOVED)
4236348ace8SDavid Xu 				__sigev_free(sn);
4246348ace8SDavid Xu 			__sigev_list_unlock();
425c5a4f4efSDavid Xu 		} else if (tn->tn_cur) {
426c5a4f4efSDavid Xu 			__sigev_list_lock();
427c5a4f4efSDavid Xu 			while (tn->tn_cur)
428c5a4f4efSDavid Xu 				_pthread_cond_wait(&tn->tn_cv, sigev_list_mtx);
429c5a4f4efSDavid Xu 			__sigev_list_unlock();
430c5a4f4efSDavid Xu 		}
431c5a4f4efSDavid Xu 	}
4326348ace8SDavid Xu 	return (0);
4336348ace8SDavid Xu }
4346348ace8SDavid Xu 
435c5a4f4efSDavid Xu /*
436c5a4f4efSDavid Xu  * newly created worker thread to call user callback function.
437c5a4f4efSDavid Xu  */
438c5a4f4efSDavid Xu static void *
worker_routine(void * arg)439c5a4f4efSDavid Xu worker_routine(void *arg)
440c5a4f4efSDavid Xu {
441c5a4f4efSDavid Xu 	struct sigev_node *sn = arg;
442c5a4f4efSDavid Xu 
44350f5f99bSDavid Xu 	pthread_cleanup_push(worker_cleanup, sn);
444c5a4f4efSDavid Xu 	sn->sn_dispatch(sn);
44550f5f99bSDavid Xu 	pthread_cleanup_pop(1);
446c5a4f4efSDavid Xu 
447c5a4f4efSDavid Xu 	return (0);
448c5a4f4efSDavid Xu }
449c5a4f4efSDavid Xu 
450c5a4f4efSDavid Xu /* clean up a notification after dispatch. */
4516348ace8SDavid Xu static void
worker_cleanup(void * arg)4526348ace8SDavid Xu worker_cleanup(void *arg)
4536348ace8SDavid Xu {
454c5a4f4efSDavid Xu 	struct sigev_node *sn = arg;
4556348ace8SDavid Xu 
4566348ace8SDavid Xu 	__sigev_list_lock();
457c5a4f4efSDavid Xu 	if (sn->sn_flags & SNF_SYNC) {
458c5a4f4efSDavid Xu 		sn->sn_tn->tn_cur = NULL;
459c5a4f4efSDavid Xu 		_pthread_cond_broadcast(&sn->sn_tn->tn_cv);
460c5a4f4efSDavid Xu 	}
4616348ace8SDavid Xu 	if (sn->sn_flags & SNF_REMOVED)
4626348ace8SDavid Xu 		__sigev_free(sn);
463c5a4f4efSDavid Xu 	else
464c5a4f4efSDavid Xu 		sn->sn_flags &= ~SNF_WORKING;
4656348ace8SDavid Xu 	__sigev_list_unlock();
4666348ace8SDavid Xu }
467