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: releng/11.0/lib/librt/sigev_thread.c 252412 2013-06-30 08:59:33Z ed $ 27156136Sdavidxu * 28156136Sdavidxu */ 29156136Sdavidxu 30156136Sdavidxu#include <sys/types.h> 31156136Sdavidxu 32156136Sdavidxu#include "namespace.h" 33156136Sdavidxu#include <err.h> 34156267Sdavidxu#include <errno.h> 35156136Sdavidxu#include <ucontext.h> 36156136Sdavidxu#include <sys/thr.h> 37252412Sed#include <stdatomic.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; 54252412Sedstatic atomic_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 70157242Sdeischen#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{ 105157242Sdeischen 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; 199252412Sed sn->sn_gen = atomic_fetch_add_explicit(&sigev_generation, 1, 200252412Sed memory_order_relaxed); 201156383Sdavidxu sn->sn_type = type; 202156383Sdavidxu _pthread_attr_init(&sn->sn_attr); 203156383Sdavidxu _pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED); 204156383Sdavidxu if (evp->sigev_notify_attributes) 205156383Sdavidxu attrcopy(evp->sigev_notify_attributes, &sn->sn_attr); 206156383Sdavidxu if (prev) { 207156383Sdavidxu __sigev_list_lock(); 208156383Sdavidxu prev->sn_tn->tn_refcount++; 209156383Sdavidxu __sigev_list_unlock(); 210156383Sdavidxu sn->sn_tn = prev->sn_tn; 211156383Sdavidxu } else { 212156383Sdavidxu sn->sn_tn = sigev_thread_create(usedefault); 213156383Sdavidxu if (sn->sn_tn == NULL) { 214156383Sdavidxu _pthread_attr_destroy(&sn->sn_attr); 215156383Sdavidxu free(sn); 216156383Sdavidxu sn = NULL; 217156383Sdavidxu } 218156136Sdavidxu } 219156136Sdavidxu } 220156136Sdavidxu return (sn); 221156136Sdavidxu} 222156136Sdavidxu 223156136Sdavidxuvoid 224156136Sdavidxu__sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp, 225156136Sdavidxu sigev_id_t id) 226156136Sdavidxu{ 227156136Sdavidxu /* 228233519Srmh * Build a new sigevent, and tell kernel to deliver SIGLIBRT 229156136Sdavidxu * signal to the new thread. 230156136Sdavidxu */ 231156136Sdavidxu newevp->sigev_notify = SIGEV_THREAD_ID; 232233519Srmh newevp->sigev_signo = SIGLIBRT; 233156136Sdavidxu newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid; 234156136Sdavidxu newevp->sigev_value.sival_ptr = (void *)id; 235156136Sdavidxu} 236156136Sdavidxu 237156136Sdavidxuvoid 238156136Sdavidxu__sigev_free(struct sigev_node *sn) 239156136Sdavidxu{ 240156383Sdavidxu _pthread_attr_destroy(&sn->sn_attr); 241156136Sdavidxu free(sn); 242156136Sdavidxu} 243156136Sdavidxu 244156136Sdavidxustruct sigev_node * 245156136Sdavidxu__sigev_find(int type, sigev_id_t id) 246156136Sdavidxu{ 247156136Sdavidxu struct sigev_node *sn; 248156136Sdavidxu int chain = HASH(type, id); 249156136Sdavidxu 250156136Sdavidxu LIST_FOREACH(sn, &sigev_hash[chain], sn_link) { 251156136Sdavidxu if (sn->sn_type == type && sn->sn_id == id) 252156136Sdavidxu break; 253156136Sdavidxu } 254156136Sdavidxu return (sn); 255156136Sdavidxu} 256156136Sdavidxu 257156136Sdavidxuint 258156136Sdavidxu__sigev_register(struct sigev_node *sn) 259156136Sdavidxu{ 260156136Sdavidxu int chain = HASH(sn->sn_type, sn->sn_id); 261156136Sdavidxu 262156136Sdavidxu LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link); 263156136Sdavidxu return (0); 264156136Sdavidxu} 265156136Sdavidxu 266156136Sdavidxuint 267156136Sdavidxu__sigev_delete(int type, sigev_id_t id) 268156136Sdavidxu{ 269156136Sdavidxu struct sigev_node *sn; 270156136Sdavidxu 271156136Sdavidxu sn = __sigev_find(type, id); 272156136Sdavidxu if (sn != NULL) 273156136Sdavidxu return (__sigev_delete_node(sn)); 274156136Sdavidxu return (0); 275156136Sdavidxu} 276156136Sdavidxu 277156136Sdavidxuint 278156136Sdavidxu__sigev_delete_node(struct sigev_node *sn) 279156136Sdavidxu{ 280156136Sdavidxu LIST_REMOVE(sn, sn_link); 281156136Sdavidxu 282156267Sdavidxu if (--sn->sn_tn->tn_refcount == 0) 283233519Srmh _pthread_kill(sn->sn_tn->tn_thread, SIGLIBRT); 284156136Sdavidxu if (sn->sn_flags & SNF_WORKING) 285156136Sdavidxu sn->sn_flags |= SNF_REMOVED; 286156383Sdavidxu else 287156136Sdavidxu __sigev_free(sn); 288156136Sdavidxu return (0); 289156136Sdavidxu} 290156136Sdavidxu 291156383Sdavidxustatic sigev_id_t 292156136Sdavidxusigev_get_id(siginfo_t *si) 293156136Sdavidxu{ 294156136Sdavidxu switch(si->si_code) { 295156136Sdavidxu case SI_TIMER: 296156136Sdavidxu return (si->si_timerid); 297156136Sdavidxu case SI_MESGQ: 298156136Sdavidxu return (si->si_mqd); 299156136Sdavidxu case SI_ASYNCIO: 300156136Sdavidxu return (sigev_id_t)si->si_value.sival_ptr; 301156136Sdavidxu } 302156136Sdavidxu return (-1); 303156136Sdavidxu} 304156136Sdavidxu 305156383Sdavidxustatic struct sigev_thread * 306156383Sdavidxusigev_thread_create(int usedefault) 307156136Sdavidxu{ 308156383Sdavidxu struct sigev_thread *tn; 309156529Sdavidxu sigset_t set, oset; 310156136Sdavidxu int ret; 311156136Sdavidxu 312156383Sdavidxu if (usedefault && sigev_default_thread) { 313156383Sdavidxu __sigev_list_lock(); 314156267Sdavidxu sigev_default_thread->tn_refcount++; 315156383Sdavidxu __sigev_list_unlock(); 316156383Sdavidxu return (sigev_default_thread); 317156267Sdavidxu } 318156267Sdavidxu 319156136Sdavidxu tn = malloc(sizeof(*tn)); 320156136Sdavidxu tn->tn_cur = NULL; 321156267Sdavidxu tn->tn_lwpid = -1; 322156267Sdavidxu tn->tn_refcount = 1; 323156383Sdavidxu _pthread_cond_init(&tn->tn_cv, NULL); 324156383Sdavidxu 325156383Sdavidxu /* for debug */ 326156383Sdavidxu __sigev_list_lock(); 327156383Sdavidxu LIST_INSERT_HEAD(&sigev_threads, tn, tn_link); 328156383Sdavidxu __sigev_list_unlock(); 329156383Sdavidxu 330233519Srmh sigfillset(&set); /* SIGLIBRT is masked. */ 331156529Sdavidxu sigdelset(&set, SIGBUS); 332156529Sdavidxu sigdelset(&set, SIGILL); 333156529Sdavidxu sigdelset(&set, SIGFPE); 334156529Sdavidxu sigdelset(&set, SIGSEGV); 335156529Sdavidxu sigdelset(&set, SIGTRAP); 336156529Sdavidxu _sigprocmask(SIG_SETMASK, &set, &oset); 337157242Sdeischen ret = _pthread_create(&tn->tn_thread, &sigev_default_attr, 338156383Sdavidxu sigev_service_loop, tn); 339156529Sdavidxu _sigprocmask(SIG_SETMASK, &oset, NULL); 340156383Sdavidxu 341156136Sdavidxu if (ret != 0) { 342156383Sdavidxu __sigev_list_lock(); 343156383Sdavidxu LIST_REMOVE(tn, tn_link); 344156383Sdavidxu __sigev_list_unlock(); 345156136Sdavidxu free(tn); 346156136Sdavidxu tn = NULL; 347156136Sdavidxu } else { 348156136Sdavidxu /* wait the thread to get its lwpid */ 349156136Sdavidxu 350156136Sdavidxu __sigev_list_lock(); 351156383Sdavidxu while (tn->tn_lwpid == -1) 352156383Sdavidxu _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx); 353156136Sdavidxu __sigev_list_unlock(); 354156136Sdavidxu } 355156383Sdavidxu return (tn); 356156136Sdavidxu} 357156136Sdavidxu 358156136Sdavidxu/* 359156383Sdavidxu * The thread receives notification from kernel and creates 360156383Sdavidxu * a thread to call user callback function. 361156136Sdavidxu */ 362156136Sdavidxustatic void * 363156136Sdavidxusigev_service_loop(void *arg) 364156136Sdavidxu{ 365156383Sdavidxu static int failure; 366156383Sdavidxu 367156136Sdavidxu siginfo_t si; 368156136Sdavidxu sigset_t set; 369156383Sdavidxu struct sigev_thread *tn; 370156136Sdavidxu struct sigev_node *sn; 371156136Sdavidxu sigev_id_t id; 372156383Sdavidxu pthread_t td; 373156267Sdavidxu int ret; 374156136Sdavidxu 375156136Sdavidxu tn = arg; 376156136Sdavidxu thr_self(&tn->tn_lwpid); 377156383Sdavidxu __sigev_list_lock(); 378156383Sdavidxu _pthread_cond_broadcast(&tn->tn_cv); 379156383Sdavidxu __sigev_list_unlock(); 380156136Sdavidxu 381156136Sdavidxu sigemptyset(&set); 382233519Srmh sigaddset(&set, SIGLIBRT); 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 414157242Sdeischen 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 443199465Sdavidxu pthread_cleanup_push(worker_cleanup, sn); 444156383Sdavidxu sn->sn_dispatch(sn); 445199465Sdavidxu 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