sigev_thread.c revision 181778
1251875Speter/*
2251875Speter * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3251875Speter * All rights reserved.
4251875Speter *
5251875Speter * Redistribution and use in source and binary forms, with or without
6251875Speter * modification, are permitted provided that the following conditions
7251875Speter * are met:
8251875Speter * 1. Redistributions of source code must retain the above copyright
9251875Speter *    notice unmodified, this list of conditions, and the following
10251875Speter *    disclaimer.
11251875Speter * 2. Redistributions in binary form must reproduce the above copyright
12251875Speter *    notice, this list of conditions and the following disclaimer in the
13251875Speter *    documentation and/or other materials provided with the distribution.
14251875Speter *
15251875Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16251875Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17251875Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18251875Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19251875Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20251875Speter * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21251875Speter * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22251875Speter * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23251875Speter * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24251875Speter * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25251875Speter *
26251875Speter * $FreeBSD: head/lib/librt/sigev_thread.c 181778 2008-08-15 21:08:48Z kmacy $
27251875Speter *
28251875Speter */
29251875Speter
30251875Speter#include <sys/types.h>
31251875Speter#include <machine/atomic.h>
32251875Speter
33251875Speter#include "namespace.h"
34251875Speter#include <err.h>
35251875Speter#include <errno.h>
36251875Speter#include <ucontext.h>
37251875Speter#include <sys/thr.h>
38251875Speter#include <stdio.h>
39251875Speter#include <stdlib.h>
40251875Speter#include <string.h>
41251875Speter#include <signal.h>
42251875Speter#include <pthread.h>
43251875Speter#include "un-namespace.h"
44251875Speter
45251875Speter#include "sigev_thread.h"
46251875Speter
47251875SpeterLIST_HEAD(sigev_list_head, sigev_node);
48251875Speter#define HASH_QUEUES		17
49251875Speter#define	HASH(t, id)		((((id) << 3) + (t)) % HASH_QUEUES)
50251875Speter
51251875Speterstatic struct sigev_list_head	sigev_hash[HASH_QUEUES];
52251875Speterstatic struct sigev_list_head	sigev_all;
53251875Speterstatic LIST_HEAD(,sigev_thread)	sigev_threads;
54251875Speterstatic unsigned int		sigev_generation;
55251875Speterstatic pthread_mutex_t		*sigev_list_mtx;
56251875Speterstatic pthread_once_t		sigev_once = PTHREAD_ONCE_INIT;
57251875Speterstatic pthread_once_t		sigev_once_default = PTHREAD_ONCE_INIT;
58251875Speterstatic struct sigev_thread	*sigev_default_thread;
59251875Speterstatic pthread_attr_t		sigev_default_attr;
60251875Speterstatic int			atfork_registered;
61251875Speter
62251875Speterstatic void	__sigev_fork_prepare(void);
63251875Speterstatic void	__sigev_fork_parent(void);
64251875Speterstatic void	__sigev_fork_child(void);
65251875Speterstatic struct sigev_thread	*sigev_thread_create(int);
66251875Speterstatic void	*sigev_service_loop(void *);
67251875Speterstatic void	*worker_routine(void *);
68251875Speterstatic void	worker_cleanup(void *);
69251875Speter
70251875Speter#pragma weak _pthread_create
71251875Speter
72251875Speterstatic void
73251875Speterattrcopy(pthread_attr_t *src, pthread_attr_t *dst)
74251875Speter{
75251875Speter	struct sched_param sched;
76251875Speter	void *a;
77251875Speter	size_t u;
78251875Speter	int v;
79251875Speter
80251875Speter	_pthread_attr_getschedpolicy(src, &v);
81251875Speter	_pthread_attr_setschedpolicy(dst, v);
82251875Speter
83251875Speter	_pthread_attr_getinheritsched(src, &v);
84251875Speter	_pthread_attr_setinheritsched(dst, v);
85251875Speter
86251875Speter	_pthread_attr_getschedparam(src, &sched);
87251875Speter	_pthread_attr_setschedparam(dst, &sched);
88251875Speter
89251875Speter	_pthread_attr_getscope(src, &v);
90251875Speter	_pthread_attr_setscope(dst, v);
91251875Speter
92251875Speter	_pthread_attr_getstacksize(src, &u);
93251875Speter	_pthread_attr_setstacksize(dst, u);
94251875Speter
95251875Speter	_pthread_attr_getstackaddr(src, &a);
96251875Speter	_pthread_attr_setstackaddr(src, a);
97251875Speter
98251875Speter	_pthread_attr_getguardsize(src, &u);
99251875Speter	_pthread_attr_setguardsize(dst, u);
100251875Speter}
101251875Speter
102251875Speterstatic __inline int
103251875Speterhave_threads(void)
104251875Speter{
105251875Speter	return (&_pthread_create != NULL);
106251875Speter}
107251875Speter
108251875Spetervoid
109251875Speter__sigev_thread_init(void)
110251875Speter{
111251875Speter	static int inited = 0;
112251875Speter	pthread_mutexattr_t mattr;
113251875Speter	int i;
114251875Speter
115251875Speter	_pthread_mutexattr_init(&mattr);
116251875Speter	_pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL);
117251875Speter	sigev_list_mtx = malloc(sizeof(pthread_mutex_t));
118251875Speter	_pthread_mutex_init(sigev_list_mtx, &mattr);
119251875Speter	_pthread_mutexattr_destroy(&mattr);
120251875Speter
121251875Speter	for (i = 0; i < HASH_QUEUES; ++i)
122251875Speter		LIST_INIT(&sigev_hash[i]);
123251875Speter	LIST_INIT(&sigev_all);
124251875Speter	LIST_INIT(&sigev_threads);
125251875Speter	sigev_default_thread = NULL;
126251875Speter	if (atfork_registered == 0) {
127251875Speter		_pthread_atfork(
128251875Speter			__sigev_fork_prepare,
129251875Speter			__sigev_fork_parent,
130251875Speter			__sigev_fork_child);
131251875Speter		atfork_registered = 1;
132251875Speter	}
133251875Speter	if (!inited) {
134251875Speter		_pthread_attr_init(&sigev_default_attr);
135251875Speter		_pthread_attr_setscope(&sigev_default_attr,
136251875Speter			PTHREAD_SCOPE_SYSTEM);
137251875Speter		_pthread_attr_setdetachstate(&sigev_default_attr,
138251875Speter			PTHREAD_CREATE_DETACHED);
139251875Speter		inited = 1;
140251875Speter	}
141251875Speter	sigev_default_thread = sigev_thread_create(0);
142251875Speter}
143251875Speter
144251875Speterint
145251875Speter__sigev_check_init(void)
146251875Speter{
147251875Speter	if (!have_threads())
148251875Speter		return (-1);
149251875Speter
150251875Speter	_pthread_once(&sigev_once, __sigev_thread_init);
151251875Speter	return (sigev_default_thread != NULL) ? 0 : -1;
152251875Speter}
153251875Speter
154251875Speterstatic void
155251875Speter__sigev_fork_prepare(void)
156251875Speter{
157251875Speter}
158251875Speter
159251875Speterstatic void
160251875Speter__sigev_fork_parent(void)
161251875Speter{
162251875Speter}
163251875Speter
164251875Speterstatic void
165251875Speter__sigev_fork_child(void)
166251875Speter{
167251875Speter	/*
168251875Speter	 * This is a hack, the thread libraries really should
169251875Speter	 * check if the handlers were already registered in
170251875Speter	 * pthread_atfork().
171251875Speter	 */
172251875Speter	atfork_registered = 1;
173251875Speter	memcpy(&sigev_once, &sigev_once_default, sizeof(sigev_once));
174251875Speter	__sigev_thread_init();
175251875Speter}
176251875Speter
177251875Spetervoid
178251875Speter__sigev_list_lock(void)
179251875Speter{
180251875Speter	_pthread_mutex_lock(sigev_list_mtx);
181251875Speter}
182251875Speter
183251875Spetervoid
184251875Speter__sigev_list_unlock(void)
185251875Speter{
186251875Speter	_pthread_mutex_unlock(sigev_list_mtx);
187251875Speter}
188251875Speter
189251875Speterstruct sigev_node *
190251875Speter__sigev_alloc(int type, const struct sigevent *evp, struct sigev_node *prev,
191251875Speter	int usedefault)
192251875Speter{
193251875Speter	struct sigev_node *sn;
194251875Speter
195251875Speter	sn = calloc(1, sizeof(*sn));
196251875Speter	if (sn != NULL) {
197251875Speter		sn->sn_value = evp->sigev_value;
198251875Speter		sn->sn_func  = evp->sigev_notify_function;
199251875Speter		sn->sn_gen   = atomic_fetchadd_int(&sigev_generation, 1);
200251875Speter		sn->sn_type  = type;
201251875Speter		_pthread_attr_init(&sn->sn_attr);
202251875Speter		_pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED);
203251875Speter		if (evp->sigev_notify_attributes)
204251875Speter			attrcopy(evp->sigev_notify_attributes, &sn->sn_attr);
205251875Speter		if (prev) {
206251875Speter			__sigev_list_lock();
207251875Speter			prev->sn_tn->tn_refcount++;
208251875Speter			__sigev_list_unlock();
209251875Speter			sn->sn_tn = prev->sn_tn;
210251875Speter		} else {
211251875Speter			sn->sn_tn = sigev_thread_create(usedefault);
212251875Speter			if (sn->sn_tn == NULL) {
213251875Speter				_pthread_attr_destroy(&sn->sn_attr);
214251875Speter				free(sn);
215251875Speter				sn = NULL;
216251875Speter			}
217251875Speter		}
218251875Speter	}
219251875Speter	return (sn);
220251875Speter}
221251875Speter
222251875Spetervoid
223251875Speter__sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp,
224251875Speter	sigev_id_t id)
225251875Speter{
226251875Speter	/*
227251875Speter	 * Build a new sigevent, and tell kernel to deliver SIGSERVICE
228251875Speter	 * signal to the new thread.
229251875Speter	 */
230251875Speter	newevp->sigev_notify = SIGEV_THREAD_ID;
231251875Speter	newevp->sigev_signo  = SIGSERVICE;
232251875Speter	newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid;
233251875Speter	newevp->sigev_value.sival_ptr = (void *)id;
234251875Speter}
235251875Speter
236251875Spetervoid
237251875Speter__sigev_free(struct sigev_node *sn)
238251875Speter{
239251875Speter	_pthread_attr_destroy(&sn->sn_attr);
240251875Speter	free(sn);
241251875Speter}
242251875Speter
243251875Speterstruct sigev_node *
244251875Speter__sigev_find(int type, sigev_id_t id)
245251875Speter{
246251875Speter	struct sigev_node *sn;
247251875Speter	int chain = HASH(type, id);
248251875Speter
249251875Speter	LIST_FOREACH(sn, &sigev_hash[chain], sn_link) {
250251875Speter		if (sn->sn_type == type && sn->sn_id == id)
251251875Speter			break;
252251875Speter	}
253251875Speter	return (sn);
254251875Speter}
255251875Speter
256251875Speterint
257251875Speter__sigev_register(struct sigev_node *sn)
258251875Speter{
259251875Speter	int chain = HASH(sn->sn_type, sn->sn_id);
260251875Speter
261251875Speter	LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link);
262251875Speter	return (0);
263251875Speter}
264251875Speter
265251875Speterint
266251875Speter__sigev_delete(int type, sigev_id_t id)
267251875Speter{
268251875Speter	struct sigev_node *sn;
269251875Speter
270251875Speter	sn = __sigev_find(type, id);
271251875Speter	if (sn != NULL)
272251875Speter		return (__sigev_delete_node(sn));
273251875Speter	return (0);
274251875Speter}
275251875Speter
276251875Speterint
277251875Speter__sigev_delete_node(struct sigev_node *sn)
278251875Speter{
279251875Speter	LIST_REMOVE(sn, sn_link);
280251875Speter
281251875Speter	if (--sn->sn_tn->tn_refcount == 0)
282251875Speter		_pthread_kill(sn->sn_tn->tn_thread, SIGSERVICE);
283251875Speter	if (sn->sn_flags & SNF_WORKING)
284251875Speter		sn->sn_flags |= SNF_REMOVED;
285251875Speter	else
286251875Speter		__sigev_free(sn);
287251875Speter	return (0);
288251875Speter}
289251875Speter
290251875Speterstatic sigev_id_t
291251875Spetersigev_get_id(siginfo_t *si)
292251875Speter{
293251875Speter	switch(si->si_code) {
294251875Speter	case SI_TIMER:
295251875Speter		return (si->si_timerid);
296251875Speter	case SI_MESGQ:
297251875Speter		return (si->si_mqd);
298251875Speter	case SI_ASYNCIO:
299251875Speter		return (sigev_id_t)si->si_value.sival_ptr;
300251875Speter	}
301251875Speter	return (-1);
302251875Speter}
303251875Speter
304251875Speterstatic struct sigev_thread *
305251875Spetersigev_thread_create(int usedefault)
306251875Speter{
307251875Speter	struct sigev_thread *tn;
308251875Speter	sigset_t set, oset;
309251875Speter	int ret;
310251875Speter
311251875Speter	if (usedefault && sigev_default_thread) {
312251875Speter		__sigev_list_lock();
313251875Speter		sigev_default_thread->tn_refcount++;
314251875Speter		__sigev_list_unlock();
315251875Speter		return (sigev_default_thread);
316251875Speter	}
317251875Speter
318251875Speter	tn = malloc(sizeof(*tn));
319251875Speter	tn->tn_cur = NULL;
320251875Speter	tn->tn_lwpid = -1;
321251875Speter	tn->tn_refcount = 1;
322251875Speter	_pthread_cond_init(&tn->tn_cv, NULL);
323251875Speter
324251875Speter	/* for debug */
325251875Speter	__sigev_list_lock();
326251875Speter	LIST_INSERT_HEAD(&sigev_threads, tn, tn_link);
327251875Speter	__sigev_list_unlock();
328251875Speter
329251875Speter	sigfillset(&set);	/* SIGSERVICE is masked. */
330251875Speter	sigdelset(&set, SIGBUS);
331251875Speter	sigdelset(&set, SIGILL);
332251875Speter	sigdelset(&set, SIGFPE);
333251875Speter	sigdelset(&set, SIGSEGV);
334251875Speter	sigdelset(&set, SIGTRAP);
335251875Speter	_sigprocmask(SIG_SETMASK, &set, &oset);
336251875Speter	ret = _pthread_create(&tn->tn_thread, &sigev_default_attr,
337251875Speter		 sigev_service_loop, tn);
338251875Speter	_sigprocmask(SIG_SETMASK, &oset, NULL);
339251875Speter
340251875Speter	if (ret != 0) {
341251875Speter		__sigev_list_lock();
342251875Speter		LIST_REMOVE(tn, tn_link);
343251875Speter		__sigev_list_unlock();
344251875Speter		free(tn);
345251875Speter		tn = NULL;
346251875Speter	} else {
347251875Speter		/* wait the thread to get its lwpid */
348251875Speter
349251875Speter		__sigev_list_lock();
350251875Speter		while (tn->tn_lwpid == -1)
351251875Speter			_pthread_cond_wait(&tn->tn_cv, sigev_list_mtx);
352251875Speter		__sigev_list_unlock();
353251875Speter	}
354251875Speter	return (tn);
355251875Speter}
356251875Speter
357251875Speter/*
358251875Speter * The thread receives notification from kernel and creates
359251875Speter * a thread to call user callback function.
360251875Speter */
361251875Speterstatic void *
362251875Spetersigev_service_loop(void *arg)
363251875Speter{
364251875Speter	static int failure;
365251875Speter
366251875Speter	siginfo_t si;
367251875Speter	sigset_t set;
368251875Speter	struct sigev_thread *tn;
369251875Speter	struct sigev_node *sn;
370251875Speter	sigev_id_t id;
371251875Speter	pthread_t td;
372251875Speter	int ret;
373251875Speter
374251875Speter	tn = arg;
375251875Speter	thr_self(&tn->tn_lwpid);
376251875Speter	__sigev_list_lock();
377251875Speter	_pthread_cond_broadcast(&tn->tn_cv);
378251875Speter	__sigev_list_unlock();
379251875Speter
380251875Speter	sigemptyset(&set);
381251875Speter	sigaddset(&set, SIGSERVICE);
382251875Speter	for (;;) {
383251875Speter		ret = sigwaitinfo(&set, &si);
384251875Speter
385251875Speter		__sigev_list_lock();
386251875Speter		if (tn->tn_refcount == 0) {
387251875Speter			LIST_REMOVE(tn, tn_link);
388251875Speter			__sigev_list_unlock();
389251875Speter			free(tn);
390251875Speter			break;
391251875Speter		}
392251875Speter
393251875Speter		if (ret == -1) {
394251875Speter			__sigev_list_unlock();
395251875Speter			continue;
396251875Speter		}
397251875Speter
398251875Speter		id = sigev_get_id(&si);
399251875Speter		sn = __sigev_find(si.si_code, id);
400251875Speter		if (sn == NULL) {
401251875Speter			__sigev_list_unlock();
402251875Speter			continue;
403251875Speter		}
404251875Speter
405251875Speter		sn->sn_info = si;
406251875Speter		if (sn->sn_flags & SNF_SYNC)
407251875Speter			tn->tn_cur = sn;
408251875Speter		else
409251875Speter			tn->tn_cur = NULL;
410251875Speter		sn->sn_flags |= SNF_WORKING;
411251875Speter		__sigev_list_unlock();
412251875Speter
413251875Speter		ret = _pthread_create(&td, &sn->sn_attr, worker_routine, sn);
414251875Speter		if (ret != 0) {
415251875Speter			if (failure++ < 5)
416251875Speter				warnc(ret, "%s:%s failed to create thread.\n",
417251875Speter					__FILE__, __func__);
418251875Speter
419251875Speter			__sigev_list_lock();
420251875Speter			sn->sn_flags &= ~SNF_WORKING;
421251875Speter			if (sn->sn_flags & SNF_REMOVED)
422251875Speter				__sigev_free(sn);
423251875Speter			__sigev_list_unlock();
424251875Speter		} else if (tn->tn_cur) {
425251875Speter			__sigev_list_lock();
426251875Speter			while (tn->tn_cur)
427251875Speter				_pthread_cond_wait(&tn->tn_cv, sigev_list_mtx);
428251875Speter			__sigev_list_unlock();
429251875Speter		}
430251875Speter	}
431251875Speter	return (0);
432251875Speter}
433251875Speter
434251875Speter/*
435251875Speter * newly created worker thread to call user callback function.
436251875Speter */
437251875Speterstatic void *
438251875Speterworker_routine(void *arg)
439251875Speter{
440251875Speter	struct sigev_node *sn = arg;
441251875Speter
442251875Speter	_pthread_cleanup_push(worker_cleanup, sn);
443251875Speter	sn->sn_dispatch(sn);
444251875Speter	_pthread_cleanup_pop(1);
445251875Speter
446251875Speter	return (0);
447251875Speter}
448251875Speter
449251875Speter/* clean up a notification after dispatch. */
450251875Speterstatic void
451251875Speterworker_cleanup(void *arg)
452251875Speter{
453251875Speter	struct sigev_node *sn = arg;
454251875Speter
455251875Speter	__sigev_list_lock();
456251875Speter	if (sn->sn_flags & SNF_SYNC) {
457251875Speter		sn->sn_tn->tn_cur = NULL;
458251875Speter		_pthread_cond_broadcast(&sn->sn_tn->tn_cv);
459251875Speter	}
460251875Speter	if (sn->sn_flags & SNF_REMOVED)
461251875Speter		__sigev_free(sn);
462251875Speter	else
463251875Speter		sn->sn_flags &= ~SNF_WORKING;
464251875Speter	__sigev_list_unlock();
465251875Speter}
466251875Speter