]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/librt/sigev_thread.c
config: Fix a few mandoc related errors
[FreeBSD/FreeBSD.git] / lib / librt / sigev_thread.c
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 }