]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/librt/sigev_thread.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / lib / librt / sigev_thread.c
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 }