sigev_thread.c revision 181778
1251875Speter/* 2251875Speter * Copyright (c) 2005 David Xu <davidxu@freebsd.org> 3251875Speter * All rights reserved. 4251875Speter * 5251875Speter * Redistribution and use in source and binary forms, with or without 6251875Speter * modification, are permitted provided that the following conditions 7251875Speter * are met: 8251875Speter * 1. Redistributions of source code must retain the above copyright 9251875Speter * notice unmodified, this list of conditions, and the following 10251875Speter * disclaimer. 11251875Speter * 2. Redistributions in binary form must reproduce the above copyright 12251875Speter * notice, this list of conditions and the following disclaimer in the 13251875Speter * documentation and/or other materials provided with the distribution. 14251875Speter * 15251875Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16251875Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17251875Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18251875Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19251875Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20251875Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21251875Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22251875Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23251875Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24251875Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25251875Speter * 26251875Speter * $FreeBSD: head/lib/librt/sigev_thread.c 181778 2008-08-15 21:08:48Z kmacy $ 27251875Speter * 28251875Speter */ 29251875Speter 30251875Speter#include <sys/types.h> 31251875Speter#include <machine/atomic.h> 32251875Speter 33251875Speter#include "namespace.h" 34251875Speter#include <err.h> 35251875Speter#include <errno.h> 36251875Speter#include <ucontext.h> 37251875Speter#include <sys/thr.h> 38251875Speter#include <stdio.h> 39251875Speter#include <stdlib.h> 40251875Speter#include <string.h> 41251875Speter#include <signal.h> 42251875Speter#include <pthread.h> 43251875Speter#include "un-namespace.h" 44251875Speter 45251875Speter#include "sigev_thread.h" 46251875Speter 47251875SpeterLIST_HEAD(sigev_list_head, sigev_node); 48251875Speter#define HASH_QUEUES 17 49251875Speter#define HASH(t, id) ((((id) << 3) + (t)) % HASH_QUEUES) 50251875Speter 51251875Speterstatic struct sigev_list_head sigev_hash[HASH_QUEUES]; 52251875Speterstatic struct sigev_list_head sigev_all; 53251875Speterstatic LIST_HEAD(,sigev_thread) sigev_threads; 54251875Speterstatic unsigned int sigev_generation; 55251875Speterstatic pthread_mutex_t *sigev_list_mtx; 56251875Speterstatic pthread_once_t sigev_once = PTHREAD_ONCE_INIT; 57251875Speterstatic pthread_once_t sigev_once_default = PTHREAD_ONCE_INIT; 58251875Speterstatic struct sigev_thread *sigev_default_thread; 59251875Speterstatic pthread_attr_t sigev_default_attr; 60251875Speterstatic int atfork_registered; 61251875Speter 62251875Speterstatic void __sigev_fork_prepare(void); 63251875Speterstatic void __sigev_fork_parent(void); 64251875Speterstatic void __sigev_fork_child(void); 65251875Speterstatic struct sigev_thread *sigev_thread_create(int); 66251875Speterstatic void *sigev_service_loop(void *); 67251875Speterstatic void *worker_routine(void *); 68251875Speterstatic void worker_cleanup(void *); 69251875Speter 70251875Speter#pragma weak _pthread_create 71251875Speter 72251875Speterstatic void 73251875Speterattrcopy(pthread_attr_t *src, pthread_attr_t *dst) 74251875Speter{ 75251875Speter struct sched_param sched; 76251875Speter void *a; 77251875Speter size_t u; 78251875Speter int v; 79251875Speter 80251875Speter _pthread_attr_getschedpolicy(src, &v); 81251875Speter _pthread_attr_setschedpolicy(dst, v); 82251875Speter 83251875Speter _pthread_attr_getinheritsched(src, &v); 84251875Speter _pthread_attr_setinheritsched(dst, v); 85251875Speter 86251875Speter _pthread_attr_getschedparam(src, &sched); 87251875Speter _pthread_attr_setschedparam(dst, &sched); 88251875Speter 89251875Speter _pthread_attr_getscope(src, &v); 90251875Speter _pthread_attr_setscope(dst, v); 91251875Speter 92251875Speter _pthread_attr_getstacksize(src, &u); 93251875Speter _pthread_attr_setstacksize(dst, u); 94251875Speter 95251875Speter _pthread_attr_getstackaddr(src, &a); 96251875Speter _pthread_attr_setstackaddr(src, a); 97251875Speter 98251875Speter _pthread_attr_getguardsize(src, &u); 99251875Speter _pthread_attr_setguardsize(dst, u); 100251875Speter} 101251875Speter 102251875Speterstatic __inline int 103251875Speterhave_threads(void) 104251875Speter{ 105251875Speter return (&_pthread_create != NULL); 106251875Speter} 107251875Speter 108251875Spetervoid 109251875Speter__sigev_thread_init(void) 110251875Speter{ 111251875Speter static int inited = 0; 112251875Speter pthread_mutexattr_t mattr; 113251875Speter int i; 114251875Speter 115251875Speter _pthread_mutexattr_init(&mattr); 116251875Speter _pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL); 117251875Speter sigev_list_mtx = malloc(sizeof(pthread_mutex_t)); 118251875Speter _pthread_mutex_init(sigev_list_mtx, &mattr); 119251875Speter _pthread_mutexattr_destroy(&mattr); 120251875Speter 121251875Speter for (i = 0; i < HASH_QUEUES; ++i) 122251875Speter LIST_INIT(&sigev_hash[i]); 123251875Speter LIST_INIT(&sigev_all); 124251875Speter LIST_INIT(&sigev_threads); 125251875Speter sigev_default_thread = NULL; 126251875Speter if (atfork_registered == 0) { 127251875Speter _pthread_atfork( 128251875Speter __sigev_fork_prepare, 129251875Speter __sigev_fork_parent, 130251875Speter __sigev_fork_child); 131251875Speter atfork_registered = 1; 132251875Speter } 133251875Speter if (!inited) { 134251875Speter _pthread_attr_init(&sigev_default_attr); 135251875Speter _pthread_attr_setscope(&sigev_default_attr, 136251875Speter PTHREAD_SCOPE_SYSTEM); 137251875Speter _pthread_attr_setdetachstate(&sigev_default_attr, 138251875Speter PTHREAD_CREATE_DETACHED); 139251875Speter inited = 1; 140251875Speter } 141251875Speter sigev_default_thread = sigev_thread_create(0); 142251875Speter} 143251875Speter 144251875Speterint 145251875Speter__sigev_check_init(void) 146251875Speter{ 147251875Speter if (!have_threads()) 148251875Speter return (-1); 149251875Speter 150251875Speter _pthread_once(&sigev_once, __sigev_thread_init); 151251875Speter return (sigev_default_thread != NULL) ? 0 : -1; 152251875Speter} 153251875Speter 154251875Speterstatic void 155251875Speter__sigev_fork_prepare(void) 156251875Speter{ 157251875Speter} 158251875Speter 159251875Speterstatic void 160251875Speter__sigev_fork_parent(void) 161251875Speter{ 162251875Speter} 163251875Speter 164251875Speterstatic void 165251875Speter__sigev_fork_child(void) 166251875Speter{ 167251875Speter /* 168251875Speter * This is a hack, the thread libraries really should 169251875Speter * check if the handlers were already registered in 170251875Speter * pthread_atfork(). 171251875Speter */ 172251875Speter atfork_registered = 1; 173251875Speter memcpy(&sigev_once, &sigev_once_default, sizeof(sigev_once)); 174251875Speter __sigev_thread_init(); 175251875Speter} 176251875Speter 177251875Spetervoid 178251875Speter__sigev_list_lock(void) 179251875Speter{ 180251875Speter _pthread_mutex_lock(sigev_list_mtx); 181251875Speter} 182251875Speter 183251875Spetervoid 184251875Speter__sigev_list_unlock(void) 185251875Speter{ 186251875Speter _pthread_mutex_unlock(sigev_list_mtx); 187251875Speter} 188251875Speter 189251875Speterstruct sigev_node * 190251875Speter__sigev_alloc(int type, const struct sigevent *evp, struct sigev_node *prev, 191251875Speter int usedefault) 192251875Speter{ 193251875Speter struct sigev_node *sn; 194251875Speter 195251875Speter sn = calloc(1, sizeof(*sn)); 196251875Speter if (sn != NULL) { 197251875Speter sn->sn_value = evp->sigev_value; 198251875Speter sn->sn_func = evp->sigev_notify_function; 199251875Speter sn->sn_gen = atomic_fetchadd_int(&sigev_generation, 1); 200251875Speter sn->sn_type = type; 201251875Speter _pthread_attr_init(&sn->sn_attr); 202251875Speter _pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED); 203251875Speter if (evp->sigev_notify_attributes) 204251875Speter attrcopy(evp->sigev_notify_attributes, &sn->sn_attr); 205251875Speter if (prev) { 206251875Speter __sigev_list_lock(); 207251875Speter prev->sn_tn->tn_refcount++; 208251875Speter __sigev_list_unlock(); 209251875Speter sn->sn_tn = prev->sn_tn; 210251875Speter } else { 211251875Speter sn->sn_tn = sigev_thread_create(usedefault); 212251875Speter if (sn->sn_tn == NULL) { 213251875Speter _pthread_attr_destroy(&sn->sn_attr); 214251875Speter free(sn); 215251875Speter sn = NULL; 216251875Speter } 217251875Speter } 218251875Speter } 219251875Speter return (sn); 220251875Speter} 221251875Speter 222251875Spetervoid 223251875Speter__sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp, 224251875Speter sigev_id_t id) 225251875Speter{ 226251875Speter /* 227251875Speter * Build a new sigevent, and tell kernel to deliver SIGSERVICE 228251875Speter * signal to the new thread. 229251875Speter */ 230251875Speter newevp->sigev_notify = SIGEV_THREAD_ID; 231251875Speter newevp->sigev_signo = SIGSERVICE; 232251875Speter newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid; 233251875Speter newevp->sigev_value.sival_ptr = (void *)id; 234251875Speter} 235251875Speter 236251875Spetervoid 237251875Speter__sigev_free(struct sigev_node *sn) 238251875Speter{ 239251875Speter _pthread_attr_destroy(&sn->sn_attr); 240251875Speter free(sn); 241251875Speter} 242251875Speter 243251875Speterstruct sigev_node * 244251875Speter__sigev_find(int type, sigev_id_t id) 245251875Speter{ 246251875Speter struct sigev_node *sn; 247251875Speter int chain = HASH(type, id); 248251875Speter 249251875Speter LIST_FOREACH(sn, &sigev_hash[chain], sn_link) { 250251875Speter if (sn->sn_type == type && sn->sn_id == id) 251251875Speter break; 252251875Speter } 253251875Speter return (sn); 254251875Speter} 255251875Speter 256251875Speterint 257251875Speter__sigev_register(struct sigev_node *sn) 258251875Speter{ 259251875Speter int chain = HASH(sn->sn_type, sn->sn_id); 260251875Speter 261251875Speter LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link); 262251875Speter return (0); 263251875Speter} 264251875Speter 265251875Speterint 266251875Speter__sigev_delete(int type, sigev_id_t id) 267251875Speter{ 268251875Speter struct sigev_node *sn; 269251875Speter 270251875Speter sn = __sigev_find(type, id); 271251875Speter if (sn != NULL) 272251875Speter return (__sigev_delete_node(sn)); 273251875Speter return (0); 274251875Speter} 275251875Speter 276251875Speterint 277251875Speter__sigev_delete_node(struct sigev_node *sn) 278251875Speter{ 279251875Speter LIST_REMOVE(sn, sn_link); 280251875Speter 281251875Speter if (--sn->sn_tn->tn_refcount == 0) 282251875Speter _pthread_kill(sn->sn_tn->tn_thread, SIGSERVICE); 283251875Speter if (sn->sn_flags & SNF_WORKING) 284251875Speter sn->sn_flags |= SNF_REMOVED; 285251875Speter else 286251875Speter __sigev_free(sn); 287251875Speter return (0); 288251875Speter} 289251875Speter 290251875Speterstatic sigev_id_t 291251875Spetersigev_get_id(siginfo_t *si) 292251875Speter{ 293251875Speter switch(si->si_code) { 294251875Speter case SI_TIMER: 295251875Speter return (si->si_timerid); 296251875Speter case SI_MESGQ: 297251875Speter return (si->si_mqd); 298251875Speter case SI_ASYNCIO: 299251875Speter return (sigev_id_t)si->si_value.sival_ptr; 300251875Speter } 301251875Speter return (-1); 302251875Speter} 303251875Speter 304251875Speterstatic struct sigev_thread * 305251875Spetersigev_thread_create(int usedefault) 306251875Speter{ 307251875Speter struct sigev_thread *tn; 308251875Speter sigset_t set, oset; 309251875Speter int ret; 310251875Speter 311251875Speter if (usedefault && sigev_default_thread) { 312251875Speter __sigev_list_lock(); 313251875Speter sigev_default_thread->tn_refcount++; 314251875Speter __sigev_list_unlock(); 315251875Speter return (sigev_default_thread); 316251875Speter } 317251875Speter 318251875Speter tn = malloc(sizeof(*tn)); 319251875Speter tn->tn_cur = NULL; 320251875Speter tn->tn_lwpid = -1; 321251875Speter tn->tn_refcount = 1; 322251875Speter _pthread_cond_init(&tn->tn_cv, NULL); 323251875Speter 324251875Speter /* for debug */ 325251875Speter __sigev_list_lock(); 326251875Speter LIST_INSERT_HEAD(&sigev_threads, tn, tn_link); 327251875Speter __sigev_list_unlock(); 328251875Speter 329251875Speter sigfillset(&set); /* SIGSERVICE is masked. */ 330251875Speter sigdelset(&set, SIGBUS); 331251875Speter sigdelset(&set, SIGILL); 332251875Speter sigdelset(&set, SIGFPE); 333251875Speter sigdelset(&set, SIGSEGV); 334251875Speter sigdelset(&set, SIGTRAP); 335251875Speter _sigprocmask(SIG_SETMASK, &set, &oset); 336251875Speter ret = _pthread_create(&tn->tn_thread, &sigev_default_attr, 337251875Speter sigev_service_loop, tn); 338251875Speter _sigprocmask(SIG_SETMASK, &oset, NULL); 339251875Speter 340251875Speter if (ret != 0) { 341251875Speter __sigev_list_lock(); 342251875Speter LIST_REMOVE(tn, tn_link); 343251875Speter __sigev_list_unlock(); 344251875Speter free(tn); 345251875Speter tn = NULL; 346251875Speter } else { 347251875Speter /* wait the thread to get its lwpid */ 348251875Speter 349251875Speter __sigev_list_lock(); 350251875Speter while (tn->tn_lwpid == -1) 351251875Speter _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx); 352251875Speter __sigev_list_unlock(); 353251875Speter } 354251875Speter return (tn); 355251875Speter} 356251875Speter 357251875Speter/* 358251875Speter * The thread receives notification from kernel and creates 359251875Speter * a thread to call user callback function. 360251875Speter */ 361251875Speterstatic void * 362251875Spetersigev_service_loop(void *arg) 363251875Speter{ 364251875Speter static int failure; 365251875Speter 366251875Speter siginfo_t si; 367251875Speter sigset_t set; 368251875Speter struct sigev_thread *tn; 369251875Speter struct sigev_node *sn; 370251875Speter sigev_id_t id; 371251875Speter pthread_t td; 372251875Speter int ret; 373251875Speter 374251875Speter tn = arg; 375251875Speter thr_self(&tn->tn_lwpid); 376251875Speter __sigev_list_lock(); 377251875Speter _pthread_cond_broadcast(&tn->tn_cv); 378251875Speter __sigev_list_unlock(); 379251875Speter 380251875Speter sigemptyset(&set); 381251875Speter sigaddset(&set, SIGSERVICE); 382251875Speter for (;;) { 383251875Speter ret = sigwaitinfo(&set, &si); 384251875Speter 385251875Speter __sigev_list_lock(); 386251875Speter if (tn->tn_refcount == 0) { 387251875Speter LIST_REMOVE(tn, tn_link); 388251875Speter __sigev_list_unlock(); 389251875Speter free(tn); 390251875Speter break; 391251875Speter } 392251875Speter 393251875Speter if (ret == -1) { 394251875Speter __sigev_list_unlock(); 395251875Speter continue; 396251875Speter } 397251875Speter 398251875Speter id = sigev_get_id(&si); 399251875Speter sn = __sigev_find(si.si_code, id); 400251875Speter if (sn == NULL) { 401251875Speter __sigev_list_unlock(); 402251875Speter continue; 403251875Speter } 404251875Speter 405251875Speter sn->sn_info = si; 406251875Speter if (sn->sn_flags & SNF_SYNC) 407251875Speter tn->tn_cur = sn; 408251875Speter else 409251875Speter tn->tn_cur = NULL; 410251875Speter sn->sn_flags |= SNF_WORKING; 411251875Speter __sigev_list_unlock(); 412251875Speter 413251875Speter ret = _pthread_create(&td, &sn->sn_attr, worker_routine, sn); 414251875Speter if (ret != 0) { 415251875Speter if (failure++ < 5) 416251875Speter warnc(ret, "%s:%s failed to create thread.\n", 417251875Speter __FILE__, __func__); 418251875Speter 419251875Speter __sigev_list_lock(); 420251875Speter sn->sn_flags &= ~SNF_WORKING; 421251875Speter if (sn->sn_flags & SNF_REMOVED) 422251875Speter __sigev_free(sn); 423251875Speter __sigev_list_unlock(); 424251875Speter } else if (tn->tn_cur) { 425251875Speter __sigev_list_lock(); 426251875Speter while (tn->tn_cur) 427251875Speter _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx); 428251875Speter __sigev_list_unlock(); 429251875Speter } 430251875Speter } 431251875Speter return (0); 432251875Speter} 433251875Speter 434251875Speter/* 435251875Speter * newly created worker thread to call user callback function. 436251875Speter */ 437251875Speterstatic void * 438251875Speterworker_routine(void *arg) 439251875Speter{ 440251875Speter struct sigev_node *sn = arg; 441251875Speter 442251875Speter _pthread_cleanup_push(worker_cleanup, sn); 443251875Speter sn->sn_dispatch(sn); 444251875Speter _pthread_cleanup_pop(1); 445251875Speter 446251875Speter return (0); 447251875Speter} 448251875Speter 449251875Speter/* clean up a notification after dispatch. */ 450251875Speterstatic void 451251875Speterworker_cleanup(void *arg) 452251875Speter{ 453251875Speter struct sigev_node *sn = arg; 454251875Speter 455251875Speter __sigev_list_lock(); 456251875Speter if (sn->sn_flags & SNF_SYNC) { 457251875Speter sn->sn_tn->tn_cur = NULL; 458251875Speter _pthread_cond_broadcast(&sn->sn_tn->tn_cv); 459251875Speter } 460251875Speter if (sn->sn_flags & SNF_REMOVED) 461251875Speter __sigev_free(sn); 462251875Speter else 463251875Speter sn->sn_flags &= ~SNF_WORKING; 464251875Speter __sigev_list_unlock(); 465251875Speter} 466251875Speter