sigev_thread.c revision 156383
1156136Sdavidxu/* 2156136Sdavidxu * Copyright (c) 2005 David Xu <davidxu@freebsd.org> 3156136Sdavidxu * All rights reserved. 4156136Sdavidxu * 5156136Sdavidxu * Redistribution and use in source and binary forms, with or without 6156136Sdavidxu * modification, are permitted provided that the following conditions 7156136Sdavidxu * are met: 8156136Sdavidxu * 1. Redistributions of source code must retain the above copyright 9156136Sdavidxu * notice unmodified, this list of conditions, and the following 10156136Sdavidxu * disclaimer. 11156136Sdavidxu * 2. Redistributions in binary form must reproduce the above copyright 12156136Sdavidxu * notice, this list of conditions and the following disclaimer in the 13156136Sdavidxu * documentation and/or other materials provided with the distribution. 14156136Sdavidxu * 15156136Sdavidxu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16156136Sdavidxu * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17156136Sdavidxu * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18156136Sdavidxu * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19156136Sdavidxu * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20156136Sdavidxu * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21156136Sdavidxu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22156136Sdavidxu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23156136Sdavidxu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24156136Sdavidxu * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25156136Sdavidxu * 26156136Sdavidxu * $FreeBSD: head/lib/librt/sigev_thread.c 156383 2006-03-07 08:28:07Z davidxu $ 27156136Sdavidxu * 28156136Sdavidxu */ 29156136Sdavidxu 30156136Sdavidxu#include <sys/types.h> 31156136Sdavidxu#include <machine/atomic.h> 32156136Sdavidxu 33156136Sdavidxu#include "namespace.h" 34156136Sdavidxu#include <err.h> 35156267Sdavidxu#include <errno.h> 36156136Sdavidxu#include <ucontext.h> 37156136Sdavidxu#include <sys/thr.h> 38156136Sdavidxu#include <stdio.h> 39156136Sdavidxu#include <stdlib.h> 40156136Sdavidxu#include <string.h> 41156136Sdavidxu#include <signal.h> 42156136Sdavidxu#include <pthread.h> 43156136Sdavidxu#include "un-namespace.h" 44156136Sdavidxu 45156136Sdavidxu#include "sigev_thread.h" 46156136Sdavidxu 47156136SdavidxuLIST_HEAD(sigev_list_head, sigev_node); 48156136Sdavidxu#define HASH_QUEUES 17 49156136Sdavidxu#define HASH(t, id) ((((id) << 3) + (t)) % HASH_QUEUES) 50156267Sdavidxu 51156136Sdavidxustatic struct sigev_list_head sigev_hash[HASH_QUEUES]; 52156136Sdavidxustatic struct sigev_list_head sigev_all; 53156383Sdavidxustatic LIST_HEAD(,sigev_thread) sigev_threads; 54156136Sdavidxustatic int sigev_generation; 55156136Sdavidxustatic pthread_mutex_t *sigev_list_mtx; 56156267Sdavidxustatic pthread_once_t sigev_once = PTHREAD_ONCE_INIT; 57156267Sdavidxustatic pthread_once_t sigev_once_default = PTHREAD_ONCE_INIT; 58156383Sdavidxustatic struct sigev_thread *sigev_default_thread; 59156136Sdavidxustatic pthread_attr_t sigev_default_attr; 60156267Sdavidxustatic int atfork_registered; 61156136Sdavidxu 62156383Sdavidxustatic void __sigev_fork_prepare(void); 63156383Sdavidxustatic void __sigev_fork_parent(void); 64156383Sdavidxustatic void __sigev_fork_child(void); 65156383Sdavidxustatic struct sigev_thread *sigev_thread_create(int); 66156383Sdavidxustatic void *sigev_service_loop(void *); 67156383Sdavidxustatic void *worker_routine(void *); 68156383Sdavidxustatic void worker_cleanup(void *); 69156136Sdavidxu 70156136Sdavidxu#pragma weak pthread_create 71156136Sdavidxu 72156383Sdavidxustatic void 73156383Sdavidxuattrcopy(pthread_attr_t *src, pthread_attr_t *dst) 74156136Sdavidxu{ 75156383Sdavidxu struct sched_param sched; 76156383Sdavidxu void *a; 77156383Sdavidxu size_t u; 78156383Sdavidxu int v; 79156136Sdavidxu 80156383Sdavidxu _pthread_attr_getschedpolicy(src, &v); 81156383Sdavidxu _pthread_attr_setschedpolicy(dst, v); 82156136Sdavidxu 83156383Sdavidxu _pthread_attr_getinheritsched(src, &v); 84156383Sdavidxu _pthread_attr_setinheritsched(dst, v); 85156383Sdavidxu 86156383Sdavidxu _pthread_attr_getschedparam(src, &sched); 87156383Sdavidxu _pthread_attr_setschedparam(dst, &sched); 88156383Sdavidxu 89156383Sdavidxu _pthread_attr_getscope(src, &v); 90156383Sdavidxu _pthread_attr_setscope(dst, v); 91156383Sdavidxu 92156383Sdavidxu _pthread_attr_getstacksize(src, &u); 93156383Sdavidxu _pthread_attr_setstacksize(dst, u); 94156383Sdavidxu 95156383Sdavidxu _pthread_attr_getstackaddr(src, &a); 96156383Sdavidxu _pthread_attr_setstackaddr(src, a); 97156383Sdavidxu 98156383Sdavidxu _pthread_attr_getguardsize(src, &u); 99156383Sdavidxu _pthread_attr_setguardsize(dst, u); 100156136Sdavidxu} 101156136Sdavidxu 102156136Sdavidxustatic __inline int 103156136Sdavidxuhave_threads(void) 104156136Sdavidxu{ 105156267Sdavidxu return (&pthread_create != NULL); 106156136Sdavidxu} 107156136Sdavidxu 108156136Sdavidxuvoid 109156136Sdavidxu__sigev_thread_init(void) 110156136Sdavidxu{ 111156267Sdavidxu static int inited = 0; 112156383Sdavidxu pthread_mutexattr_t mattr; 113156136Sdavidxu int i; 114156136Sdavidxu 115156383Sdavidxu _pthread_mutexattr_init(&mattr); 116156383Sdavidxu _pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL); 117156136Sdavidxu sigev_list_mtx = malloc(sizeof(pthread_mutex_t)); 118156383Sdavidxu _pthread_mutex_init(sigev_list_mtx, &mattr); 119156383Sdavidxu _pthread_mutexattr_destroy(&mattr); 120156383Sdavidxu 121156136Sdavidxu for (i = 0; i < HASH_QUEUES; ++i) 122156136Sdavidxu LIST_INIT(&sigev_hash[i]); 123156136Sdavidxu LIST_INIT(&sigev_all); 124156383Sdavidxu LIST_INIT(&sigev_threads); 125156267Sdavidxu sigev_default_thread = NULL; 126156267Sdavidxu if (atfork_registered == 0) { 127156383Sdavidxu _pthread_atfork( 128156267Sdavidxu __sigev_fork_prepare, 129156267Sdavidxu __sigev_fork_parent, 130156267Sdavidxu __sigev_fork_child); 131156267Sdavidxu atfork_registered = 1; 132156267Sdavidxu } 133156267Sdavidxu if (!inited) { 134156383Sdavidxu _pthread_attr_init(&sigev_default_attr); 135156383Sdavidxu _pthread_attr_setscope(&sigev_default_attr, 136156267Sdavidxu PTHREAD_SCOPE_SYSTEM); 137156383Sdavidxu _pthread_attr_setdetachstate(&sigev_default_attr, 138156136Sdavidxu PTHREAD_CREATE_DETACHED); 139156267Sdavidxu inited = 1; 140156136Sdavidxu } 141156383Sdavidxu sigev_default_thread = sigev_thread_create(0); 142156136Sdavidxu} 143156136Sdavidxu 144156136Sdavidxuint 145156136Sdavidxu__sigev_check_init(void) 146156136Sdavidxu{ 147156136Sdavidxu if (!have_threads()) 148156136Sdavidxu return (-1); 149156136Sdavidxu 150156136Sdavidxu _pthread_once(&sigev_once, __sigev_thread_init); 151156267Sdavidxu return (sigev_default_thread != NULL) ? 0 : -1; 152156136Sdavidxu} 153156136Sdavidxu 154156267Sdavidxustatic void 155156136Sdavidxu__sigev_fork_prepare(void) 156156136Sdavidxu{ 157156136Sdavidxu} 158156136Sdavidxu 159156267Sdavidxustatic void 160156136Sdavidxu__sigev_fork_parent(void) 161156136Sdavidxu{ 162156136Sdavidxu} 163156136Sdavidxu 164156267Sdavidxustatic void 165156136Sdavidxu__sigev_fork_child(void) 166156136Sdavidxu{ 167156267Sdavidxu /* 168156267Sdavidxu * This is a hack, the thread libraries really should 169156267Sdavidxu * check if the handlers were already registered in 170156267Sdavidxu * pthread_atfork(). 171156267Sdavidxu */ 172156267Sdavidxu atfork_registered = 1; 173156267Sdavidxu memcpy(&sigev_once, &sigev_once_default, sizeof(sigev_once)); 174156136Sdavidxu __sigev_thread_init(); 175156136Sdavidxu} 176156136Sdavidxu 177156383Sdavidxuvoid 178156136Sdavidxu__sigev_list_lock(void) 179156136Sdavidxu{ 180156383Sdavidxu _pthread_mutex_lock(sigev_list_mtx); 181156136Sdavidxu} 182156136Sdavidxu 183156383Sdavidxuvoid 184156136Sdavidxu__sigev_list_unlock(void) 185156136Sdavidxu{ 186156383Sdavidxu _pthread_mutex_unlock(sigev_list_mtx); 187156136Sdavidxu} 188156136Sdavidxu 189156136Sdavidxustruct sigev_node * 190156267Sdavidxu__sigev_alloc(int type, const struct sigevent *evp, struct sigev_node *prev, 191156383Sdavidxu int usedefault) 192156136Sdavidxu{ 193156136Sdavidxu struct sigev_node *sn; 194156136Sdavidxu 195156136Sdavidxu sn = calloc(1, sizeof(*sn)); 196156136Sdavidxu if (sn != NULL) { 197156136Sdavidxu sn->sn_value = evp->sigev_value; 198156383Sdavidxu sn->sn_func = evp->sigev_notify_function; 199156383Sdavidxu sn->sn_gen = atomic_fetchadd_int(&sigev_generation, 1); 200156383Sdavidxu sn->sn_type = type; 201156383Sdavidxu _pthread_attr_init(&sn->sn_attr); 202156383Sdavidxu _pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED); 203156383Sdavidxu if (evp->sigev_notify_attributes) 204156383Sdavidxu attrcopy(evp->sigev_notify_attributes, &sn->sn_attr); 205156383Sdavidxu if (prev) { 206156383Sdavidxu __sigev_list_lock(); 207156383Sdavidxu prev->sn_tn->tn_refcount++; 208156383Sdavidxu __sigev_list_unlock(); 209156383Sdavidxu sn->sn_tn = prev->sn_tn; 210156383Sdavidxu } else { 211156383Sdavidxu sn->sn_tn = sigev_thread_create(usedefault); 212156383Sdavidxu if (sn->sn_tn == NULL) { 213156383Sdavidxu _pthread_attr_destroy(&sn->sn_attr); 214156383Sdavidxu free(sn); 215156383Sdavidxu sn = NULL; 216156383Sdavidxu } 217156136Sdavidxu } 218156136Sdavidxu } 219156136Sdavidxu return (sn); 220156136Sdavidxu} 221156136Sdavidxu 222156136Sdavidxuvoid 223156136Sdavidxu__sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp, 224156136Sdavidxu sigev_id_t id) 225156136Sdavidxu{ 226156136Sdavidxu /* 227156383Sdavidxu * Build a new sigevent, and tell kernel to deliver SIGSERVICE 228156136Sdavidxu * signal to the new thread. 229156136Sdavidxu */ 230156136Sdavidxu newevp->sigev_notify = SIGEV_THREAD_ID; 231156383Sdavidxu newevp->sigev_signo = SIGSERVICE; 232156136Sdavidxu newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid; 233156136Sdavidxu newevp->sigev_value.sival_ptr = (void *)id; 234156136Sdavidxu} 235156136Sdavidxu 236156136Sdavidxuvoid 237156136Sdavidxu__sigev_free(struct sigev_node *sn) 238156136Sdavidxu{ 239156383Sdavidxu _pthread_attr_destroy(&sn->sn_attr); 240156136Sdavidxu free(sn); 241156136Sdavidxu} 242156136Sdavidxu 243156136Sdavidxustruct sigev_node * 244156136Sdavidxu__sigev_find(int type, sigev_id_t id) 245156136Sdavidxu{ 246156136Sdavidxu struct sigev_node *sn; 247156136Sdavidxu int chain = HASH(type, id); 248156136Sdavidxu 249156136Sdavidxu LIST_FOREACH(sn, &sigev_hash[chain], sn_link) { 250156136Sdavidxu if (sn->sn_type == type && sn->sn_id == id) 251156136Sdavidxu break; 252156136Sdavidxu } 253156136Sdavidxu return (sn); 254156136Sdavidxu} 255156136Sdavidxu 256156136Sdavidxuint 257156136Sdavidxu__sigev_register(struct sigev_node *sn) 258156136Sdavidxu{ 259156136Sdavidxu int chain = HASH(sn->sn_type, sn->sn_id); 260156136Sdavidxu 261156136Sdavidxu LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link); 262156136Sdavidxu return (0); 263156136Sdavidxu} 264156136Sdavidxu 265156136Sdavidxuint 266156136Sdavidxu__sigev_delete(int type, sigev_id_t id) 267156136Sdavidxu{ 268156136Sdavidxu struct sigev_node *sn; 269156136Sdavidxu 270156136Sdavidxu sn = __sigev_find(type, id); 271156136Sdavidxu if (sn != NULL) 272156136Sdavidxu return (__sigev_delete_node(sn)); 273156136Sdavidxu return (0); 274156136Sdavidxu} 275156136Sdavidxu 276156136Sdavidxuint 277156136Sdavidxu__sigev_delete_node(struct sigev_node *sn) 278156136Sdavidxu{ 279156136Sdavidxu LIST_REMOVE(sn, sn_link); 280156136Sdavidxu 281156267Sdavidxu if (--sn->sn_tn->tn_refcount == 0) 282156383Sdavidxu _pthread_kill(sn->sn_tn->tn_thread, SIGSERVICE); 283156136Sdavidxu if (sn->sn_flags & SNF_WORKING) 284156136Sdavidxu sn->sn_flags |= SNF_REMOVED; 285156383Sdavidxu else 286156136Sdavidxu __sigev_free(sn); 287156136Sdavidxu return (0); 288156136Sdavidxu} 289156136Sdavidxu 290156383Sdavidxustatic sigev_id_t 291156136Sdavidxusigev_get_id(siginfo_t *si) 292156136Sdavidxu{ 293156136Sdavidxu switch(si->si_code) { 294156136Sdavidxu case SI_TIMER: 295156136Sdavidxu return (si->si_timerid); 296156136Sdavidxu case SI_MESGQ: 297156136Sdavidxu return (si->si_mqd); 298156136Sdavidxu case SI_ASYNCIO: 299156136Sdavidxu return (sigev_id_t)si->si_value.sival_ptr; 300156136Sdavidxu } 301156136Sdavidxu return (-1); 302156136Sdavidxu} 303156136Sdavidxu 304156383Sdavidxustatic struct sigev_thread * 305156383Sdavidxusigev_thread_create(int usedefault) 306156136Sdavidxu{ 307156383Sdavidxu struct sigev_thread *tn; 308156136Sdavidxu sigset_t set; 309156136Sdavidxu int ret; 310156136Sdavidxu 311156383Sdavidxu if (usedefault && sigev_default_thread) { 312156383Sdavidxu __sigev_list_lock(); 313156267Sdavidxu sigev_default_thread->tn_refcount++; 314156383Sdavidxu __sigev_list_unlock(); 315156383Sdavidxu return (sigev_default_thread); 316156267Sdavidxu } 317156267Sdavidxu 318156136Sdavidxu tn = malloc(sizeof(*tn)); 319156136Sdavidxu tn->tn_cur = NULL; 320156267Sdavidxu tn->tn_lwpid = -1; 321156267Sdavidxu tn->tn_refcount = 1; 322156383Sdavidxu _pthread_cond_init(&tn->tn_cv, NULL); 323156383Sdavidxu 324156383Sdavidxu /* for debug */ 325156383Sdavidxu __sigev_list_lock(); 326156383Sdavidxu LIST_INSERT_HEAD(&sigev_threads, tn, tn_link); 327156383Sdavidxu __sigev_list_unlock(); 328156383Sdavidxu 329156136Sdavidxu sigemptyset(&set); 330156383Sdavidxu sigaddset(&set, SIGSERVICE); 331156383Sdavidxu 332156136Sdavidxu _sigprocmask(SIG_BLOCK, &set, NULL); 333156383Sdavidxu ret = pthread_create(&tn->tn_thread, &sigev_default_attr, 334156383Sdavidxu sigev_service_loop, tn); 335156136Sdavidxu _sigprocmask(SIG_UNBLOCK, &set, NULL); 336156383Sdavidxu 337156136Sdavidxu if (ret != 0) { 338156383Sdavidxu __sigev_list_lock(); 339156383Sdavidxu LIST_REMOVE(tn, tn_link); 340156383Sdavidxu __sigev_list_unlock(); 341156136Sdavidxu free(tn); 342156136Sdavidxu tn = NULL; 343156136Sdavidxu } else { 344156136Sdavidxu /* wait the thread to get its lwpid */ 345156136Sdavidxu 346156136Sdavidxu __sigev_list_lock(); 347156383Sdavidxu while (tn->tn_lwpid == -1) 348156383Sdavidxu _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx); 349156136Sdavidxu __sigev_list_unlock(); 350156136Sdavidxu } 351156383Sdavidxu return (tn); 352156136Sdavidxu} 353156136Sdavidxu 354156136Sdavidxu/* 355156383Sdavidxu * The thread receives notification from kernel and creates 356156383Sdavidxu * a thread to call user callback function. 357156136Sdavidxu */ 358156136Sdavidxustatic void * 359156136Sdavidxusigev_service_loop(void *arg) 360156136Sdavidxu{ 361156383Sdavidxu static int failure; 362156383Sdavidxu 363156136Sdavidxu siginfo_t si; 364156136Sdavidxu sigset_t set; 365156383Sdavidxu struct sigev_thread *tn; 366156136Sdavidxu struct sigev_node *sn; 367156136Sdavidxu sigev_id_t id; 368156383Sdavidxu pthread_t td; 369156267Sdavidxu int ret; 370156136Sdavidxu 371156136Sdavidxu tn = arg; 372156136Sdavidxu thr_self(&tn->tn_lwpid); 373156383Sdavidxu __sigev_list_lock(); 374156383Sdavidxu _pthread_cond_broadcast(&tn->tn_cv); 375156383Sdavidxu __sigev_list_unlock(); 376156136Sdavidxu 377156136Sdavidxu /* 378156136Sdavidxu * Service thread should not be killed by callback, if user 379156136Sdavidxu * attempts to do so, the thread will be restarted. 380156136Sdavidxu */ 381156136Sdavidxu sigemptyset(&set); 382156383Sdavidxu sigaddset(&set, SIGSERVICE); 383156136Sdavidxu for (;;) { 384156267Sdavidxu ret = sigwaitinfo(&set, &si); 385156383Sdavidxu 386156383Sdavidxu __sigev_list_lock(); 387156267Sdavidxu if (tn->tn_refcount == 0) { 388156383Sdavidxu LIST_REMOVE(tn, tn_link); 389156383Sdavidxu __sigev_list_unlock(); 390156267Sdavidxu free(tn); 391156267Sdavidxu break; 392156267Sdavidxu } 393156383Sdavidxu 394156383Sdavidxu if (ret == -1) { 395156383Sdavidxu __sigev_list_unlock(); 396156136Sdavidxu continue; 397156383Sdavidxu } 398156383Sdavidxu 399156136Sdavidxu id = sigev_get_id(&si); 400156136Sdavidxu sn = __sigev_find(si.si_code, id); 401156383Sdavidxu if (sn == NULL) { 402156192Sdavidxu __sigev_list_unlock(); 403156383Sdavidxu continue; 404156192Sdavidxu } 405156383Sdavidxu 406156383Sdavidxu sn->sn_info = si; 407156383Sdavidxu if (sn->sn_flags & SNF_SYNC) 408156383Sdavidxu tn->tn_cur = sn; 409156383Sdavidxu else 410156383Sdavidxu tn->tn_cur = NULL; 411156383Sdavidxu sn->sn_flags |= SNF_WORKING; 412156383Sdavidxu __sigev_list_unlock(); 413156267Sdavidxu 414156383Sdavidxu ret = pthread_create(&td, &sn->sn_attr, worker_routine, sn); 415156383Sdavidxu if (ret != 0) { 416156383Sdavidxu if (failure++ < 5) 417156383Sdavidxu warnc(ret, "%s:%s failed to create thread.\n", 418156383Sdavidxu __FILE__, __func__); 419156267Sdavidxu 420156267Sdavidxu __sigev_list_lock(); 421156383Sdavidxu sn->sn_flags &= ~SNF_WORKING; 422156383Sdavidxu if (sn->sn_flags & SNF_REMOVED) 423156383Sdavidxu __sigev_free(sn); 424156267Sdavidxu __sigev_list_unlock(); 425156383Sdavidxu } else if (tn->tn_cur) { 426156267Sdavidxu __sigev_list_lock(); 427156383Sdavidxu while (tn->tn_cur) 428156383Sdavidxu _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx); 429156267Sdavidxu __sigev_list_unlock(); 430156267Sdavidxu } 431156267Sdavidxu } 432156383Sdavidxu return (0); 433156267Sdavidxu} 434156267Sdavidxu 435156267Sdavidxu/* 436156383Sdavidxu * newly created worker thread to call user callback function. 437156267Sdavidxu */ 438156267Sdavidxustatic void * 439156383Sdavidxuworker_routine(void *arg) 440156267Sdavidxu{ 441156383Sdavidxu struct sigev_node *sn = arg; 442156267Sdavidxu 443156383Sdavidxu _pthread_cleanup_push(worker_cleanup, sn); 444156383Sdavidxu sn->sn_dispatch(sn); 445156383Sdavidxu _pthread_cleanup_pop(1); 446156267Sdavidxu 447156267Sdavidxu return (0); 448156267Sdavidxu} 449156267Sdavidxu 450156383Sdavidxu/* clean up a notification after dispatch. */ 451156267Sdavidxustatic void 452156267Sdavidxuworker_cleanup(void *arg) 453156267Sdavidxu{ 454156383Sdavidxu struct sigev_node *sn = arg; 455156267Sdavidxu 456156267Sdavidxu __sigev_list_lock(); 457156383Sdavidxu if (sn->sn_flags & SNF_SYNC) { 458156383Sdavidxu sn->sn_tn->tn_cur = NULL; 459156383Sdavidxu _pthread_cond_broadcast(&sn->sn_tn->tn_cv); 460156383Sdavidxu } 461156267Sdavidxu if (sn->sn_flags & SNF_REMOVED) 462156267Sdavidxu __sigev_free(sn); 463156383Sdavidxu else 464156383Sdavidxu sn->sn_flags &= ~SNF_WORKING; 465156267Sdavidxu __sigev_list_unlock(); 466156267Sdavidxu} 467