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$ 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 49 LIST_HEAD(sigev_list_head, sigev_node); 50 #define HASH_QUEUES 17 51 #define HASH(t, id) ((((id) << 3) + (t)) % HASH_QUEUES) 52 53 static struct sigev_list_head sigev_hash[HASH_QUEUES]; 54 static struct sigev_list_head sigev_all; 55 static LIST_HEAD(,sigev_thread) sigev_threads; 56 static atomic_int sigev_generation; 57 static pthread_mutex_t *sigev_list_mtx; 58 static pthread_once_t sigev_once = PTHREAD_ONCE_INIT; 59 static pthread_once_t sigev_once_default = PTHREAD_ONCE_INIT; 60 static struct sigev_thread *sigev_default_thread; 61 static pthread_attr_t sigev_default_attr; 62 static int atfork_registered; 63 64 static void __sigev_fork_prepare(void); 65 static void __sigev_fork_parent(void); 66 static void __sigev_fork_child(void); 67 static struct sigev_thread *sigev_thread_create(int); 68 static void *sigev_service_loop(void *); 69 static void *worker_routine(void *); 70 static void worker_cleanup(void *); 71 72 #pragma weak _pthread_create 73 74 static void 75 attrcopy(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 104 static __inline int 105 have_threads(void) 106 { 107 return (&_pthread_create != NULL); 108 } 109 110 void 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 146 int 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 156 static void 157 __sigev_fork_prepare(void) 158 { 159 } 160 161 static void 162 __sigev_fork_parent(void) 163 { 164 } 165 166 static 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 179 void 180 __sigev_list_lock(void) 181 { 182 _pthread_mutex_lock(sigev_list_mtx); 183 } 184 185 void 186 __sigev_list_unlock(void) 187 { 188 _pthread_mutex_unlock(sigev_list_mtx); 189 } 190 191 struct 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 225 void 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 239 void 240 __sigev_free(struct sigev_node *sn) 241 { 242 _pthread_attr_destroy(&sn->sn_attr); 243 free(sn); 244 } 245 246 struct 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 259 int 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 268 int 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 279 int 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 293 static sigev_id_t 294 sigev_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 307 static struct sigev_thread * 308 sigev_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 */ 364 static void * 365 sigev_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 */ 440 static void * 441 worker_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. */ 453 static void 454 worker_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