1/*
2 * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
--- 9 unchanged lines hidden (view full) ---
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.
25 *
26 * $FreeBSD: head/lib/librt/sigev_thread.c 156192 2006-03-01 23:38:53Z davidxu $
27 *
28 */
29
30#include <sys/types.h>
31#include <machine/atomic.h>
32
33#include "namespace.h"
34#include <err.h>
35#include <ucontext.h>
36#include <sys/thr.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <signal.h>
41#include <pthread.h>
42#include "un-namespace.h"
43
44#include "sigev_thread.h"
45
46LIST_HEAD(sigev_list_head, sigev_node);
47#define HASH_QUEUES 17
48#define HASH(t, id) ((((id) << 3) + (t)) % HASH_QUEUES)
49static struct sigev_list_head sigev_hash[HASH_QUEUES];
50static struct sigev_list_head sigev_all;
51static int sigev_generation;
52static pthread_mutex_t *sigev_list_mtx;
53static TAILQ_HEAD(,sigev_thread_node) sigev_threads;
54static pthread_mutex_t *sigev_threads_mtx;
55static pthread_attr_t sigev_default_attr;
56static pthread_once_t sigev_once = PTHREAD_ONCE_INIT;
57
58static void __sigev_fork_prepare(void);
59static void __sigev_fork_parent(void);
60static void __sigev_fork_child(void);
61static struct sigev_thread_node *sigev_thread_create(pthread_attr_t *);
62static void *sigev_service_loop(void *);
63
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
--- 11 unchanged lines hidden (view full) ---
82attr2sna(pthread_attr_t *attr, struct sigev_thread_attr *sna)
83{
84 struct sched_param sched_param;
85
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);
94}
95
96static __inline int
97sna_eq(const struct sigev_thread_attr *a, const struct sigev_thread_attr *b)
98{
99 return memcmp(a, b, sizeof(*a)) == 0;
100}
101
102static __inline int
103have_threads(void)
104{
105 return (pthread_create != NULL);
106}
107
108void
109__sigev_thread_init(void)
110{
111 static int notfirst = 0;
112 int i;
113
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);
122 if (!notfirst) {
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,
128 __sigev_fork_child);
129 notfirst = 1;
130 }
131}
132
133int
134__sigev_check_init(void)
135{
136 if (!have_threads())
137 return (-1);
138
139 _pthread_once(&sigev_once, __sigev_thread_init);
140 return (0);
141}
142
143void
144__sigev_fork_prepare(void)
145{
146 __sigev_thread_list_lock();
147}
148
149void
150__sigev_fork_parent(void)
151{
152 __sigev_thread_list_unlock();
153}
154
155void
156__sigev_fork_child(void)
157{
158 __sigev_thread_init();
159}
160
161int
162__sigev_list_lock(void)
163{
164 return _pthread_mutex_lock(sigev_list_mtx);
165}
--- 12 unchanged lines hidden (view full) ---
178
179int
180__sigev_thread_list_unlock(void)
181{
182 return _pthread_mutex_unlock(sigev_threads_mtx);
183}
184
185struct sigev_node *
186__sigev_alloc(int type, const struct sigevent *evp)
187{
188 struct sigev_node *sn;
189
190 sn = calloc(1, sizeof(*sn));
191 if (sn != NULL) {
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);
195 sn->sn_type = type;
196 sn->sn_tn = sigev_thread_create(evp->sigev_notify_attributes);
197 if (sn->sn_tn == NULL) {
198 free(sn);
199 sn = NULL;
200 }
201 }
202 return (sn);
203}
204
--- 52 unchanged lines hidden (view full) ---
257}
258
259int
260__sigev_delete_node(struct sigev_node *sn)
261{
262 LIST_REMOVE(sn, sn_link);
263 LIST_REMOVE(sn, sn_allist);
264
265 if (sn->sn_flags & SNF_WORKING)
266 sn->sn_flags |= SNF_REMOVED;
267 else
268 __sigev_free(sn);
269 return (0);
270}
271
272static
273sigev_id_t
274sigev_get_id(siginfo_t *si)
275{
276 switch(si->si_code) {
277 case SI_TIMER:
278 return (si->si_timerid);
279 case SI_MESGQ:
280 return (si->si_mqd);
281 case SI_ASYNCIO:
282 return (sigev_id_t)si->si_value.sival_ptr;
283 default:
284 warnx("%s %s : unknown si_code %d\n", __FILE__, __func__,
285 si->si_code);
286 }
287 return (-1);
288}
289
290static struct sigev_thread_node *
291sigev_thread_create(pthread_attr_t *pattr)
292{
293 struct sigev_thread_node *tn;
294 struct sigev_thread_attr sna;
295 sigset_t set;
296 int ret;
297
298 if (pattr == NULL)
299 pattr = &sigev_default_attr;
300 else {
301 pthread_attr_setscope(pattr, PTHREAD_SCOPE_SYSTEM);
302 pthread_attr_setdetachstate(pattr, PTHREAD_CREATE_DETACHED);
303 }
304
305 attr2sna(pattr, &sna);
306
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))
312 break;
313 } else {
314 /*
315 * Reuse the thread if it has same stack address,
316 * because two threads can not run on same stack.
317 */
318 if (sna.sna_stackaddr == tn->tn_sna.sna_stackaddr)
319 break;
320 }
321 }
322 if (tn != NULL) {
323 __sigev_thread_list_unlock();
324 return (tn);
325 }
326 tn = malloc(sizeof(*tn));
327 tn->tn_sna = sna;
328 tn->tn_cur = NULL;
329 TAILQ_INSERT_TAIL(&sigev_threads, tn, tn_link);
330 sigemptyset(&set);
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);
336 if (ret != 0) {
337 TAILQ_REMOVE(&sigev_threads, tn, tn_link);
338 __sigev_thread_list_unlock();
339 _pthread_cond_destroy(&tn->tn_cv);
340 free(tn);
341 tn = NULL;
342 } else {
343 /* wait the thread to get its lwpid */
344 _pthread_cond_wait(&tn->tn_cv, sigev_threads_mtx);
345 __sigev_thread_list_unlock();
346 }
347 return (tn);
348}
349
350static void
351after_dispatch(struct sigev_thread_node *tn)
352{
--- 4 unchanged lines hidden (view full) ---
357 sn->sn_flags &= ~SNF_WORKING;
358 if (sn->sn_flags & SNF_REMOVED)
359 __sigev_free(sn);
360 else if (sn->sn_flags & SNF_ONESHOT)
361 __sigev_delete_node(sn);
362 tn->tn_cur = NULL;
363 __sigev_list_unlock();
364 }
365}
366
367/*
368 * This function is called if user callback calls
369 * pthread_exit() or pthread_cancel() for the thread.
370 */
371static void
372thread_cleanup(void *arg)
373{
374 struct sigev_thread_node *tn = arg;
375
376 fprintf(stderr, "Dangerous Robinson, calling pthread_exit() from "
377 "SIGEV_THREAD is undefined.");
378 after_dispatch(tn);
379 /* longjmp(tn->tn_jbuf, 1); */
380 abort();
381}
382
383static void *
384sigev_service_loop(void *arg)
385{
386 siginfo_t si;
387 sigset_t set;
388 struct sigev_thread_node *tn;
389 struct sigev_node *sn;
390 sigev_id_t id;
391
392 tn = arg;
393 thr_self(&tn->tn_lwpid);
394 __sigev_list_lock();
395 _pthread_cond_broadcast(&tn->tn_cv);
396 __sigev_list_unlock();
397
398 /*
399 * Service thread should not be killed by callback, if user
400 * attempts to do so, the thread will be restarted.
401 */
402 setjmp(tn->tn_jbuf);
403 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
404 sigemptyset(&set);
405 sigaddset(&set, SIGEV_SIGSERVICE);
406 pthread_cleanup_push(thread_cleanup, tn);
407 for (;;) {
408 if (__predict_false(sigwaitinfo(&set, &si) == -1))
409 continue;
410
411 id = sigev_get_id(&si);
412 __sigev_list_lock();
413 sn = __sigev_find(si.si_code, id);
414 if (sn != NULL) {
415 tn->tn_cur = sn;
416 sn->sn_flags |= SNF_WORKING;
417 __sigev_list_unlock();
418 sn->sn_dispatch(sn, &si);
419 after_dispatch(tn);
420 } else {
421 tn->tn_cur = NULL;
422 __sigev_list_unlock();
423 }
424 }
425 pthread_cleanup_pop(0);
426 return (0);
427}
2 * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
--- 9 unchanged lines hidden (view full) ---
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.
25 *
26 * $FreeBSD: head/lib/librt/sigev_thread.c 156192 2006-03-01 23:38:53Z davidxu $
27 *
28 */
29
30#include <sys/types.h>
31#include <machine/atomic.h>
32
33#include "namespace.h"
34#include <err.h>
35#include <ucontext.h>
36#include <sys/thr.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <signal.h>
41#include <pthread.h>
42#include "un-namespace.h"
43
44#include "sigev_thread.h"
45
46LIST_HEAD(sigev_list_head, sigev_node);
47#define HASH_QUEUES 17
48#define HASH(t, id) ((((id) << 3) + (t)) % HASH_QUEUES)
49static struct sigev_list_head sigev_hash[HASH_QUEUES];
50static struct sigev_list_head sigev_all;
51static int sigev_generation;
52static pthread_mutex_t *sigev_list_mtx;
53static TAILQ_HEAD(,sigev_thread_node) sigev_threads;
54static pthread_mutex_t *sigev_threads_mtx;
55static pthread_attr_t sigev_default_attr;
56static pthread_once_t sigev_once = PTHREAD_ONCE_INIT;
57
58static void __sigev_fork_prepare(void);
59static void __sigev_fork_parent(void);
60static void __sigev_fork_child(void);
61static struct sigev_thread_node *sigev_thread_create(pthread_attr_t *);
62static void *sigev_service_loop(void *);
63
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
--- 11 unchanged lines hidden (view full) ---
82attr2sna(pthread_attr_t *attr, struct sigev_thread_attr *sna)
83{
84 struct sched_param sched_param;
85
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);
94}
95
96static __inline int
97sna_eq(const struct sigev_thread_attr *a, const struct sigev_thread_attr *b)
98{
99 return memcmp(a, b, sizeof(*a)) == 0;
100}
101
102static __inline int
103have_threads(void)
104{
105 return (pthread_create != NULL);
106}
107
108void
109__sigev_thread_init(void)
110{
111 static int notfirst = 0;
112 int i;
113
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);
122 if (!notfirst) {
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,
128 __sigev_fork_child);
129 notfirst = 1;
130 }
131}
132
133int
134__sigev_check_init(void)
135{
136 if (!have_threads())
137 return (-1);
138
139 _pthread_once(&sigev_once, __sigev_thread_init);
140 return (0);
141}
142
143void
144__sigev_fork_prepare(void)
145{
146 __sigev_thread_list_lock();
147}
148
149void
150__sigev_fork_parent(void)
151{
152 __sigev_thread_list_unlock();
153}
154
155void
156__sigev_fork_child(void)
157{
158 __sigev_thread_init();
159}
160
161int
162__sigev_list_lock(void)
163{
164 return _pthread_mutex_lock(sigev_list_mtx);
165}
--- 12 unchanged lines hidden (view full) ---
178
179int
180__sigev_thread_list_unlock(void)
181{
182 return _pthread_mutex_unlock(sigev_threads_mtx);
183}
184
185struct sigev_node *
186__sigev_alloc(int type, const struct sigevent *evp)
187{
188 struct sigev_node *sn;
189
190 sn = calloc(1, sizeof(*sn));
191 if (sn != NULL) {
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);
195 sn->sn_type = type;
196 sn->sn_tn = sigev_thread_create(evp->sigev_notify_attributes);
197 if (sn->sn_tn == NULL) {
198 free(sn);
199 sn = NULL;
200 }
201 }
202 return (sn);
203}
204
--- 52 unchanged lines hidden (view full) ---
257}
258
259int
260__sigev_delete_node(struct sigev_node *sn)
261{
262 LIST_REMOVE(sn, sn_link);
263 LIST_REMOVE(sn, sn_allist);
264
265 if (sn->sn_flags & SNF_WORKING)
266 sn->sn_flags |= SNF_REMOVED;
267 else
268 __sigev_free(sn);
269 return (0);
270}
271
272static
273sigev_id_t
274sigev_get_id(siginfo_t *si)
275{
276 switch(si->si_code) {
277 case SI_TIMER:
278 return (si->si_timerid);
279 case SI_MESGQ:
280 return (si->si_mqd);
281 case SI_ASYNCIO:
282 return (sigev_id_t)si->si_value.sival_ptr;
283 default:
284 warnx("%s %s : unknown si_code %d\n", __FILE__, __func__,
285 si->si_code);
286 }
287 return (-1);
288}
289
290static struct sigev_thread_node *
291sigev_thread_create(pthread_attr_t *pattr)
292{
293 struct sigev_thread_node *tn;
294 struct sigev_thread_attr sna;
295 sigset_t set;
296 int ret;
297
298 if (pattr == NULL)
299 pattr = &sigev_default_attr;
300 else {
301 pthread_attr_setscope(pattr, PTHREAD_SCOPE_SYSTEM);
302 pthread_attr_setdetachstate(pattr, PTHREAD_CREATE_DETACHED);
303 }
304
305 attr2sna(pattr, &sna);
306
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))
312 break;
313 } else {
314 /*
315 * Reuse the thread if it has same stack address,
316 * because two threads can not run on same stack.
317 */
318 if (sna.sna_stackaddr == tn->tn_sna.sna_stackaddr)
319 break;
320 }
321 }
322 if (tn != NULL) {
323 __sigev_thread_list_unlock();
324 return (tn);
325 }
326 tn = malloc(sizeof(*tn));
327 tn->tn_sna = sna;
328 tn->tn_cur = NULL;
329 TAILQ_INSERT_TAIL(&sigev_threads, tn, tn_link);
330 sigemptyset(&set);
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);
336 if (ret != 0) {
337 TAILQ_REMOVE(&sigev_threads, tn, tn_link);
338 __sigev_thread_list_unlock();
339 _pthread_cond_destroy(&tn->tn_cv);
340 free(tn);
341 tn = NULL;
342 } else {
343 /* wait the thread to get its lwpid */
344 _pthread_cond_wait(&tn->tn_cv, sigev_threads_mtx);
345 __sigev_thread_list_unlock();
346 }
347 return (tn);
348}
349
350static void
351after_dispatch(struct sigev_thread_node *tn)
352{
--- 4 unchanged lines hidden (view full) ---
357 sn->sn_flags &= ~SNF_WORKING;
358 if (sn->sn_flags & SNF_REMOVED)
359 __sigev_free(sn);
360 else if (sn->sn_flags & SNF_ONESHOT)
361 __sigev_delete_node(sn);
362 tn->tn_cur = NULL;
363 __sigev_list_unlock();
364 }
365}
366
367/*
368 * This function is called if user callback calls
369 * pthread_exit() or pthread_cancel() for the thread.
370 */
371static void
372thread_cleanup(void *arg)
373{
374 struct sigev_thread_node *tn = arg;
375
376 fprintf(stderr, "Dangerous Robinson, calling pthread_exit() from "
377 "SIGEV_THREAD is undefined.");
378 after_dispatch(tn);
379 /* longjmp(tn->tn_jbuf, 1); */
380 abort();
381}
382
383static void *
384sigev_service_loop(void *arg)
385{
386 siginfo_t si;
387 sigset_t set;
388 struct sigev_thread_node *tn;
389 struct sigev_node *sn;
390 sigev_id_t id;
391
392 tn = arg;
393 thr_self(&tn->tn_lwpid);
394 __sigev_list_lock();
395 _pthread_cond_broadcast(&tn->tn_cv);
396 __sigev_list_unlock();
397
398 /*
399 * Service thread should not be killed by callback, if user
400 * attempts to do so, the thread will be restarted.
401 */
402 setjmp(tn->tn_jbuf);
403 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
404 sigemptyset(&set);
405 sigaddset(&set, SIGEV_SIGSERVICE);
406 pthread_cleanup_push(thread_cleanup, tn);
407 for (;;) {
408 if (__predict_false(sigwaitinfo(&set, &si) == -1))
409 continue;
410
411 id = sigev_get_id(&si);
412 __sigev_list_lock();
413 sn = __sigev_find(si.si_code, id);
414 if (sn != NULL) {
415 tn->tn_cur = sn;
416 sn->sn_flags |= SNF_WORKING;
417 __sigev_list_unlock();
418 sn->sn_dispatch(sn, &si);
419 after_dispatch(tn);
420 } else {
421 tn->tn_cur = NULL;
422 __sigev_list_unlock();
423 }
424 }
425 pthread_cleanup_pop(0);
426 return (0);
427}