sigev_thread.c revision 156142
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: head/lib/librt/sigev_thread.c 156142 2006-03-01 08:50:36Z davidxu $
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
46LIST_HEAD(sigev_list_head, sigev_node);
47#define HASH_QUEUES		17
48#define	HASH(t, id)		((((id) << 3) + (t)) % HASH_QUEUES)
49static struct sigev_list_head	sigev_hash[HASH_QUEUES];
50static struct sigev_list_head	sigev_all;
51static int			sigev_generation;
52static pthread_mutex_t		*sigev_list_mtx;
53static TAILQ_HEAD(,sigev_thread_node)	sigev_threads;
54static pthread_mutex_t		*sigev_threads_mtx;
55static pthread_attr_t		sigev_default_attr;
56static pthread_once_t		sigev_once = PTHREAD_ONCE_INIT;
57
58static void __sigev_fork_prepare(void);
59static void __sigev_fork_parent(void);
60static void __sigev_fork_child(void);
61static struct sigev_thread_node *sigev_thread_create(pthread_attr_t *);
62static 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
81static __inline void
82attr2sna(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
96static __inline int
97sna_eq(const struct sigev_thread_attr *a, const struct sigev_thread_attr *b)
98{
99	return memcmp(a, b, sizeof(*a)) == 0;
100}
101
102static __inline int
103have_threads(void)
104{
105	return (pthread_create != NULL);
106}
107
108void
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
133int
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
143void
144__sigev_fork_prepare(void)
145{
146	__sigev_thread_list_lock();
147}
148
149void
150__sigev_fork_parent(void)
151{
152	__sigev_thread_list_unlock();
153}
154
155void
156__sigev_fork_child(void)
157{
158	__sigev_thread_init();
159}
160
161int
162__sigev_list_lock(void)
163{
164	return _pthread_mutex_lock(sigev_list_mtx);
165}
166
167int
168__sigev_list_unlock(void)
169{
170	return _pthread_mutex_unlock(sigev_list_mtx);
171}
172
173int
174__sigev_thread_list_lock(void)
175{
176	return _pthread_mutex_lock(sigev_threads_mtx);
177}
178
179int
180__sigev_thread_list_unlock(void)
181{
182	return _pthread_mutex_unlock(sigev_threads_mtx);
183}
184
185struct 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
205void
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
219void
220__sigev_free(struct sigev_node *sn)
221{
222	free(sn);
223}
224
225struct 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
238int
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
248int
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
259int
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
272static
273sigev_id_t
274sigev_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
290static struct sigev_thread_node *
291sigev_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
350static void
351after_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 */
371static void
372thread_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
383static void *
384sigev_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}
427