sigev_thread.c revision 157242
11556Srgrimes/* 21556Srgrimes * Copyright (c) 2005 David Xu <davidxu@freebsd.org> 31556Srgrimes * All rights reserved. 41556Srgrimes * 51556Srgrimes * Redistribution and use in source and binary forms, with or without 61556Srgrimes * modification, are permitted provided that the following conditions 71556Srgrimes * are met: 81556Srgrimes * 1. Redistributions of source code must retain the above copyright 91556Srgrimes * notice unmodified, this list of conditions, and the following 101556Srgrimes * disclaimer. 111556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer in the 131556Srgrimes * documentation and/or other materials provided with the distribution. 141556Srgrimes * 151556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 161556Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 171556Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 181556Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 191556Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 201556Srgrimes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 211556Srgrimes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 221556Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 231556Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 241556Srgrimes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 251556Srgrimes * 261556Srgrimes * $FreeBSD: head/lib/librt/sigev_thread.c 157242 2006-03-29 04:20:53Z deischen $ 271556Srgrimes * 281556Srgrimes */ 291556Srgrimes 301556Srgrimes#include <sys/types.h> 311556Srgrimes#include <machine/atomic.h> 321556Srgrimes 331556Srgrimes#include "namespace.h" 3436150Scharnier#include <err.h> 3536150Scharnier#include <errno.h> 3636150Scharnier#include <ucontext.h> 371556Srgrimes#include <sys/thr.h> 3899110Sobrien#include <stdio.h> 3999110Sobrien#include <stdlib.h> 401556Srgrimes#include <string.h> 4117987Speter#include <signal.h> 4217987Speter#include <pthread.h> 4317987Speter#include "un-namespace.h" 4417987Speter 4517987Speter#include "sigev_thread.h" 4617987Speter 4717987SpeterLIST_HEAD(sigev_list_head, sigev_node); 481556Srgrimes#define HASH_QUEUES 17 491556Srgrimes#define HASH(t, id) ((((id) << 3) + (t)) % HASH_QUEUES) 501556Srgrimes 511556Srgrimesstatic struct sigev_list_head sigev_hash[HASH_QUEUES]; 521556Srgrimesstatic struct sigev_list_head sigev_all; 531556Srgrimesstatic LIST_HEAD(,sigev_thread) sigev_threads; 541556Srgrimesstatic int sigev_generation; 551556Srgrimesstatic pthread_mutex_t *sigev_list_mtx; 561556Srgrimesstatic pthread_once_t sigev_once = PTHREAD_ONCE_INIT; 571556Srgrimesstatic pthread_once_t sigev_once_default = PTHREAD_ONCE_INIT; 581556Srgrimesstatic struct sigev_thread *sigev_default_thread; 591556Srgrimesstatic pthread_attr_t sigev_default_attr; 601556Srgrimesstatic int atfork_registered; 611556Srgrimes 621556Srgrimesstatic void __sigev_fork_prepare(void); 631556Srgrimesstatic void __sigev_fork_parent(void); 641556Srgrimesstatic void __sigev_fork_child(void); 651556Srgrimesstatic struct sigev_thread *sigev_thread_create(int); 661556Srgrimesstatic void *sigev_service_loop(void *); 671556Srgrimesstatic void *worker_routine(void *); 681556Srgrimesstatic void worker_cleanup(void *); 691556Srgrimes 701556Srgrimes#pragma weak _pthread_create 711556Srgrimes 721556Srgrimesstatic void 731556Srgrimesattrcopy(pthread_attr_t *src, pthread_attr_t *dst) 7417987Speter{ 751556Srgrimes struct sched_param sched; 7625223Ssteve void *a; 771556Srgrimes size_t u; 781556Srgrimes int v; 791556Srgrimes 801556Srgrimes _pthread_attr_getschedpolicy(src, &v); 811556Srgrimes _pthread_attr_setschedpolicy(dst, v); 821556Srgrimes 831556Srgrimes _pthread_attr_getinheritsched(src, &v); 841556Srgrimes _pthread_attr_setinheritsched(dst, v); 851556Srgrimes 861556Srgrimes _pthread_attr_getschedparam(src, &sched); 87157601Sstefanf _pthread_attr_setschedparam(dst, &sched); 881556Srgrimes 891556Srgrimes _pthread_attr_getscope(src, &v); 901556Srgrimes _pthread_attr_setscope(dst, v); 911556Srgrimes 921556Srgrimes _pthread_attr_getstacksize(src, &u); 931556Srgrimes _pthread_attr_setstacksize(dst, u); 941556Srgrimes 951556Srgrimes _pthread_attr_getstackaddr(src, &a); 9620425Ssteve _pthread_attr_setstackaddr(src, a); 971556Srgrimes 981556Srgrimes _pthread_attr_getguardsize(src, &u); 9990111Simp _pthread_attr_setguardsize(dst, u); 10090111Simp} 10190111Simp 10290111Simpstatic __inline int 1031556Srgrimeshave_threads(void) 1041556Srgrimes{ 1051556Srgrimes return (&_pthread_create != NULL); 1061556Srgrimes} 1071556Srgrimes 1081556Srgrimesvoid 1091556Srgrimes__sigev_thread_init(void) 1101556Srgrimes{ 1111556Srgrimes static int inited = 0; 11290111Simp pthread_mutexattr_t mattr; 11317987Speter int i; 1141556Srgrimes 1151556Srgrimes _pthread_mutexattr_init(&mattr); 1161556Srgrimes _pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL); 1171556Srgrimes sigev_list_mtx = malloc(sizeof(pthread_mutex_t)); 1181556Srgrimes _pthread_mutex_init(sigev_list_mtx, &mattr); 1191556Srgrimes _pthread_mutexattr_destroy(&mattr); 1201556Srgrimes 1211556Srgrimes for (i = 0; i < HASH_QUEUES; ++i) 1221556Srgrimes LIST_INIT(&sigev_hash[i]); 1231556Srgrimes LIST_INIT(&sigev_all); 1241556Srgrimes LIST_INIT(&sigev_threads); 1251556Srgrimes sigev_default_thread = NULL; 1261556Srgrimes if (atfork_registered == 0) { 1271556Srgrimes _pthread_atfork( 1281556Srgrimes __sigev_fork_prepare, 1291556Srgrimes __sigev_fork_parent, 1301556Srgrimes __sigev_fork_child); 13120425Ssteve atfork_registered = 1; 13220425Ssteve } 13320425Ssteve if (!inited) { 13420425Ssteve _pthread_attr_init(&sigev_default_attr); 13520425Ssteve _pthread_attr_setscope(&sigev_default_attr, 13620425Ssteve PTHREAD_SCOPE_SYSTEM); 13720425Ssteve _pthread_attr_setdetachstate(&sigev_default_attr, 13820425Ssteve PTHREAD_CREATE_DETACHED); 13920425Ssteve inited = 1; 14020425Ssteve } 14120425Ssteve sigev_default_thread = sigev_thread_create(0); 14220425Ssteve} 14320425Ssteve 144104283Stjrint 145104283Stjr__sigev_check_init(void) 146104132Stjr{ 1471556Srgrimes if (!have_threads()) 1481556Srgrimes return (-1); 1491556Srgrimes 1501556Srgrimes _pthread_once(&sigev_once, __sigev_thread_init); 15190111Simp return (sigev_default_thread != NULL) ? 0 : -1; 15290111Simp} 1531556Srgrimes 1541556Srgrimesstatic void 1551556Srgrimes__sigev_fork_prepare(void) 1561556Srgrimes{ 1571556Srgrimes} 1581556Srgrimes 1591556Srgrimesstatic void 1601556Srgrimes__sigev_fork_parent(void) 1611556Srgrimes{ 1621556Srgrimes} 1631556Srgrimes 1641556Srgrimesstatic void 1651556Srgrimes__sigev_fork_child(void) 1661556Srgrimes{ 1671556Srgrimes /* 1681556Srgrimes * This is a hack, the thread libraries really should 1691556Srgrimes * check if the handlers were already registered in 1701556Srgrimes * pthread_atfork(). 1711556Srgrimes */ 1721556Srgrimes atfork_registered = 1; 1731556Srgrimes memcpy(&sigev_once, &sigev_once_default, sizeof(sigev_once)); 1741556Srgrimes __sigev_thread_init(); 1751556Srgrimes} 1761556Srgrimes 1771556Srgrimesvoid 1781556Srgrimes__sigev_list_lock(void) 1791556Srgrimes{ 1801556Srgrimes _pthread_mutex_lock(sigev_list_mtx); 18190111Simp} 18290111Simp 18325223Sstevevoid 1841556Srgrimes__sigev_list_unlock(void) 1851556Srgrimes{ 1861556Srgrimes _pthread_mutex_unlock(sigev_list_mtx); 1871556Srgrimes} 1881556Srgrimes 1891556Srgrimesstruct sigev_node * 190193223Srse__sigev_alloc(int type, const struct sigevent *evp, struct sigev_node *prev, 191193223Srse int usedefault) 1921556Srgrimes{ 1931556Srgrimes struct sigev_node *sn; 1941556Srgrimes 1951556Srgrimes sn = calloc(1, sizeof(*sn)); 1961556Srgrimes if (sn != NULL) { 19717987Speter sn->sn_value = evp->sigev_value; 1981556Srgrimes sn->sn_func = evp->sigev_notify_function; 1991556Srgrimes sn->sn_gen = atomic_fetchadd_int(&sigev_generation, 1); 2001556Srgrimes sn->sn_type = type; 2011556Srgrimes _pthread_attr_init(&sn->sn_attr); 2021556Srgrimes _pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED); 2031556Srgrimes if (evp->sigev_notify_attributes) 2041556Srgrimes attrcopy(evp->sigev_notify_attributes, &sn->sn_attr); 2051556Srgrimes if (prev) { 2061556Srgrimes __sigev_list_lock(); 2071556Srgrimes prev->sn_tn->tn_refcount++; 2081556Srgrimes __sigev_list_unlock(); 2091556Srgrimes sn->sn_tn = prev->sn_tn; 2101556Srgrimes } else { 2111556Srgrimes sn->sn_tn = sigev_thread_create(usedefault); 2121556Srgrimes if (sn->sn_tn == NULL) { 2131556Srgrimes _pthread_attr_destroy(&sn->sn_attr); 2141556Srgrimes free(sn); 2151556Srgrimes sn = NULL; 2161556Srgrimes } 2171556Srgrimes } 2181556Srgrimes } 21917987Speter return (sn); 22090111Simp} 22117987Speter 2221556Srgrimesvoid 2231556Srgrimes__sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp, 2241556Srgrimes sigev_id_t id) 2251556Srgrimes{ 2261556Srgrimes /* 2271556Srgrimes * Build a new sigevent, and tell kernel to deliver SIGSERVICE 2281556Srgrimes * signal to the new thread. 2291556Srgrimes */ 2301556Srgrimes newevp->sigev_notify = SIGEV_THREAD_ID; 2311556Srgrimes newevp->sigev_signo = SIGSERVICE; 2321556Srgrimes newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid; 2331556Srgrimes newevp->sigev_value.sival_ptr = (void *)id; 2341556Srgrimes} 2351556Srgrimes 2361556Srgrimesvoid 2371556Srgrimes__sigev_free(struct sigev_node *sn) 2381556Srgrimes{ 2391556Srgrimes _pthread_attr_destroy(&sn->sn_attr); 24098157Stjr free(sn); 24198157Stjr} 2421556Srgrimes 2431556Srgrimesstruct sigev_node * 2441556Srgrimes__sigev_find(int type, sigev_id_t id) 2451556Srgrimes{ 2461556Srgrimes struct sigev_node *sn; 2471556Srgrimes int chain = HASH(type, id); 2481556Srgrimes 24917987Speter LIST_FOREACH(sn, &sigev_hash[chain], sn_link) { 2501556Srgrimes if (sn->sn_type == type && sn->sn_id == id) 25117987Speter break; 2521556Srgrimes } 2531556Srgrimes return (sn); 2541556Srgrimes} 25594775Sgreid 25694775Sgreidint 25794775Sgreid__sigev_register(struct sigev_node *sn) 25894775Sgreid{ 2591556Srgrimes int chain = HASH(sn->sn_type, sn->sn_id); 2601556Srgrimes 2611556Srgrimes LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link); 2621556Srgrimes return (0); 2631556Srgrimes} 2641556Srgrimes 2651556Srgrimesint 2661556Srgrimes__sigev_delete(int type, sigev_id_t id) 2671556Srgrimes{ 2681556Srgrimes struct sigev_node *sn; 26990111Simp 27090111Simp sn = __sigev_find(type, id); 2711556Srgrimes if (sn != NULL) 2721556Srgrimes return (__sigev_delete_node(sn)); 2731556Srgrimes return (0); 2741556Srgrimes} 2751556Srgrimes 2761556Srgrimesint 2771556Srgrimes__sigev_delete_node(struct sigev_node *sn) 2781556Srgrimes{ 2791556Srgrimes LIST_REMOVE(sn, sn_link); 2801556Srgrimes 2811556Srgrimes if (--sn->sn_tn->tn_refcount == 0) 2821556Srgrimes _pthread_kill(sn->sn_tn->tn_thread, SIGSERVICE); 2831556Srgrimes if (sn->sn_flags & SNF_WORKING) 2841556Srgrimes sn->sn_flags |= SNF_REMOVED; 2851556Srgrimes else 2861556Srgrimes __sigev_free(sn); 2871556Srgrimes return (0); 2881556Srgrimes} 289196483Sjilles 2901556Srgrimesstatic sigev_id_t 2911556Srgrimessigev_get_id(siginfo_t *si) 2921556Srgrimes{ 2931556Srgrimes switch(si->si_code) { 2941556Srgrimes case SI_TIMER: 2951556Srgrimes return (si->si_timerid); 2961556Srgrimes case SI_MESGQ: 2971556Srgrimes return (si->si_mqd); 2981556Srgrimes case SI_ASYNCIO: 2991556Srgrimes return (sigev_id_t)si->si_value.sival_ptr; 3001556Srgrimes } 3011556Srgrimes return (-1); 3021556Srgrimes} 3031556Srgrimes 3041556Srgrimesstatic struct sigev_thread * 3051556Srgrimessigev_thread_create(int usedefault) 3061556Srgrimes{ 3071556Srgrimes struct sigev_thread *tn; 3081556Srgrimes sigset_t set, oset; 3091556Srgrimes int ret; 3101556Srgrimes 3111556Srgrimes if (usedefault && sigev_default_thread) { 3121556Srgrimes __sigev_list_lock(); 31390111Simp sigev_default_thread->tn_refcount++; 31417987Speter __sigev_list_unlock(); 3151556Srgrimes return (sigev_default_thread); 3161556Srgrimes } 3171556Srgrimes 3181556Srgrimes tn = malloc(sizeof(*tn)); 3191556Srgrimes tn->tn_cur = NULL; 3201556Srgrimes tn->tn_lwpid = -1; 3211556Srgrimes tn->tn_refcount = 1; 322157601Sstefanf _pthread_cond_init(&tn->tn_cv, NULL); 3231556Srgrimes 3241556Srgrimes /* for debug */ 3251556Srgrimes __sigev_list_lock(); 3261556Srgrimes LIST_INSERT_HEAD(&sigev_threads, tn, tn_link); 3271556Srgrimes __sigev_list_unlock(); 3281556Srgrimes 3291556Srgrimes sigfillset(&set); /* SIGSERVICE is masked. */ 3301556Srgrimes sigdelset(&set, SIGBUS); 3311556Srgrimes sigdelset(&set, SIGILL); 3321556Srgrimes sigdelset(&set, SIGFPE); 3331556Srgrimes sigdelset(&set, SIGSEGV); 3341556Srgrimes sigdelset(&set, SIGTRAP); 3351556Srgrimes _sigprocmask(SIG_SETMASK, &set, &oset); 336157601Sstefanf ret = _pthread_create(&tn->tn_thread, &sigev_default_attr, 3371556Srgrimes sigev_service_loop, tn); 3381556Srgrimes _sigprocmask(SIG_SETMASK, &oset, NULL); 3391556Srgrimes 3401556Srgrimes if (ret != 0) { 341157601Sstefanf __sigev_list_lock(); 3421556Srgrimes LIST_REMOVE(tn, tn_link); 3431556Srgrimes __sigev_list_unlock(); 3441556Srgrimes free(tn); 3451556Srgrimes tn = NULL; 3461556Srgrimes } else { 3471556Srgrimes /* wait the thread to get its lwpid */ 3481556Srgrimes 3491556Srgrimes __sigev_list_lock(); 3501556Srgrimes while (tn->tn_lwpid == -1) 3511556Srgrimes _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx); 3521556Srgrimes __sigev_list_unlock(); 3531556Srgrimes } 3541556Srgrimes return (tn); 3551556Srgrimes} 3561556Srgrimes 3571556Srgrimes/* 3581556Srgrimes * The thread receives notification from kernel and creates 3591556Srgrimes * a thread to call user callback function. 3601556Srgrimes */ 3611556Srgrimesstatic void * 3621556Srgrimessigev_service_loop(void *arg) 363157601Sstefanf{ 3641556Srgrimes static int failure; 3651556Srgrimes 3661556Srgrimes siginfo_t si; 3671556Srgrimes sigset_t set; 3681556Srgrimes struct sigev_thread *tn; 369157601Sstefanf struct sigev_node *sn; 3701556Srgrimes sigev_id_t id; 3711556Srgrimes pthread_t td; 3721556Srgrimes int ret; 3731556Srgrimes 3741556Srgrimes tn = arg; 3751556Srgrimes thr_self(&tn->tn_lwpid); 3761556Srgrimes __sigev_list_lock(); 3771556Srgrimes _pthread_cond_broadcast(&tn->tn_cv); 3781556Srgrimes __sigev_list_unlock(); 3791556Srgrimes 3801556Srgrimes sigemptyset(&set); 3811556Srgrimes sigaddset(&set, SIGSERVICE); 3821556Srgrimes for (;;) { 3831556Srgrimes ret = sigwaitinfo(&set, &si); 3841556Srgrimes 385100351Stjr __sigev_list_lock(); 3861556Srgrimes if (tn->tn_refcount == 0) { 3871556Srgrimes LIST_REMOVE(tn, tn_link); 3881556Srgrimes __sigev_list_unlock(); 3891556Srgrimes free(tn); 3901556Srgrimes break; 39117987Speter } 3921556Srgrimes 3931556Srgrimes if (ret == -1) { 3941556Srgrimes __sigev_list_unlock(); 3951556Srgrimes continue; 3961556Srgrimes } 3971556Srgrimes 3981556Srgrimes id = sigev_get_id(&si); 3991556Srgrimes sn = __sigev_find(si.si_code, id); 4001556Srgrimes if (sn == NULL) { 4011556Srgrimes __sigev_list_unlock(); 4021556Srgrimes continue; 4031556Srgrimes } 4041556Srgrimes 4051556Srgrimes sn->sn_info = si; 4061556Srgrimes if (sn->sn_flags & SNF_SYNC) 4071556Srgrimes tn->tn_cur = sn; 4081556Srgrimes else 4091556Srgrimes tn->tn_cur = NULL; 4101556Srgrimes sn->sn_flags |= SNF_WORKING; 4111556Srgrimes __sigev_list_unlock(); 4121556Srgrimes 4131556Srgrimes ret = _pthread_create(&td, &sn->sn_attr, worker_routine, sn); 4141556Srgrimes if (ret != 0) { 4151556Srgrimes if (failure++ < 5) 4161556Srgrimes warnc(ret, "%s:%s failed to create thread.\n", 4171556Srgrimes __FILE__, __func__); 4181556Srgrimes 4191556Srgrimes __sigev_list_lock(); 4201556Srgrimes sn->sn_flags &= ~SNF_WORKING; 4211556Srgrimes if (sn->sn_flags & SNF_REMOVED) 4221556Srgrimes __sigev_free(sn); 4231556Srgrimes __sigev_list_unlock(); 4241556Srgrimes } else if (tn->tn_cur) { 425104283Stjr __sigev_list_lock(); 426104283Stjr while (tn->tn_cur) 427104283Stjr _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx); 428104283Stjr __sigev_list_unlock(); 429104283Stjr } 430104283Stjr } 4311556Srgrimes return (0); 4321556Srgrimes} 4331556Srgrimes 4341556Srgrimes/* 4351556Srgrimes * newly created worker thread to call user callback function. 4361556Srgrimes */ 4371556Srgrimesstatic void * 438157601Sstefanfworker_routine(void *arg) 4391556Srgrimes{ 4401556Srgrimes struct sigev_node *sn = arg; 4411556Srgrimes 4421556Srgrimes _pthread_cleanup_push(worker_cleanup, sn); 4431556Srgrimes sn->sn_dispatch(sn); 4441556Srgrimes _pthread_cleanup_pop(1); 4451556Srgrimes 4461556Srgrimes return (0); 4471556Srgrimes} 448157601Sstefanf 44917987Speter/* clean up a notification after dispatch. */ 45025223Sstevestatic void 4511556Srgrimesworker_cleanup(void *arg) 4521556Srgrimes{ 453157601Sstefanf struct sigev_node *sn = arg; 454157601Sstefanf 4551556Srgrimes __sigev_list_lock(); 456157601Sstefanf if (sn->sn_flags & SNF_SYNC) { 4571556Srgrimes sn->sn_tn->tn_cur = NULL; 4581556Srgrimes _pthread_cond_broadcast(&sn->sn_tn->tn_cv); 4591556Srgrimes } 4601556Srgrimes if (sn->sn_flags & SNF_REMOVED) 4611556Srgrimes __sigev_free(sn); 4621556Srgrimes else 4631556Srgrimes sn->sn_flags &= ~SNF_WORKING; 4641556Srgrimes __sigev_list_unlock(); 4651556Srgrimes} 4661556Srgrimes