sigev_thread.c revision 156529
1235288Sadrian/* 2235288Sadrian * Copyright (c) 2005 David Xu <davidxu@freebsd.org> 3235288Sadrian * All rights reserved. 4235288Sadrian * 5235288Sadrian * Redistribution and use in source and binary forms, with or without 6235288Sadrian * modification, are permitted provided that the following conditions 7235288Sadrian * are met: 8235288Sadrian * 1. Redistributions of source code must retain the above copyright 9235288Sadrian * notice unmodified, this list of conditions, and the following 10235288Sadrian * disclaimer. 11235288Sadrian * 2. Redistributions in binary form must reproduce the above copyright 12235288Sadrian * notice, this list of conditions and the following disclaimer in the 13235288Sadrian * documentation and/or other materials provided with the distribution. 14235288Sadrian * 15235288Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16235288Sadrian * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17235288Sadrian * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18235288Sadrian * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19235288Sadrian * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20235288Sadrian * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21235288Sadrian * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22235288Sadrian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23235288Sadrian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24235288Sadrian * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25235288Sadrian * 26235288Sadrian * $FreeBSD: head/lib/librt/sigev_thread.c 156529 2006-03-10 08:01:23Z davidxu $ 27235288Sadrian * 28235288Sadrian */ 29235288Sadrian 30235288Sadrian#include <sys/types.h> 31235288Sadrian#include <machine/atomic.h> 32235288Sadrian 33235288Sadrian#include "namespace.h" 34257284Sglebius#include <err.h> 35235288Sadrian#include <errno.h> 36235288Sadrian#include <ucontext.h> 37235288Sadrian#include <sys/thr.h> 38235288Sadrian#include <stdio.h> 39235288Sadrian#include <stdlib.h> 40235288Sadrian#include <string.h> 41235288Sadrian#include <signal.h> 42257284Sglebius#include <pthread.h> 43235288Sadrian#include "un-namespace.h" 44235288Sadrian 45235288Sadrian#include "sigev_thread.h" 46235288Sadrian 47235288SadrianLIST_HEAD(sigev_list_head, sigev_node); 48235288Sadrian#define HASH_QUEUES 17 49235288Sadrian#define HASH(t, id) ((((id) << 3) + (t)) % HASH_QUEUES) 50235288Sadrian 51235288Sadrianstatic struct sigev_list_head sigev_hash[HASH_QUEUES]; 52235288Sadrianstatic struct sigev_list_head sigev_all; 53235288Sadrianstatic LIST_HEAD(,sigev_thread) sigev_threads; 54235288Sadrianstatic int sigev_generation; 55235288Sadrianstatic pthread_mutex_t *sigev_list_mtx; 56235288Sadrianstatic pthread_once_t sigev_once = PTHREAD_ONCE_INIT; 57235288Sadrianstatic pthread_once_t sigev_once_default = PTHREAD_ONCE_INIT; 58235288Sadrianstatic struct sigev_thread *sigev_default_thread; 59235288Sadrianstatic pthread_attr_t sigev_default_attr; 60235288Sadrianstatic int atfork_registered; 61235288Sadrian 62235288Sadrianstatic void __sigev_fork_prepare(void); 63253572Sloosstatic void __sigev_fork_parent(void); 64235288Sadrianstatic void __sigev_fork_child(void); 65235323Sadrianstatic struct sigev_thread *sigev_thread_create(int); 66235288Sadrianstatic void *sigev_service_loop(void *); 67235288Sadrianstatic void *worker_routine(void *); 68235288Sadrianstatic void worker_cleanup(void *); 69256582Sadrian 70235288Sadrian#pragma weak pthread_create 71235288Sadrian 72235288Sadrianstatic void 73235288Sadrianattrcopy(pthread_attr_t *src, pthread_attr_t *dst) 74235288Sadrian{ 75235288Sadrian struct sched_param sched; 76235288Sadrian void *a; 77235288Sadrian size_t u; 78235288Sadrian int v; 79235288Sadrian 80235288Sadrian _pthread_attr_getschedpolicy(src, &v); 81235288Sadrian _pthread_attr_setschedpolicy(dst, v); 82235288Sadrian 83235288Sadrian _pthread_attr_getinheritsched(src, &v); 84235288Sadrian _pthread_attr_setinheritsched(dst, v); 85235288Sadrian 86235288Sadrian _pthread_attr_getschedparam(src, &sched); 87235288Sadrian _pthread_attr_setschedparam(dst, &sched); 88235288Sadrian 89235288Sadrian _pthread_attr_getscope(src, &v); 90235288Sadrian _pthread_attr_setscope(dst, v); 91235288Sadrian 92235288Sadrian _pthread_attr_getstacksize(src, &u); 93235288Sadrian _pthread_attr_setstacksize(dst, u); 94235323Sadrian 95235323Sadrian _pthread_attr_getstackaddr(src, &a); 96235323Sadrian _pthread_attr_setstackaddr(src, a); 97235323Sadrian 98235323Sadrian _pthread_attr_getguardsize(src, &u); 99256582Sadrian _pthread_attr_setguardsize(dst, u); 100241577Sray} 101235323Sadrian 102235323Sadrianstatic __inline int 103235323Sadrianhave_threads(void) 104256582Sadrian{ 105256582Sadrian return (&pthread_create != NULL); 106256582Sadrian} 107256582Sadrian 108256582Sadrianvoid 109256582Sadrian__sigev_thread_init(void) 110256582Sadrian{ 111256582Sadrian static int inited = 0; 112256582Sadrian pthread_mutexattr_t mattr; 113235323Sadrian int i; 114235288Sadrian 115262201Sadrian _pthread_mutexattr_init(&mattr); 116262201Sadrian _pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL); 117256582Sadrian sigev_list_mtx = malloc(sizeof(pthread_mutex_t)); 118256582Sadrian _pthread_mutex_init(sigev_list_mtx, &mattr); 119235288Sadrian _pthread_mutexattr_destroy(&mattr); 120235288Sadrian 121235288Sadrian for (i = 0; i < HASH_QUEUES; ++i) 122256582Sadrian LIST_INIT(&sigev_hash[i]); 123235288Sadrian LIST_INIT(&sigev_all); 124235288Sadrian LIST_INIT(&sigev_threads); 125235288Sadrian sigev_default_thread = NULL; 126256582Sadrian if (atfork_registered == 0) { 127256582Sadrian _pthread_atfork( 128256582Sadrian __sigev_fork_prepare, 129235288Sadrian __sigev_fork_parent, 130235288Sadrian __sigev_fork_child); 131235288Sadrian atfork_registered = 1; 132262202Sadrian } 133262202Sadrian if (!inited) { 134262202Sadrian _pthread_attr_init(&sigev_default_attr); 135262202Sadrian _pthread_attr_setscope(&sigev_default_attr, 136262202Sadrian PTHREAD_SCOPE_SYSTEM); 137235288Sadrian _pthread_attr_setdetachstate(&sigev_default_attr, 138235288Sadrian PTHREAD_CREATE_DETACHED); 139235288Sadrian inited = 1; 140235323Sadrian } 141235323Sadrian sigev_default_thread = sigev_thread_create(0); 142256582Sadrian} 143256582Sadrian 144235288Sadrianint 145235288Sadrian__sigev_check_init(void) 146235288Sadrian{ 147235288Sadrian if (!have_threads()) 148235288Sadrian return (-1); 149235288Sadrian 150235288Sadrian _pthread_once(&sigev_once, __sigev_thread_init); 151235288Sadrian return (sigev_default_thread != NULL) ? 0 : -1; 152235288Sadrian} 153235288Sadrian 154235288Sadrianstatic void 155235288Sadrian__sigev_fork_prepare(void) 156235288Sadrian{ 157235288Sadrian} 158235288Sadrian 159235288Sadrianstatic void 160235288Sadrian__sigev_fork_parent(void) 161235288Sadrian{ 162235288Sadrian} 163235288Sadrian 164235288Sadrianstatic void 165235288Sadrian__sigev_fork_child(void) 166235288Sadrian{ 167235288Sadrian /* 168235288Sadrian * This is a hack, the thread libraries really should 169235288Sadrian * check if the handlers were already registered in 170235288Sadrian * pthread_atfork(). 171235288Sadrian */ 172235288Sadrian atfork_registered = 1; 173235288Sadrian memcpy(&sigev_once, &sigev_once_default, sizeof(sigev_once)); 174235288Sadrian __sigev_thread_init(); 175235288Sadrian} 176235288Sadrian 177235288Sadrianvoid 178235288Sadrian__sigev_list_lock(void) 179235288Sadrian{ 180235288Sadrian _pthread_mutex_lock(sigev_list_mtx); 181235288Sadrian} 182235288Sadrian 183235288Sadrianvoid 184235288Sadrian__sigev_list_unlock(void) 185235288Sadrian{ 186235288Sadrian _pthread_mutex_unlock(sigev_list_mtx); 187253572Sloos} 188253572Sloos 189253572Sloosstruct sigev_node * 190253572Sloos__sigev_alloc(int type, const struct sigevent *evp, struct sigev_node *prev, 191253572Sloos int usedefault) 192253572Sloos{ 193253572Sloos struct sigev_node *sn; 194253572Sloos 195253572Sloos sn = calloc(1, sizeof(*sn)); 196253572Sloos if (sn != NULL) { 197253572Sloos sn->sn_value = evp->sigev_value; 198253572Sloos sn->sn_func = evp->sigev_notify_function; 199253572Sloos sn->sn_gen = atomic_fetchadd_int(&sigev_generation, 1); 200253572Sloos sn->sn_type = type; 201253572Sloos _pthread_attr_init(&sn->sn_attr); 202253572Sloos _pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED); 203253572Sloos if (evp->sigev_notify_attributes) 204253572Sloos attrcopy(evp->sigev_notify_attributes, &sn->sn_attr); 205253572Sloos if (prev) { 206253572Sloos __sigev_list_lock(); 207253572Sloos prev->sn_tn->tn_refcount++; 208253572Sloos __sigev_list_unlock(); 209253572Sloos sn->sn_tn = prev->sn_tn; 210253572Sloos } else { 211253572Sloos sn->sn_tn = sigev_thread_create(usedefault); 212253572Sloos if (sn->sn_tn == NULL) { 213253572Sloos _pthread_attr_destroy(&sn->sn_attr); 214253572Sloos free(sn); 215253572Sloos sn = NULL; 216253572Sloos } 217253572Sloos } 218253572Sloos } 219253572Sloos return (sn); 220253572Sloos} 221253572Sloos 222253572Sloosvoid 223253572Sloos__sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp, 224253572Sloos sigev_id_t id) 225253572Sloos{ 226253572Sloos /* 227262203Sadrian * Build a new sigevent, and tell kernel to deliver SIGSERVICE 228253572Sloos * signal to the new thread. 229253572Sloos */ 230253572Sloos newevp->sigev_notify = SIGEV_THREAD_ID; 231262203Sadrian newevp->sigev_signo = SIGSERVICE; 232262203Sadrian newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid; 233262203Sadrian newevp->sigev_value.sival_ptr = (void *)id; 234262203Sadrian} 235262203Sadrian 236262203Sadrianvoid 237262203Sadrian__sigev_free(struct sigev_node *sn) 238262203Sadrian{ 239262203Sadrian _pthread_attr_destroy(&sn->sn_attr); 240262203Sadrian free(sn); 241262203Sadrian} 242262203Sadrian 243262203Sadrianstruct sigev_node * 244253572Sloos__sigev_find(int type, sigev_id_t id) 245253572Sloos{ 246253572Sloos struct sigev_node *sn; 247253572Sloos int chain = HASH(type, id); 248253572Sloos 249253572Sloos LIST_FOREACH(sn, &sigev_hash[chain], sn_link) { 250253572Sloos if (sn->sn_type == type && sn->sn_id == id) 251253572Sloos break; 252253572Sloos } 253253572Sloos return (sn); 254235288Sadrian} 255235288Sadrian 256235288Sadrianint 257235288Sadrian__sigev_register(struct sigev_node *sn) 258262203Sadrian{ 259235288Sadrian int chain = HASH(sn->sn_type, sn->sn_id); 260235288Sadrian 261235288Sadrian LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link); 262235288Sadrian return (0); 263235288Sadrian} 264235288Sadrian 265235288Sadrianint 266235288Sadrian__sigev_delete(int type, sigev_id_t id) 267235288Sadrian{ 268235288Sadrian struct sigev_node *sn; 269235288Sadrian 270235288Sadrian sn = __sigev_find(type, id); 271235288Sadrian if (sn != NULL) 272235323Sadrian return (__sigev_delete_node(sn)); 273235323Sadrian return (0); 274256582Sadrian} 275256582Sadrian 276235323Sadrianint 277235288Sadrian__sigev_delete_node(struct sigev_node *sn) 278235288Sadrian{ 279235288Sadrian LIST_REMOVE(sn, sn_link); 280235288Sadrian 281235288Sadrian if (--sn->sn_tn->tn_refcount == 0) 282235288Sadrian _pthread_kill(sn->sn_tn->tn_thread, SIGSERVICE); 283235288Sadrian if (sn->sn_flags & SNF_WORKING) 284235288Sadrian sn->sn_flags |= SNF_REMOVED; 285253572Sloos else 286235288Sadrian __sigev_free(sn); 287235288Sadrian return (0); 288235288Sadrian} 289235288Sadrian 290235288Sadrianstatic sigev_id_t 291235288Sadriansigev_get_id(siginfo_t *si) 292235288Sadrian{ 293256582Sadrian switch(si->si_code) { 294235288Sadrian case SI_TIMER: 295235288Sadrian return (si->si_timerid); 296235288Sadrian case SI_MESGQ: 297235288Sadrian return (si->si_mqd); 298235288Sadrian case SI_ASYNCIO: 299235288Sadrian return (sigev_id_t)si->si_value.sival_ptr; 300235288Sadrian } 301235288Sadrian return (-1); 302235288Sadrian} 303256582Sadrian 304256582Sadrianstatic struct sigev_thread * 305235288Sadriansigev_thread_create(int usedefault) 306253572Sloos{ 307253572Sloos struct sigev_thread *tn; 308253572Sloos sigset_t set, oset; 309253572Sloos int ret; 310253572Sloos 311235288Sadrian if (usedefault && sigev_default_thread) { 312235288Sadrian __sigev_list_lock(); 313256582Sadrian sigev_default_thread->tn_refcount++; 314256582Sadrian __sigev_list_unlock(); 315256582Sadrian return (sigev_default_thread); 316256582Sadrian } 317235288Sadrian 318235288Sadrian tn = malloc(sizeof(*tn)); 319235288Sadrian tn->tn_cur = NULL; 320235288Sadrian tn->tn_lwpid = -1; 321253572Sloos tn->tn_refcount = 1; 322262203Sadrian _pthread_cond_init(&tn->tn_cv, NULL); 323262203Sadrian 324262203Sadrian /* for debug */ 325253572Sloos __sigev_list_lock(); 326235288Sadrian LIST_INSERT_HEAD(&sigev_threads, tn, tn_link); 327235288Sadrian __sigev_list_unlock(); 328235288Sadrian 329235288Sadrian sigfillset(&set); /* SIGSERVICE is masked. */ 330235288Sadrian sigdelset(&set, SIGBUS); 331235288Sadrian sigdelset(&set, SIGILL); 332235288Sadrian sigdelset(&set, SIGFPE); 333253572Sloos sigdelset(&set, SIGSEGV); 334253572Sloos sigdelset(&set, SIGTRAP); 335253572Sloos _sigprocmask(SIG_SETMASK, &set, &oset); 336253572Sloos ret = pthread_create(&tn->tn_thread, &sigev_default_attr, 337253572Sloos sigev_service_loop, tn); 338235288Sadrian _sigprocmask(SIG_SETMASK, &oset, NULL); 339235288Sadrian 340235288Sadrian if (ret != 0) { 341235288Sadrian __sigev_list_lock(); 342235288Sadrian LIST_REMOVE(tn, tn_link); 343235288Sadrian __sigev_list_unlock(); 344235288Sadrian free(tn); 345241578Sray tn = NULL; 346241578Sray } else { 347235288Sadrian /* wait the thread to get its lwpid */ 348241578Sray 349235288Sadrian __sigev_list_lock(); 350235288Sadrian while (tn->tn_lwpid == -1) 351235288Sadrian _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx); 352235288Sadrian __sigev_list_unlock(); 353235288Sadrian } 354235288Sadrian return (tn); 355235288Sadrian} 356235288Sadrian 357235288Sadrian/* 358235288Sadrian * The thread receives notification from kernel and creates 359235288Sadrian * a thread to call user callback function. 360235288Sadrian */ 361235288Sadrianstatic void * 362235288Sadriansigev_service_loop(void *arg) 363235288Sadrian{ 364235288Sadrian static int failure; 365235288Sadrian 366235288Sadrian siginfo_t si; 367235288Sadrian sigset_t set; 368235288Sadrian struct sigev_thread *tn; 369235288Sadrian struct sigev_node *sn; 370235288Sadrian sigev_id_t id; 371235288Sadrian pthread_t td; 372235288Sadrian int ret; 373235288Sadrian 374235288Sadrian tn = arg; 375235288Sadrian thr_self(&tn->tn_lwpid); 376235288Sadrian __sigev_list_lock(); 377235288Sadrian _pthread_cond_broadcast(&tn->tn_cv); 378235288Sadrian __sigev_list_unlock(); 379235288Sadrian 380235288Sadrian sigemptyset(&set); 381235288Sadrian sigaddset(&set, SIGSERVICE); 382235288Sadrian for (;;) { 383235288Sadrian ret = sigwaitinfo(&set, &si); 384235288Sadrian 385235288Sadrian __sigev_list_lock(); 386235288Sadrian if (tn->tn_refcount == 0) { 387235288Sadrian LIST_REMOVE(tn, tn_link); 388235288Sadrian __sigev_list_unlock(); 389235288Sadrian free(tn); 390235288Sadrian break; 391235288Sadrian } 392235288Sadrian 393235288Sadrian if (ret == -1) { 394235288Sadrian __sigev_list_unlock(); 395235288Sadrian continue; 396235288Sadrian } 397235288Sadrian 398235288Sadrian id = sigev_get_id(&si); 399235288Sadrian sn = __sigev_find(si.si_code, id); 400235288Sadrian if (sn == NULL) { 401235288Sadrian __sigev_list_unlock(); 402235288Sadrian continue; 403235288Sadrian } 404235288Sadrian 405235288Sadrian sn->sn_info = si; 406235288Sadrian if (sn->sn_flags & SNF_SYNC) 407235288Sadrian tn->tn_cur = sn; 408235288Sadrian else 409235288Sadrian tn->tn_cur = NULL; 410235288Sadrian sn->sn_flags |= SNF_WORKING; 411235288Sadrian __sigev_list_unlock(); 412235288Sadrian 413235288Sadrian ret = pthread_create(&td, &sn->sn_attr, worker_routine, sn); 414235288Sadrian if (ret != 0) { 415235288Sadrian if (failure++ < 5) 416235288Sadrian warnc(ret, "%s:%s failed to create thread.\n", 417235288Sadrian __FILE__, __func__); 418235288Sadrian 419235288Sadrian __sigev_list_lock(); 420235288Sadrian sn->sn_flags &= ~SNF_WORKING; 421235288Sadrian if (sn->sn_flags & SNF_REMOVED) 422235288Sadrian __sigev_free(sn); 423235288Sadrian __sigev_list_unlock(); 424235288Sadrian } else if (tn->tn_cur) { 425235288Sadrian __sigev_list_lock(); 426235288Sadrian while (tn->tn_cur) 427235288Sadrian _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx); 428235288Sadrian __sigev_list_unlock(); 429235288Sadrian } 430235288Sadrian } 431235288Sadrian return (0); 432235288Sadrian} 433235288Sadrian 434235288Sadrian/* 435235288Sadrian * newly created worker thread to call user callback function. 436235288Sadrian */ 437235288Sadrianstatic void * 438235288Sadrianworker_routine(void *arg) 439235288Sadrian{ 440235288Sadrian struct sigev_node *sn = arg; 441235288Sadrian 442235288Sadrian _pthread_cleanup_push(worker_cleanup, sn); 443235288Sadrian sn->sn_dispatch(sn); 444235288Sadrian _pthread_cleanup_pop(1); 445235288Sadrian 446235288Sadrian return (0); 447235288Sadrian} 448235288Sadrian 449235288Sadrian/* clean up a notification after dispatch. */ 450235288Sadrianstatic void 451235288Sadrianworker_cleanup(void *arg) 452235288Sadrian{ 453235288Sadrian struct sigev_node *sn = arg; 454235288Sadrian 455235288Sadrian __sigev_list_lock(); 456241578Sray if (sn->sn_flags & SNF_SYNC) { 457241578Sray sn->sn_tn->tn_cur = NULL; 458235288Sadrian _pthread_cond_broadcast(&sn->sn_tn->tn_cv); 459235288Sadrian } 460235288Sadrian if (sn->sn_flags & SNF_REMOVED) 461235288Sadrian __sigev_free(sn); 462235288Sadrian else 463235288Sadrian sn->sn_flags &= ~SNF_WORKING; 464235288Sadrian __sigev_list_unlock(); 465235288Sadrian} 466256582Sadrian