2 * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <sys/types.h>
31 #include <machine/atomic.h>
33 #include "namespace.h"
42 #include "un-namespace.h"
44 #include "sigev_thread.h"
46 LIST_HEAD(sigev_list_head, sigev_node);
47 #define HASH_QUEUES 17
48 #define HASH(t, id) ((((id) << 3) + (t)) % HASH_QUEUES)
49 static struct sigev_list_head sigev_hash[HASH_QUEUES];
50 static struct sigev_list_head sigev_all;
51 static int sigev_generation;
52 static pthread_mutex_t *sigev_list_mtx;
53 static TAILQ_HEAD(,sigev_thread_node) sigev_threads;
54 static pthread_mutex_t *sigev_threads_mtx;
55 static pthread_attr_t sigev_default_attr;
56 static pthread_once_t sigev_once = PTHREAD_ONCE_INIT;
58 static void __sigev_fork_prepare(void);
59 static void __sigev_fork_parent(void);
60 static void __sigev_fork_child(void);
61 static struct sigev_thread_node *sigev_thread_create(pthread_attr_t *);
62 static void *sigev_service_loop(void *);
64 #pragma weak pthread_create
65 #pragma weak pthread_attr_getschedpolicy
66 #pragma weak pthread_attr_getinheritsched
67 #pragma weak pthread_attr_getschedparam
68 #pragma weak pthread_attr_getscope
69 #pragma weak pthread_attr_getstacksize
70 #pragma weak pthread_attr_getstackaddr
71 #pragma weak pthread_attr_getguardsize
72 #pragma weak pthread_attr_init
73 #pragma weak pthread_attr_setscope
74 #pragma weak pthread_attr_setdetachstate
75 #pragma weak pthread_atfork
76 #pragma weak _pthread_once
77 #pragma weak pthread_cleanup_push
78 #pragma weak pthread_cleanup_pop
79 #pragma weak pthread_setcancelstate
82 attr2sna(pthread_attr_t *attr, struct sigev_thread_attr *sna)
84 struct sched_param sched_param;
86 pthread_attr_getschedpolicy(attr, &sna->sna_policy);
87 pthread_attr_getinheritsched(attr, &sna->sna_inherit);
88 pthread_attr_getschedparam(attr, &sched_param);
89 sna->sna_prio = sched_param.sched_priority;
90 pthread_attr_getscope(attr, &sna->sna_scope);
91 pthread_attr_getstacksize(attr, &sna->sna_stacksize);
92 pthread_attr_getstackaddr(attr, &sna->sna_stackaddr);
93 pthread_attr_getguardsize(attr, &sna->sna_guardsize);
97 sna_eq(const struct sigev_thread_attr *a, const struct sigev_thread_attr *b)
99 return memcmp(a, b, sizeof(*a)) == 0;
105 return (pthread_create != NULL);
109 __sigev_thread_init(void)
111 static int notfirst = 0;
114 sigev_list_mtx = malloc(sizeof(pthread_mutex_t));
115 _pthread_mutex_init(sigev_list_mtx, NULL);
116 sigev_threads_mtx = malloc(sizeof(pthread_mutex_t));
117 _pthread_mutex_init(sigev_threads_mtx, NULL);
118 for (i = 0; i < HASH_QUEUES; ++i)
119 LIST_INIT(&sigev_hash[i]);
120 LIST_INIT(&sigev_all);
121 TAILQ_INIT(&sigev_threads);
123 pthread_attr_init(&sigev_default_attr);
124 pthread_attr_setscope(&sigev_default_attr, PTHREAD_SCOPE_SYSTEM);
125 pthread_attr_setdetachstate(&sigev_default_attr,
126 PTHREAD_CREATE_DETACHED);
127 pthread_atfork(__sigev_fork_prepare, __sigev_fork_parent,
134 __sigev_check_init(void)
139 _pthread_once(&sigev_once, __sigev_thread_init);
144 __sigev_fork_prepare(void)
146 __sigev_thread_list_lock();
150 __sigev_fork_parent(void)
152 __sigev_thread_list_unlock();
156 __sigev_fork_child(void)
158 __sigev_thread_init();
162 __sigev_list_lock(void)
164 return _pthread_mutex_lock(sigev_list_mtx);
168 __sigev_list_unlock(void)
170 return _pthread_mutex_unlock(sigev_list_mtx);
174 __sigev_thread_list_lock(void)
176 return _pthread_mutex_lock(sigev_threads_mtx);
180 __sigev_thread_list_unlock(void)
182 return _pthread_mutex_unlock(sigev_threads_mtx);
186 __sigev_alloc(int type, const struct sigevent *evp)
188 struct sigev_node *sn;
190 sn = calloc(1, sizeof(*sn));
192 sn->sn_value = evp->sigev_value;
193 sn->sn_func = evp->sigev_notify_function;
194 sn->sn_gen = atomic_fetchadd_int(&sigev_generation, 1);
196 sn->sn_tn = sigev_thread_create(evp->sigev_notify_attributes);
197 if (sn->sn_tn == NULL) {
206 __sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp,
210 * Build a new sigevent, and tell kernel to deliver SIGEV_SIGSERVICE
211 * signal to the new thread.
213 newevp->sigev_notify = SIGEV_THREAD_ID;
214 newevp->sigev_signo = SIGEV_SIGSERVICE;
215 newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid;
216 newevp->sigev_value.sival_ptr = (void *)id;
220 __sigev_free(struct sigev_node *sn)
226 __sigev_find(int type, sigev_id_t id)
228 struct sigev_node *sn;
229 int chain = HASH(type, id);
231 LIST_FOREACH(sn, &sigev_hash[chain], sn_link) {
232 if (sn->sn_type == type && sn->sn_id == id)
239 __sigev_register(struct sigev_node *sn)
241 int chain = HASH(sn->sn_type, sn->sn_id);
243 LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link);
244 LIST_INSERT_HEAD(&sigev_all, sn, sn_allist);
249 __sigev_delete(int type, sigev_id_t id)
251 struct sigev_node *sn;
253 sn = __sigev_find(type, id);
255 return (__sigev_delete_node(sn));
260 __sigev_delete_node(struct sigev_node *sn)
262 LIST_REMOVE(sn, sn_link);
263 LIST_REMOVE(sn, sn_allist);
265 if (sn->sn_flags & SNF_WORKING)
266 sn->sn_flags |= SNF_REMOVED;
274 sigev_get_id(siginfo_t *si)
276 switch(si->si_code) {
278 return (si->si_timerid);
282 return (sigev_id_t)si->si_value.sival_ptr;
284 warnx("%s %s : unknown si_code %d\n", __FILE__, __func__,
290 static struct sigev_thread_node *
291 sigev_thread_create(pthread_attr_t *pattr)
293 struct sigev_thread_node *tn;
294 struct sigev_thread_attr sna;
299 pattr = &sigev_default_attr;
301 pthread_attr_setscope(pattr, PTHREAD_SCOPE_SYSTEM);
302 pthread_attr_setdetachstate(pattr, PTHREAD_CREATE_DETACHED);
305 attr2sna(pattr, &sna);
307 __sigev_thread_list_lock();
308 /* Search a thread matching the required pthread_attr. */
309 TAILQ_FOREACH(tn, &sigev_threads, tn_link) {
310 if (sna.sna_stackaddr == NULL) {
311 if (sna_eq(&tn->tn_sna, &sna))
315 * Reuse the thread if it has same stack address,
316 * because two threads can not run on same stack.
318 if (sna.sna_stackaddr == tn->tn_sna.sna_stackaddr)
323 __sigev_thread_list_unlock();
326 tn = malloc(sizeof(*tn));
329 TAILQ_INSERT_TAIL(&sigev_threads, tn, tn_link);
331 sigaddset(&set, SIGEV_SIGSERVICE);
332 _sigprocmask(SIG_BLOCK, &set, NULL);
333 _pthread_cond_init(&tn->tn_cv, NULL);
334 ret = pthread_create(&tn->tn_thread, pattr, sigev_service_loop, tn);
335 _sigprocmask(SIG_UNBLOCK, &set, NULL);
337 TAILQ_REMOVE(&sigev_threads, tn, tn_link);
338 __sigev_thread_list_unlock();
339 _pthread_cond_destroy(&tn->tn_cv);
343 /* wait the thread to get its lwpid */
344 _pthread_cond_wait(&tn->tn_cv, sigev_threads_mtx);
345 __sigev_thread_list_unlock();
351 after_dispatch(struct sigev_thread_node *tn)
353 struct sigev_node *sn;
355 if ((sn = tn->tn_cur) != NULL) {
357 sn->sn_flags &= ~SNF_WORKING;
358 if (sn->sn_flags & SNF_REMOVED)
360 else if (sn->sn_flags & SNF_ONESHOT)
361 __sigev_delete_node(sn);
363 __sigev_list_unlock();
368 * This function is called if user callback calls
369 * pthread_exit() or pthread_cancel() for the thread.
372 thread_cleanup(void *arg)
374 struct sigev_thread_node *tn = arg;
376 fprintf(stderr, "Dangerous Robinson, calling pthread_exit() from "
377 "SIGEV_THREAD is undefined.");
379 /* longjmp(tn->tn_jbuf, 1); */
384 sigev_service_loop(void *arg)
388 struct sigev_thread_node *tn;
389 struct sigev_node *sn;
393 thr_self(&tn->tn_lwpid);
395 _pthread_cond_broadcast(&tn->tn_cv);
396 __sigev_list_unlock();
399 * Service thread should not be killed by callback, if user
400 * attempts to do so, the thread will be restarted.
403 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
405 sigaddset(&set, SIGEV_SIGSERVICE);
406 pthread_cleanup_push(thread_cleanup, tn);
408 if (__predict_false(sigwaitinfo(&set, &si) == -1))
411 id = sigev_get_id(&si);
413 sn = __sigev_find(si.si_code, id);
416 sn->sn_flags |= SNF_WORKING;
417 __sigev_list_unlock();
418 sn->sn_dispatch(sn, &si);
422 __sigev_list_unlock();
424 pthread_cleanup_pop(0);