sigev_thread.c revision 330897
1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2005 David Xu <davidxu@freebsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice unmodified, this list of conditions, and the following 12 * disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * $FreeBSD: stable/11/lib/librt/sigev_thread.c 330897 2018-03-14 03:19:51Z eadler $ 29 * 30 */ 31 32#include <sys/types.h> 33 34#include "namespace.h" 35#include <err.h> 36#include <errno.h> 37#include <ucontext.h> 38#include <sys/thr.h> 39#include <stdatomic.h> 40#include <stdio.h> 41#include <stdlib.h> 42#include <string.h> 43#include <signal.h> 44#include <pthread.h> 45#include "un-namespace.h" 46 47#include "sigev_thread.h" 48 49LIST_HEAD(sigev_list_head, sigev_node); 50#define HASH_QUEUES 17 51#define HASH(t, id) ((((id) << 3) + (t)) % HASH_QUEUES) 52 53static struct sigev_list_head sigev_hash[HASH_QUEUES]; 54static struct sigev_list_head sigev_all; 55static LIST_HEAD(,sigev_thread) sigev_threads; 56static atomic_int sigev_generation; 57static pthread_mutex_t *sigev_list_mtx; 58static pthread_once_t sigev_once = PTHREAD_ONCE_INIT; 59static pthread_once_t sigev_once_default = PTHREAD_ONCE_INIT; 60static struct sigev_thread *sigev_default_thread; 61static pthread_attr_t sigev_default_attr; 62static int atfork_registered; 63 64static void __sigev_fork_prepare(void); 65static void __sigev_fork_parent(void); 66static void __sigev_fork_child(void); 67static struct sigev_thread *sigev_thread_create(int); 68static void *sigev_service_loop(void *); 69static void *worker_routine(void *); 70static void worker_cleanup(void *); 71 72#pragma weak _pthread_create 73 74static void 75attrcopy(pthread_attr_t *src, pthread_attr_t *dst) 76{ 77 struct sched_param sched; 78 void *a; 79 size_t u; 80 int v; 81 82 _pthread_attr_getschedpolicy(src, &v); 83 _pthread_attr_setschedpolicy(dst, v); 84 85 _pthread_attr_getinheritsched(src, &v); 86 _pthread_attr_setinheritsched(dst, v); 87 88 _pthread_attr_getschedparam(src, &sched); 89 _pthread_attr_setschedparam(dst, &sched); 90 91 _pthread_attr_getscope(src, &v); 92 _pthread_attr_setscope(dst, v); 93 94 _pthread_attr_getstacksize(src, &u); 95 _pthread_attr_setstacksize(dst, u); 96 97 _pthread_attr_getstackaddr(src, &a); 98 _pthread_attr_setstackaddr(src, a); 99 100 _pthread_attr_getguardsize(src, &u); 101 _pthread_attr_setguardsize(dst, u); 102} 103 104static __inline int 105have_threads(void) 106{ 107 return (&_pthread_create != NULL); 108} 109 110void 111__sigev_thread_init(void) 112{ 113 static int inited = 0; 114 pthread_mutexattr_t mattr; 115 int i; 116 117 _pthread_mutexattr_init(&mattr); 118 _pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL); 119 sigev_list_mtx = malloc(sizeof(pthread_mutex_t)); 120 _pthread_mutex_init(sigev_list_mtx, &mattr); 121 _pthread_mutexattr_destroy(&mattr); 122 123 for (i = 0; i < HASH_QUEUES; ++i) 124 LIST_INIT(&sigev_hash[i]); 125 LIST_INIT(&sigev_all); 126 LIST_INIT(&sigev_threads); 127 sigev_default_thread = NULL; 128 if (atfork_registered == 0) { 129 _pthread_atfork( 130 __sigev_fork_prepare, 131 __sigev_fork_parent, 132 __sigev_fork_child); 133 atfork_registered = 1; 134 } 135 if (!inited) { 136 _pthread_attr_init(&sigev_default_attr); 137 _pthread_attr_setscope(&sigev_default_attr, 138 PTHREAD_SCOPE_SYSTEM); 139 _pthread_attr_setdetachstate(&sigev_default_attr, 140 PTHREAD_CREATE_DETACHED); 141 inited = 1; 142 } 143 sigev_default_thread = sigev_thread_create(0); 144} 145 146int 147__sigev_check_init(void) 148{ 149 if (!have_threads()) 150 return (-1); 151 152 _pthread_once(&sigev_once, __sigev_thread_init); 153 return (sigev_default_thread != NULL) ? 0 : -1; 154} 155 156static void 157__sigev_fork_prepare(void) 158{ 159} 160 161static void 162__sigev_fork_parent(void) 163{ 164} 165 166static void 167__sigev_fork_child(void) 168{ 169 /* 170 * This is a hack, the thread libraries really should 171 * check if the handlers were already registered in 172 * pthread_atfork(). 173 */ 174 atfork_registered = 1; 175 memcpy(&sigev_once, &sigev_once_default, sizeof(sigev_once)); 176 __sigev_thread_init(); 177} 178 179void 180__sigev_list_lock(void) 181{ 182 _pthread_mutex_lock(sigev_list_mtx); 183} 184 185void 186__sigev_list_unlock(void) 187{ 188 _pthread_mutex_unlock(sigev_list_mtx); 189} 190 191struct sigev_node * 192__sigev_alloc(int type, const struct sigevent *evp, struct sigev_node *prev, 193 int usedefault) 194{ 195 struct sigev_node *sn; 196 197 sn = calloc(1, sizeof(*sn)); 198 if (sn != NULL) { 199 sn->sn_value = evp->sigev_value; 200 sn->sn_func = evp->sigev_notify_function; 201 sn->sn_gen = atomic_fetch_add_explicit(&sigev_generation, 1, 202 memory_order_relaxed); 203 sn->sn_type = type; 204 _pthread_attr_init(&sn->sn_attr); 205 _pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED); 206 if (evp->sigev_notify_attributes) 207 attrcopy(evp->sigev_notify_attributes, &sn->sn_attr); 208 if (prev) { 209 __sigev_list_lock(); 210 prev->sn_tn->tn_refcount++; 211 __sigev_list_unlock(); 212 sn->sn_tn = prev->sn_tn; 213 } else { 214 sn->sn_tn = sigev_thread_create(usedefault); 215 if (sn->sn_tn == NULL) { 216 _pthread_attr_destroy(&sn->sn_attr); 217 free(sn); 218 sn = NULL; 219 } 220 } 221 } 222 return (sn); 223} 224 225void 226__sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp, 227 sigev_id_t id) 228{ 229 /* 230 * Build a new sigevent, and tell kernel to deliver SIGLIBRT 231 * signal to the new thread. 232 */ 233 newevp->sigev_notify = SIGEV_THREAD_ID; 234 newevp->sigev_signo = SIGLIBRT; 235 newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid; 236 newevp->sigev_value.sival_ptr = (void *)id; 237} 238 239void 240__sigev_free(struct sigev_node *sn) 241{ 242 _pthread_attr_destroy(&sn->sn_attr); 243 free(sn); 244} 245 246struct sigev_node * 247__sigev_find(int type, sigev_id_t id) 248{ 249 struct sigev_node *sn; 250 int chain = HASH(type, id); 251 252 LIST_FOREACH(sn, &sigev_hash[chain], sn_link) { 253 if (sn->sn_type == type && sn->sn_id == id) 254 break; 255 } 256 return (sn); 257} 258 259int 260__sigev_register(struct sigev_node *sn) 261{ 262 int chain = HASH(sn->sn_type, sn->sn_id); 263 264 LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link); 265 return (0); 266} 267 268int 269__sigev_delete(int type, sigev_id_t id) 270{ 271 struct sigev_node *sn; 272 273 sn = __sigev_find(type, id); 274 if (sn != NULL) 275 return (__sigev_delete_node(sn)); 276 return (0); 277} 278 279int 280__sigev_delete_node(struct sigev_node *sn) 281{ 282 LIST_REMOVE(sn, sn_link); 283 284 if (--sn->sn_tn->tn_refcount == 0) 285 _pthread_kill(sn->sn_tn->tn_thread, SIGLIBRT); 286 if (sn->sn_flags & SNF_WORKING) 287 sn->sn_flags |= SNF_REMOVED; 288 else 289 __sigev_free(sn); 290 return (0); 291} 292 293static sigev_id_t 294sigev_get_id(siginfo_t *si) 295{ 296 switch(si->si_code) { 297 case SI_TIMER: 298 return (si->si_timerid); 299 case SI_MESGQ: 300 return (si->si_mqd); 301 case SI_ASYNCIO: 302 return (sigev_id_t)si->si_value.sival_ptr; 303 } 304 return (-1); 305} 306 307static struct sigev_thread * 308sigev_thread_create(int usedefault) 309{ 310 struct sigev_thread *tn; 311 sigset_t set, oset; 312 int ret; 313 314 if (usedefault && sigev_default_thread) { 315 __sigev_list_lock(); 316 sigev_default_thread->tn_refcount++; 317 __sigev_list_unlock(); 318 return (sigev_default_thread); 319 } 320 321 tn = malloc(sizeof(*tn)); 322 tn->tn_cur = NULL; 323 tn->tn_lwpid = -1; 324 tn->tn_refcount = 1; 325 _pthread_cond_init(&tn->tn_cv, NULL); 326 327 /* for debug */ 328 __sigev_list_lock(); 329 LIST_INSERT_HEAD(&sigev_threads, tn, tn_link); 330 __sigev_list_unlock(); 331 332 sigfillset(&set); /* SIGLIBRT is masked. */ 333 sigdelset(&set, SIGBUS); 334 sigdelset(&set, SIGILL); 335 sigdelset(&set, SIGFPE); 336 sigdelset(&set, SIGSEGV); 337 sigdelset(&set, SIGTRAP); 338 _sigprocmask(SIG_SETMASK, &set, &oset); 339 ret = _pthread_create(&tn->tn_thread, &sigev_default_attr, 340 sigev_service_loop, tn); 341 _sigprocmask(SIG_SETMASK, &oset, NULL); 342 343 if (ret != 0) { 344 __sigev_list_lock(); 345 LIST_REMOVE(tn, tn_link); 346 __sigev_list_unlock(); 347 free(tn); 348 tn = NULL; 349 } else { 350 /* wait the thread to get its lwpid */ 351 352 __sigev_list_lock(); 353 while (tn->tn_lwpid == -1) 354 _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx); 355 __sigev_list_unlock(); 356 } 357 return (tn); 358} 359 360/* 361 * The thread receives notification from kernel and creates 362 * a thread to call user callback function. 363 */ 364static void * 365sigev_service_loop(void *arg) 366{ 367 static int failure; 368 369 siginfo_t si; 370 sigset_t set; 371 struct sigev_thread *tn; 372 struct sigev_node *sn; 373 sigev_id_t id; 374 pthread_t td; 375 int ret; 376 377 tn = arg; 378 thr_self(&tn->tn_lwpid); 379 __sigev_list_lock(); 380 _pthread_cond_broadcast(&tn->tn_cv); 381 __sigev_list_unlock(); 382 383 sigemptyset(&set); 384 sigaddset(&set, SIGLIBRT); 385 for (;;) { 386 ret = sigwaitinfo(&set, &si); 387 388 __sigev_list_lock(); 389 if (tn->tn_refcount == 0) { 390 LIST_REMOVE(tn, tn_link); 391 __sigev_list_unlock(); 392 free(tn); 393 break; 394 } 395 396 if (ret == -1) { 397 __sigev_list_unlock(); 398 continue; 399 } 400 401 id = sigev_get_id(&si); 402 sn = __sigev_find(si.si_code, id); 403 if (sn == NULL) { 404 __sigev_list_unlock(); 405 continue; 406 } 407 408 sn->sn_info = si; 409 if (sn->sn_flags & SNF_SYNC) 410 tn->tn_cur = sn; 411 else 412 tn->tn_cur = NULL; 413 sn->sn_flags |= SNF_WORKING; 414 __sigev_list_unlock(); 415 416 ret = _pthread_create(&td, &sn->sn_attr, worker_routine, sn); 417 if (ret != 0) { 418 if (failure++ < 5) 419 warnc(ret, "%s:%s failed to create thread.\n", 420 __FILE__, __func__); 421 422 __sigev_list_lock(); 423 sn->sn_flags &= ~SNF_WORKING; 424 if (sn->sn_flags & SNF_REMOVED) 425 __sigev_free(sn); 426 __sigev_list_unlock(); 427 } else if (tn->tn_cur) { 428 __sigev_list_lock(); 429 while (tn->tn_cur) 430 _pthread_cond_wait(&tn->tn_cv, sigev_list_mtx); 431 __sigev_list_unlock(); 432 } 433 } 434 return (0); 435} 436 437/* 438 * newly created worker thread to call user callback function. 439 */ 440static void * 441worker_routine(void *arg) 442{ 443 struct sigev_node *sn = arg; 444 445 pthread_cleanup_push(worker_cleanup, sn); 446 sn->sn_dispatch(sn); 447 pthread_cleanup_pop(1); 448 449 return (0); 450} 451 452/* clean up a notification after dispatch. */ 453static void 454worker_cleanup(void *arg) 455{ 456 struct sigev_node *sn = arg; 457 458 __sigev_list_lock(); 459 if (sn->sn_flags & SNF_SYNC) { 460 sn->sn_tn->tn_cur = NULL; 461 _pthread_cond_broadcast(&sn->sn_tn->tn_cv); 462 } 463 if (sn->sn_flags & SNF_REMOVED) 464 __sigev_free(sn); 465 else 466 sn->sn_flags &= ~SNF_WORKING; 467 __sigev_list_unlock(); 468} 469