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