]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/librt/sigev_thread.c
Forgot to revert to use weak symbols when I was debugging, fix it!
[FreeBSD/FreeBSD.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 #include <machine/atomic.h>
32
33 #include "namespace.h"
34 #include <err.h>
35 #include <ucontext.h>
36 #include <sys/thr.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <signal.h>
41 #include <pthread.h>
42 #include "un-namespace.h"
43
44 #include "sigev_thread.h"
45
46 LIST_HEAD(sigev_list_head, sigev_node);
47 #define HASH_QUEUES             17
48 #define HASH(t, id)             ((((id) << 3) + (t)) % HASH_QUEUES)
49 static struct sigev_list_head   sigev_hash[HASH_QUEUES];
50 static struct sigev_list_head   sigev_all;
51 static int                      sigev_generation;
52 static pthread_mutex_t          *sigev_list_mtx;
53 static TAILQ_HEAD(,sigev_thread_node)   sigev_threads;
54 static pthread_mutex_t          *sigev_threads_mtx;
55 static pthread_attr_t           sigev_default_attr;
56 static pthread_once_t           sigev_once = PTHREAD_ONCE_INIT;
57
58 static void __sigev_fork_prepare(void);
59 static void __sigev_fork_parent(void);
60 static void __sigev_fork_child(void);
61 static struct sigev_thread_node *sigev_thread_create(pthread_attr_t *);
62 static void *sigev_service_loop(void *);
63
64 #pragma weak pthread_create
65 #pragma weak pthread_attr_getschedpolicy
66 #pragma weak pthread_attr_getinheritsched
67 #pragma weak pthread_attr_getschedparam
68 #pragma weak pthread_attr_getscope
69 #pragma weak pthread_attr_getstacksize
70 #pragma weak pthread_attr_getstackaddr
71 #pragma weak pthread_attr_getguardsize
72 #pragma weak pthread_attr_init
73 #pragma weak pthread_attr_setscope
74 #pragma weak pthread_attr_setdetachstate
75 #pragma weak pthread_atfork
76 #pragma weak _pthread_once
77 #pragma weak pthread_cleanup_push
78 #pragma weak pthread_cleanup_pop
79 #pragma weak pthread_setcancelstate
80
81 static __inline void
82 attr2sna(pthread_attr_t *attr, struct sigev_thread_attr *sna)
83 {
84         struct sched_param sched_param;
85
86         pthread_attr_getschedpolicy(attr, &sna->sna_policy);
87         pthread_attr_getinheritsched(attr, &sna->sna_inherit);
88         pthread_attr_getschedparam(attr, &sched_param);
89         sna->sna_prio = sched_param.sched_priority;
90         pthread_attr_getscope(attr, &sna->sna_scope);
91         pthread_attr_getstacksize(attr, &sna->sna_stacksize);
92         pthread_attr_getstackaddr(attr, &sna->sna_stackaddr);
93         pthread_attr_getguardsize(attr, &sna->sna_guardsize);
94 }
95
96 static __inline int
97 sna_eq(const struct sigev_thread_attr *a, const struct sigev_thread_attr *b)
98 {
99         return memcmp(a, b, sizeof(*a)) == 0;
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 notfirst = 0;
112         int i;
113
114         sigev_list_mtx = malloc(sizeof(pthread_mutex_t));
115         _pthread_mutex_init(sigev_list_mtx, NULL);
116         sigev_threads_mtx = malloc(sizeof(pthread_mutex_t));
117         _pthread_mutex_init(sigev_threads_mtx, NULL);
118         for (i = 0; i < HASH_QUEUES; ++i)
119                 LIST_INIT(&sigev_hash[i]);
120         LIST_INIT(&sigev_all);
121         TAILQ_INIT(&sigev_threads);
122         if (!notfirst) {
123                 pthread_attr_init(&sigev_default_attr);
124                 pthread_attr_setscope(&sigev_default_attr, PTHREAD_SCOPE_SYSTEM);
125                 pthread_attr_setdetachstate(&sigev_default_attr,
126                         PTHREAD_CREATE_DETACHED);
127                 pthread_atfork(__sigev_fork_prepare, __sigev_fork_parent,
128                         __sigev_fork_child);
129                 notfirst = 1;
130         }
131 }
132
133 int
134 __sigev_check_init(void)
135 {
136         if (!have_threads())
137                 return (-1);
138
139         _pthread_once(&sigev_once, __sigev_thread_init);
140         return (0);
141 }
142
143 void
144 __sigev_fork_prepare(void)
145 {
146         __sigev_thread_list_lock();
147 }
148
149 void
150 __sigev_fork_parent(void)
151 {
152         __sigev_thread_list_unlock();
153 }
154
155 void
156 __sigev_fork_child(void)
157 {
158         __sigev_thread_init();
159 }
160
161 int
162 __sigev_list_lock(void)
163 {
164         return _pthread_mutex_lock(sigev_list_mtx);
165 }
166
167 int
168 __sigev_list_unlock(void)
169 {
170         return _pthread_mutex_unlock(sigev_list_mtx);
171 }
172
173 int
174 __sigev_thread_list_lock(void)
175 {
176         return _pthread_mutex_lock(sigev_threads_mtx);
177 }
178
179 int
180 __sigev_thread_list_unlock(void)
181 {
182         return _pthread_mutex_unlock(sigev_threads_mtx);
183 }
184
185 struct sigev_node *
186 __sigev_alloc(int type, const struct sigevent *evp)
187 {
188         struct sigev_node *sn;
189
190         sn = calloc(1, sizeof(*sn));
191         if (sn != NULL) {
192                 sn->sn_value = evp->sigev_value;
193                 sn->sn_func = evp->sigev_notify_function;
194                 sn->sn_gen = atomic_fetchadd_int(&sigev_generation, 1);
195                 sn->sn_type = type;
196                 sn->sn_tn = sigev_thread_create(evp->sigev_notify_attributes);
197                 if (sn->sn_tn == NULL) {
198                         free(sn);
199                         sn = NULL;
200                 }
201         }
202         return (sn);
203 }
204
205 void
206 __sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp,
207         sigev_id_t id)
208 {
209         /*
210          * Build a new sigevent, and tell kernel to deliver SIGEV_SIGSERVICE
211          * signal to the new thread.
212          */
213         newevp->sigev_notify = SIGEV_THREAD_ID;
214         newevp->sigev_signo  = SIGEV_SIGSERVICE;
215         newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid;
216         newevp->sigev_value.sival_ptr = (void *)id;
217 }
218
219 void
220 __sigev_free(struct sigev_node *sn)
221 {
222         free(sn);
223 }
224
225 struct sigev_node *
226 __sigev_find(int type, sigev_id_t id)
227 {
228         struct sigev_node *sn;
229         int chain = HASH(type, id);
230
231         LIST_FOREACH(sn, &sigev_hash[chain], sn_link) {
232                 if (sn->sn_type == type && sn->sn_id == id)
233                         break;
234         }
235         return (sn);
236 }
237
238 int
239 __sigev_register(struct sigev_node *sn)
240 {
241         int chain = HASH(sn->sn_type, sn->sn_id);
242
243         LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link);
244         LIST_INSERT_HEAD(&sigev_all, sn, sn_allist);
245         return (0);
246 }
247
248 int
249 __sigev_delete(int type, sigev_id_t id)
250 {
251         struct sigev_node *sn;
252
253         sn = __sigev_find(type, id);
254         if (sn != NULL)
255                 return (__sigev_delete_node(sn));
256         return (0);
257 }
258
259 int
260 __sigev_delete_node(struct sigev_node *sn)
261 {
262         LIST_REMOVE(sn, sn_link);
263         LIST_REMOVE(sn, sn_allist);
264
265         if (sn->sn_flags & SNF_WORKING)
266                 sn->sn_flags |= SNF_REMOVED;
267         else
268                 __sigev_free(sn);
269         return (0);
270 }
271
272 static
273 sigev_id_t
274 sigev_get_id(siginfo_t *si)
275 {
276         switch(si->si_code) {
277         case SI_TIMER:
278                 return (si->si_timerid);
279         case SI_MESGQ:
280                 return (si->si_mqd);
281         case SI_ASYNCIO:
282                 return (sigev_id_t)si->si_value.sival_ptr;
283         default:
284                 warnx("%s %s : unknown si_code %d\n", __FILE__, __func__,
285                         si->si_code);
286         }
287         return (-1);
288 }
289
290 static struct sigev_thread_node *
291 sigev_thread_create(pthread_attr_t *pattr)
292 {
293         struct sigev_thread_node *tn;
294         struct sigev_thread_attr sna;
295         sigset_t set;
296         int ret;
297
298         if (pattr == NULL)
299                 pattr = &sigev_default_attr;
300         else {
301                 pthread_attr_setscope(pattr, PTHREAD_SCOPE_SYSTEM);
302                 pthread_attr_setdetachstate(pattr, PTHREAD_CREATE_DETACHED);
303         }
304  
305         attr2sna(pattr, &sna);
306
307         __sigev_thread_list_lock();
308         /* Search a thread matching the required pthread_attr. */
309         TAILQ_FOREACH(tn, &sigev_threads, tn_link) {
310                 if (sna.sna_stackaddr == NULL) {
311                         if (sna_eq(&tn->tn_sna, &sna))
312                                 break;
313                 } else {
314                         /*
315                          * Reuse the thread if it has same stack address,
316                          * because two threads can not run on same stack.
317                          */
318                         if (sna.sna_stackaddr == tn->tn_sna.sna_stackaddr)
319                                 break;
320                 }
321         }
322         if (tn != NULL) {
323                 __sigev_thread_list_unlock();
324                 return (tn);
325         }
326         tn = malloc(sizeof(*tn));
327         tn->tn_sna = sna;
328         tn->tn_cur = NULL;
329         TAILQ_INSERT_TAIL(&sigev_threads, tn, tn_link);
330         sigemptyset(&set);
331         sigaddset(&set, SIGEV_SIGSERVICE);
332         _sigprocmask(SIG_BLOCK, &set, NULL);
333         _pthread_cond_init(&tn->tn_cv, NULL);
334         ret = pthread_create(&tn->tn_thread, pattr, sigev_service_loop, tn);
335         _sigprocmask(SIG_UNBLOCK, &set, NULL);
336         if (ret != 0) {
337                 TAILQ_REMOVE(&sigev_threads, tn, tn_link);
338                 __sigev_thread_list_unlock();
339                 _pthread_cond_destroy(&tn->tn_cv);
340                 free(tn);
341                 tn = NULL;
342         } else {
343                 /* wait the thread to get its lwpid */
344                 _pthread_cond_wait(&tn->tn_cv, sigev_threads_mtx);
345                 __sigev_thread_list_unlock();
346         }
347         return (tn);
348 }
349
350 static void
351 after_dispatch(struct sigev_thread_node *tn)
352 {
353         struct sigev_node *sn;
354
355         if ((sn = tn->tn_cur) != NULL) {
356                 __sigev_list_lock();
357                 sn->sn_flags &= ~SNF_WORKING;
358                 if (sn->sn_flags & SNF_REMOVED)
359                         __sigev_free(sn);
360                 else if (sn->sn_flags & SNF_ONESHOT)
361                         __sigev_delete_node(sn);
362                 tn->tn_cur = NULL;
363                 __sigev_list_unlock();
364         }
365 }
366
367 /*
368  * This function is called if user callback calls
369  * pthread_exit() or pthread_cancel() for the thread.
370  */
371 static void
372 thread_cleanup(void *arg)
373 {
374         struct sigev_thread_node *tn = arg;
375
376         fprintf(stderr, "Dangerous Robinson, calling pthread_exit() from "
377                         "SIGEV_THREAD is undefined.");
378         after_dispatch(tn);
379         /* longjmp(tn->tn_jbuf, 1); */
380         abort();
381 }
382
383 static void *
384 sigev_service_loop(void *arg)
385 {
386         siginfo_t si;
387         sigset_t set;
388         struct sigev_thread_node *tn;
389         struct sigev_node *sn;
390         sigev_id_t id;
391
392         tn = arg;
393         thr_self(&tn->tn_lwpid);
394         __sigev_list_lock();
395         _pthread_cond_broadcast(&tn->tn_cv);
396         __sigev_list_unlock();
397
398         /*
399          * Service thread should not be killed by callback, if user
400          * attempts to do so, the thread will be restarted.
401          */
402         setjmp(tn->tn_jbuf);
403         pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
404         sigemptyset(&set);
405         sigaddset(&set, SIGEV_SIGSERVICE);
406         pthread_cleanup_push(thread_cleanup, tn);
407         for (;;) {
408                 if (__predict_false(sigwaitinfo(&set, &si) == -1))
409                         continue;
410
411                 id = sigev_get_id(&si);
412                 __sigev_list_lock();
413                 sn = __sigev_find(si.si_code, id);
414                 if (sn != NULL) {
415                         tn->tn_cur = sn;
416                         sn->sn_flags |= SNF_WORKING;
417                         __sigev_list_unlock();
418                         sn->sn_dispatch(sn, &si);
419                         after_dispatch(tn);
420                 } else 
421                         tn->tn_cur = NULL;
422                 __sigev_list_unlock();
423         }
424         pthread_cleanup_pop(0);
425         return (0);
426 }