2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/types.h>
34 #include "namespace.h"
39 #include <stdatomic.h>
45 #include "un-namespace.h"
47 #include "sigev_thread.h"
49 LIST_HEAD(sigev_list_head, sigev_node);
50 #define HASH_QUEUES 17
51 #define HASH(t, id) ((((id) << 3) + (t)) % HASH_QUEUES)
53 static struct sigev_list_head sigev_hash[HASH_QUEUES];
54 static struct sigev_list_head sigev_all;
55 static LIST_HEAD(,sigev_thread) sigev_threads;
56 static atomic_int sigev_generation;
57 static pthread_mutex_t *sigev_list_mtx;
58 static pthread_once_t sigev_once = PTHREAD_ONCE_INIT;
59 static pthread_once_t sigev_once_default = PTHREAD_ONCE_INIT;
60 static struct sigev_thread *sigev_default_thread;
61 static pthread_attr_t sigev_default_attr;
62 static int atfork_registered;
64 static void __sigev_fork_prepare(void);
65 static void __sigev_fork_parent(void);
66 static void __sigev_fork_child(void);
67 static struct sigev_thread *sigev_thread_create(int);
68 static void *sigev_service_loop(void *);
69 static void *worker_routine(void *);
70 static void worker_cleanup(void *);
72 #pragma weak _pthread_create
75 attrcopy(pthread_attr_t *src, pthread_attr_t *dst)
77 struct sched_param sched;
82 _pthread_attr_getschedpolicy(src, &v);
83 _pthread_attr_setschedpolicy(dst, v);
85 _pthread_attr_getinheritsched(src, &v);
86 _pthread_attr_setinheritsched(dst, v);
88 _pthread_attr_getschedparam(src, &sched);
89 _pthread_attr_setschedparam(dst, &sched);
91 _pthread_attr_getscope(src, &v);
92 _pthread_attr_setscope(dst, v);
94 _pthread_attr_getstacksize(src, &u);
95 _pthread_attr_setstacksize(dst, u);
97 _pthread_attr_getstackaddr(src, &a);
98 _pthread_attr_setstackaddr(src, a);
100 _pthread_attr_getguardsize(src, &u);
101 _pthread_attr_setguardsize(dst, u);
107 return (&_pthread_create != NULL);
111 __sigev_thread_init(void)
113 static int inited = 0;
114 pthread_mutexattr_t mattr;
117 _pthread_mutexattr_init(&mattr);
118 _pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL);
119 sigev_list_mtx = malloc(sizeof(pthread_mutex_t));
120 _pthread_mutex_init(sigev_list_mtx, &mattr);
121 _pthread_mutexattr_destroy(&mattr);
123 for (i = 0; i < HASH_QUEUES; ++i)
124 LIST_INIT(&sigev_hash[i]);
125 LIST_INIT(&sigev_all);
126 LIST_INIT(&sigev_threads);
127 sigev_default_thread = NULL;
128 if (atfork_registered == 0) {
130 __sigev_fork_prepare,
133 atfork_registered = 1;
136 _pthread_attr_init(&sigev_default_attr);
137 _pthread_attr_setscope(&sigev_default_attr,
138 PTHREAD_SCOPE_SYSTEM);
139 _pthread_attr_setdetachstate(&sigev_default_attr,
140 PTHREAD_CREATE_DETACHED);
143 sigev_default_thread = sigev_thread_create(0);
147 __sigev_check_init(void)
152 _pthread_once(&sigev_once, __sigev_thread_init);
153 return (sigev_default_thread != NULL) ? 0 : -1;
157 __sigev_fork_prepare(void)
162 __sigev_fork_parent(void)
167 __sigev_fork_child(void)
170 * This is a hack, the thread libraries really should
171 * check if the handlers were already registered in
174 atfork_registered = 1;
175 memcpy(&sigev_once, &sigev_once_default, sizeof(sigev_once));
176 __sigev_thread_init();
180 __sigev_list_lock(void)
182 _pthread_mutex_lock(sigev_list_mtx);
186 __sigev_list_unlock(void)
188 _pthread_mutex_unlock(sigev_list_mtx);
192 __sigev_alloc(int type, const struct sigevent *evp, struct sigev_node *prev,
195 struct sigev_node *sn;
197 sn = calloc(1, sizeof(*sn));
199 sn->sn_value = evp->sigev_value;
200 sn->sn_func = evp->sigev_notify_function;
201 sn->sn_gen = atomic_fetch_add_explicit(&sigev_generation, 1,
202 memory_order_relaxed);
204 _pthread_attr_init(&sn->sn_attr);
205 _pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED);
206 if (evp->sigev_notify_attributes)
207 attrcopy(evp->sigev_notify_attributes, &sn->sn_attr);
210 prev->sn_tn->tn_refcount++;
211 __sigev_list_unlock();
212 sn->sn_tn = prev->sn_tn;
214 sn->sn_tn = sigev_thread_create(usedefault);
215 if (sn->sn_tn == NULL) {
216 _pthread_attr_destroy(&sn->sn_attr);
226 __sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp,
230 * Build a new sigevent, and tell kernel to deliver SIGLIBRT
231 * signal to the new thread.
233 newevp->sigev_notify = SIGEV_THREAD_ID;
234 newevp->sigev_signo = SIGLIBRT;
235 newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid;
236 newevp->sigev_value.sival_ptr = (void *)id;
240 __sigev_free(struct sigev_node *sn)
242 _pthread_attr_destroy(&sn->sn_attr);
247 __sigev_find(int type, sigev_id_t id)
249 struct sigev_node *sn;
250 int chain = HASH(type, id);
252 LIST_FOREACH(sn, &sigev_hash[chain], sn_link) {
253 if (sn->sn_type == type && sn->sn_id == id)
260 __sigev_register(struct sigev_node *sn)
262 int chain = HASH(sn->sn_type, sn->sn_id);
264 LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link);
269 __sigev_delete(int type, sigev_id_t id)
271 struct sigev_node *sn;
273 sn = __sigev_find(type, id);
275 return (__sigev_delete_node(sn));
280 __sigev_delete_node(struct sigev_node *sn)
282 LIST_REMOVE(sn, sn_link);
284 if (--sn->sn_tn->tn_refcount == 0)
285 _pthread_kill(sn->sn_tn->tn_thread, SIGLIBRT);
286 if (sn->sn_flags & SNF_WORKING)
287 sn->sn_flags |= SNF_REMOVED;
294 sigev_get_id(siginfo_t *si)
296 switch(si->si_code) {
298 return (si->si_timerid);
302 return (sigev_id_t)si->si_value.sival_ptr;
307 static struct sigev_thread *
308 sigev_thread_create(int usedefault)
310 struct sigev_thread *tn;
314 if (usedefault && sigev_default_thread) {
316 sigev_default_thread->tn_refcount++;
317 __sigev_list_unlock();
318 return (sigev_default_thread);
321 tn = malloc(sizeof(*tn));
325 _pthread_cond_init(&tn->tn_cv, NULL);
329 LIST_INSERT_HEAD(&sigev_threads, tn, tn_link);
330 __sigev_list_unlock();
332 sigfillset(&set); /* SIGLIBRT is masked. */
333 sigdelset(&set, SIGBUS);
334 sigdelset(&set, SIGILL);
335 sigdelset(&set, SIGFPE);
336 sigdelset(&set, SIGSEGV);
337 sigdelset(&set, SIGTRAP);
338 _sigprocmask(SIG_SETMASK, &set, &oset);
339 ret = _pthread_create(&tn->tn_thread, &sigev_default_attr,
340 sigev_service_loop, tn);
341 _sigprocmask(SIG_SETMASK, &oset, NULL);
345 LIST_REMOVE(tn, tn_link);
346 __sigev_list_unlock();
350 /* wait the thread to get its lwpid */
353 while (tn->tn_lwpid == -1)
354 _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx);
355 __sigev_list_unlock();
361 * The thread receives notification from kernel and creates
362 * a thread to call user callback function.
365 sigev_service_loop(void *arg)
371 struct sigev_thread *tn;
372 struct sigev_node *sn;
378 thr_self(&tn->tn_lwpid);
380 _pthread_cond_broadcast(&tn->tn_cv);
381 __sigev_list_unlock();
384 sigaddset(&set, SIGLIBRT);
386 ret = sigwaitinfo(&set, &si);
389 if (tn->tn_refcount == 0) {
390 LIST_REMOVE(tn, tn_link);
391 __sigev_list_unlock();
397 __sigev_list_unlock();
401 id = sigev_get_id(&si);
402 sn = __sigev_find(si.si_code, id);
404 __sigev_list_unlock();
409 if (sn->sn_flags & SNF_SYNC)
413 sn->sn_flags |= SNF_WORKING;
414 __sigev_list_unlock();
416 ret = _pthread_create(&td, &sn->sn_attr, worker_routine, sn);
419 warnc(ret, "%s:%s failed to create thread.\n",
423 sn->sn_flags &= ~SNF_WORKING;
424 if (sn->sn_flags & SNF_REMOVED)
426 __sigev_list_unlock();
427 } else if (tn->tn_cur) {
430 _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx);
431 __sigev_list_unlock();
438 * newly created worker thread to call user callback function.
441 worker_routine(void *arg)
443 struct sigev_node *sn = arg;
445 pthread_cleanup_push(worker_cleanup, sn);
447 pthread_cleanup_pop(1);
452 /* clean up a notification after dispatch. */
454 worker_cleanup(void *arg)
456 struct sigev_node *sn = arg;
459 if (sn->sn_flags & SNF_SYNC) {
460 sn->sn_tn->tn_cur = NULL;
461 _pthread_cond_broadcast(&sn->sn_tn->tn_cv);
463 if (sn->sn_flags & SNF_REMOVED)
466 sn->sn_flags &= ~SNF_WORKING;
467 __sigev_list_unlock();