sigev_thread.c revision 157242
11556Srgrimes/*
21556Srgrimes * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
31556Srgrimes * All rights reserved.
41556Srgrimes *
51556Srgrimes * Redistribution and use in source and binary forms, with or without
61556Srgrimes * modification, are permitted provided that the following conditions
71556Srgrimes * are met:
81556Srgrimes * 1. Redistributions of source code must retain the above copyright
91556Srgrimes *    notice unmodified, this list of conditions, and the following
101556Srgrimes *    disclaimer.
111556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer in the
131556Srgrimes *    documentation and/or other materials provided with the distribution.
141556Srgrimes *
151556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
161556Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
171556Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
181556Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
191556Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
201556Srgrimes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
211556Srgrimes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
221556Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
231556Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
241556Srgrimes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
251556Srgrimes *
261556Srgrimes * $FreeBSD: head/lib/librt/sigev_thread.c 157242 2006-03-29 04:20:53Z deischen $
271556Srgrimes *
281556Srgrimes */
291556Srgrimes
301556Srgrimes#include <sys/types.h>
311556Srgrimes#include <machine/atomic.h>
321556Srgrimes
331556Srgrimes#include "namespace.h"
3436150Scharnier#include <err.h>
3536150Scharnier#include <errno.h>
3636150Scharnier#include <ucontext.h>
371556Srgrimes#include <sys/thr.h>
3899110Sobrien#include <stdio.h>
3999110Sobrien#include <stdlib.h>
401556Srgrimes#include <string.h>
4117987Speter#include <signal.h>
4217987Speter#include <pthread.h>
4317987Speter#include "un-namespace.h"
4417987Speter
4517987Speter#include "sigev_thread.h"
4617987Speter
4717987SpeterLIST_HEAD(sigev_list_head, sigev_node);
481556Srgrimes#define HASH_QUEUES		17
491556Srgrimes#define	HASH(t, id)		((((id) << 3) + (t)) % HASH_QUEUES)
501556Srgrimes
511556Srgrimesstatic struct sigev_list_head	sigev_hash[HASH_QUEUES];
521556Srgrimesstatic struct sigev_list_head	sigev_all;
531556Srgrimesstatic LIST_HEAD(,sigev_thread)	sigev_threads;
541556Srgrimesstatic int			sigev_generation;
551556Srgrimesstatic pthread_mutex_t		*sigev_list_mtx;
561556Srgrimesstatic pthread_once_t		sigev_once = PTHREAD_ONCE_INIT;
571556Srgrimesstatic pthread_once_t		sigev_once_default = PTHREAD_ONCE_INIT;
581556Srgrimesstatic struct sigev_thread	*sigev_default_thread;
591556Srgrimesstatic pthread_attr_t		sigev_default_attr;
601556Srgrimesstatic int			atfork_registered;
611556Srgrimes
621556Srgrimesstatic void	__sigev_fork_prepare(void);
631556Srgrimesstatic void	__sigev_fork_parent(void);
641556Srgrimesstatic void	__sigev_fork_child(void);
651556Srgrimesstatic struct sigev_thread	*sigev_thread_create(int);
661556Srgrimesstatic void	*sigev_service_loop(void *);
671556Srgrimesstatic void	*worker_routine(void *);
681556Srgrimesstatic void	worker_cleanup(void *);
691556Srgrimes
701556Srgrimes#pragma weak _pthread_create
711556Srgrimes
721556Srgrimesstatic void
731556Srgrimesattrcopy(pthread_attr_t *src, pthread_attr_t *dst)
7417987Speter{
751556Srgrimes	struct sched_param sched;
7625223Ssteve	void *a;
771556Srgrimes	size_t u;
781556Srgrimes	int v;
791556Srgrimes
801556Srgrimes	_pthread_attr_getschedpolicy(src, &v);
811556Srgrimes	_pthread_attr_setschedpolicy(dst, v);
821556Srgrimes
831556Srgrimes	_pthread_attr_getinheritsched(src, &v);
841556Srgrimes	_pthread_attr_setinheritsched(dst, v);
851556Srgrimes
861556Srgrimes	_pthread_attr_getschedparam(src, &sched);
87157601Sstefanf	_pthread_attr_setschedparam(dst, &sched);
881556Srgrimes
891556Srgrimes	_pthread_attr_getscope(src, &v);
901556Srgrimes	_pthread_attr_setscope(dst, v);
911556Srgrimes
921556Srgrimes	_pthread_attr_getstacksize(src, &u);
931556Srgrimes	_pthread_attr_setstacksize(dst, u);
941556Srgrimes
951556Srgrimes	_pthread_attr_getstackaddr(src, &a);
9620425Ssteve	_pthread_attr_setstackaddr(src, a);
971556Srgrimes
981556Srgrimes	_pthread_attr_getguardsize(src, &u);
9990111Simp	_pthread_attr_setguardsize(dst, u);
10090111Simp}
10190111Simp
10290111Simpstatic __inline int
1031556Srgrimeshave_threads(void)
1041556Srgrimes{
1051556Srgrimes	return (&_pthread_create != NULL);
1061556Srgrimes}
1071556Srgrimes
1081556Srgrimesvoid
1091556Srgrimes__sigev_thread_init(void)
1101556Srgrimes{
1111556Srgrimes	static int inited = 0;
11290111Simp	pthread_mutexattr_t mattr;
11317987Speter	int i;
1141556Srgrimes
1151556Srgrimes	_pthread_mutexattr_init(&mattr);
1161556Srgrimes	_pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL);
1171556Srgrimes	sigev_list_mtx = malloc(sizeof(pthread_mutex_t));
1181556Srgrimes	_pthread_mutex_init(sigev_list_mtx, &mattr);
1191556Srgrimes	_pthread_mutexattr_destroy(&mattr);
1201556Srgrimes
1211556Srgrimes	for (i = 0; i < HASH_QUEUES; ++i)
1221556Srgrimes		LIST_INIT(&sigev_hash[i]);
1231556Srgrimes	LIST_INIT(&sigev_all);
1241556Srgrimes	LIST_INIT(&sigev_threads);
1251556Srgrimes	sigev_default_thread = NULL;
1261556Srgrimes	if (atfork_registered == 0) {
1271556Srgrimes		_pthread_atfork(
1281556Srgrimes			__sigev_fork_prepare,
1291556Srgrimes			__sigev_fork_parent,
1301556Srgrimes			__sigev_fork_child);
13120425Ssteve		atfork_registered = 1;
13220425Ssteve	}
13320425Ssteve	if (!inited) {
13420425Ssteve		_pthread_attr_init(&sigev_default_attr);
13520425Ssteve		_pthread_attr_setscope(&sigev_default_attr,
13620425Ssteve			PTHREAD_SCOPE_SYSTEM);
13720425Ssteve		_pthread_attr_setdetachstate(&sigev_default_attr,
13820425Ssteve			PTHREAD_CREATE_DETACHED);
13920425Ssteve		inited = 1;
14020425Ssteve	}
14120425Ssteve	sigev_default_thread = sigev_thread_create(0);
14220425Ssteve}
14320425Ssteve
144104283Stjrint
145104283Stjr__sigev_check_init(void)
146104132Stjr{
1471556Srgrimes	if (!have_threads())
1481556Srgrimes		return (-1);
1491556Srgrimes
1501556Srgrimes	_pthread_once(&sigev_once, __sigev_thread_init);
15190111Simp	return (sigev_default_thread != NULL) ? 0 : -1;
15290111Simp}
1531556Srgrimes
1541556Srgrimesstatic void
1551556Srgrimes__sigev_fork_prepare(void)
1561556Srgrimes{
1571556Srgrimes}
1581556Srgrimes
1591556Srgrimesstatic void
1601556Srgrimes__sigev_fork_parent(void)
1611556Srgrimes{
1621556Srgrimes}
1631556Srgrimes
1641556Srgrimesstatic void
1651556Srgrimes__sigev_fork_child(void)
1661556Srgrimes{
1671556Srgrimes	/*
1681556Srgrimes	 * This is a hack, the thread libraries really should
1691556Srgrimes	 * check if the handlers were already registered in
1701556Srgrimes	 * pthread_atfork().
1711556Srgrimes	 */
1721556Srgrimes	atfork_registered = 1;
1731556Srgrimes	memcpy(&sigev_once, &sigev_once_default, sizeof(sigev_once));
1741556Srgrimes	__sigev_thread_init();
1751556Srgrimes}
1761556Srgrimes
1771556Srgrimesvoid
1781556Srgrimes__sigev_list_lock(void)
1791556Srgrimes{
1801556Srgrimes	_pthread_mutex_lock(sigev_list_mtx);
18190111Simp}
18290111Simp
18325223Sstevevoid
1841556Srgrimes__sigev_list_unlock(void)
1851556Srgrimes{
1861556Srgrimes	_pthread_mutex_unlock(sigev_list_mtx);
1871556Srgrimes}
1881556Srgrimes
1891556Srgrimesstruct sigev_node *
190193223Srse__sigev_alloc(int type, const struct sigevent *evp, struct sigev_node *prev,
191193223Srse	int usedefault)
1921556Srgrimes{
1931556Srgrimes	struct sigev_node *sn;
1941556Srgrimes
1951556Srgrimes	sn = calloc(1, sizeof(*sn));
1961556Srgrimes	if (sn != NULL) {
19717987Speter		sn->sn_value = evp->sigev_value;
1981556Srgrimes		sn->sn_func  = evp->sigev_notify_function;
1991556Srgrimes		sn->sn_gen   = atomic_fetchadd_int(&sigev_generation, 1);
2001556Srgrimes		sn->sn_type  = type;
2011556Srgrimes		_pthread_attr_init(&sn->sn_attr);
2021556Srgrimes		_pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED);
2031556Srgrimes		if (evp->sigev_notify_attributes)
2041556Srgrimes			attrcopy(evp->sigev_notify_attributes, &sn->sn_attr);
2051556Srgrimes		if (prev) {
2061556Srgrimes			__sigev_list_lock();
2071556Srgrimes			prev->sn_tn->tn_refcount++;
2081556Srgrimes			__sigev_list_unlock();
2091556Srgrimes			sn->sn_tn = prev->sn_tn;
2101556Srgrimes		} else {
2111556Srgrimes			sn->sn_tn = sigev_thread_create(usedefault);
2121556Srgrimes			if (sn->sn_tn == NULL) {
2131556Srgrimes				_pthread_attr_destroy(&sn->sn_attr);
2141556Srgrimes				free(sn);
2151556Srgrimes				sn = NULL;
2161556Srgrimes			}
2171556Srgrimes		}
2181556Srgrimes	}
21917987Speter	return (sn);
22090111Simp}
22117987Speter
2221556Srgrimesvoid
2231556Srgrimes__sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp,
2241556Srgrimes	sigev_id_t id)
2251556Srgrimes{
2261556Srgrimes	/*
2271556Srgrimes	 * Build a new sigevent, and tell kernel to deliver SIGSERVICE
2281556Srgrimes	 * signal to the new thread.
2291556Srgrimes	 */
2301556Srgrimes	newevp->sigev_notify = SIGEV_THREAD_ID;
2311556Srgrimes	newevp->sigev_signo  = SIGSERVICE;
2321556Srgrimes	newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid;
2331556Srgrimes	newevp->sigev_value.sival_ptr = (void *)id;
2341556Srgrimes}
2351556Srgrimes
2361556Srgrimesvoid
2371556Srgrimes__sigev_free(struct sigev_node *sn)
2381556Srgrimes{
2391556Srgrimes	_pthread_attr_destroy(&sn->sn_attr);
24098157Stjr	free(sn);
24198157Stjr}
2421556Srgrimes
2431556Srgrimesstruct sigev_node *
2441556Srgrimes__sigev_find(int type, sigev_id_t id)
2451556Srgrimes{
2461556Srgrimes	struct sigev_node *sn;
2471556Srgrimes	int chain = HASH(type, id);
2481556Srgrimes
24917987Speter	LIST_FOREACH(sn, &sigev_hash[chain], sn_link) {
2501556Srgrimes		if (sn->sn_type == type && sn->sn_id == id)
25117987Speter			break;
2521556Srgrimes	}
2531556Srgrimes	return (sn);
2541556Srgrimes}
25594775Sgreid
25694775Sgreidint
25794775Sgreid__sigev_register(struct sigev_node *sn)
25894775Sgreid{
2591556Srgrimes	int chain = HASH(sn->sn_type, sn->sn_id);
2601556Srgrimes
2611556Srgrimes	LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link);
2621556Srgrimes	return (0);
2631556Srgrimes}
2641556Srgrimes
2651556Srgrimesint
2661556Srgrimes__sigev_delete(int type, sigev_id_t id)
2671556Srgrimes{
2681556Srgrimes	struct sigev_node *sn;
26990111Simp
27090111Simp	sn = __sigev_find(type, id);
2711556Srgrimes	if (sn != NULL)
2721556Srgrimes		return (__sigev_delete_node(sn));
2731556Srgrimes	return (0);
2741556Srgrimes}
2751556Srgrimes
2761556Srgrimesint
2771556Srgrimes__sigev_delete_node(struct sigev_node *sn)
2781556Srgrimes{
2791556Srgrimes	LIST_REMOVE(sn, sn_link);
2801556Srgrimes
2811556Srgrimes	if (--sn->sn_tn->tn_refcount == 0)
2821556Srgrimes		_pthread_kill(sn->sn_tn->tn_thread, SIGSERVICE);
2831556Srgrimes	if (sn->sn_flags & SNF_WORKING)
2841556Srgrimes		sn->sn_flags |= SNF_REMOVED;
2851556Srgrimes	else
2861556Srgrimes		__sigev_free(sn);
2871556Srgrimes	return (0);
2881556Srgrimes}
289196483Sjilles
2901556Srgrimesstatic sigev_id_t
2911556Srgrimessigev_get_id(siginfo_t *si)
2921556Srgrimes{
2931556Srgrimes	switch(si->si_code) {
2941556Srgrimes	case SI_TIMER:
2951556Srgrimes		return (si->si_timerid);
2961556Srgrimes	case SI_MESGQ:
2971556Srgrimes		return (si->si_mqd);
2981556Srgrimes	case SI_ASYNCIO:
2991556Srgrimes		return (sigev_id_t)si->si_value.sival_ptr;
3001556Srgrimes	}
3011556Srgrimes	return (-1);
3021556Srgrimes}
3031556Srgrimes
3041556Srgrimesstatic struct sigev_thread *
3051556Srgrimessigev_thread_create(int usedefault)
3061556Srgrimes{
3071556Srgrimes	struct sigev_thread *tn;
3081556Srgrimes	sigset_t set, oset;
3091556Srgrimes	int ret;
3101556Srgrimes
3111556Srgrimes	if (usedefault && sigev_default_thread) {
3121556Srgrimes		__sigev_list_lock();
31390111Simp		sigev_default_thread->tn_refcount++;
31417987Speter		__sigev_list_unlock();
3151556Srgrimes		return (sigev_default_thread);
3161556Srgrimes	}
3171556Srgrimes
3181556Srgrimes	tn = malloc(sizeof(*tn));
3191556Srgrimes	tn->tn_cur = NULL;
3201556Srgrimes	tn->tn_lwpid = -1;
3211556Srgrimes	tn->tn_refcount = 1;
322157601Sstefanf	_pthread_cond_init(&tn->tn_cv, NULL);
3231556Srgrimes
3241556Srgrimes	/* for debug */
3251556Srgrimes	__sigev_list_lock();
3261556Srgrimes	LIST_INSERT_HEAD(&sigev_threads, tn, tn_link);
3271556Srgrimes	__sigev_list_unlock();
3281556Srgrimes
3291556Srgrimes	sigfillset(&set);	/* SIGSERVICE is masked. */
3301556Srgrimes	sigdelset(&set, SIGBUS);
3311556Srgrimes	sigdelset(&set, SIGILL);
3321556Srgrimes	sigdelset(&set, SIGFPE);
3331556Srgrimes	sigdelset(&set, SIGSEGV);
3341556Srgrimes	sigdelset(&set, SIGTRAP);
3351556Srgrimes	_sigprocmask(SIG_SETMASK, &set, &oset);
336157601Sstefanf	ret = _pthread_create(&tn->tn_thread, &sigev_default_attr,
3371556Srgrimes		 sigev_service_loop, tn);
3381556Srgrimes	_sigprocmask(SIG_SETMASK, &oset, NULL);
3391556Srgrimes
3401556Srgrimes	if (ret != 0) {
341157601Sstefanf		__sigev_list_lock();
3421556Srgrimes		LIST_REMOVE(tn, tn_link);
3431556Srgrimes		__sigev_list_unlock();
3441556Srgrimes		free(tn);
3451556Srgrimes		tn = NULL;
3461556Srgrimes	} else {
3471556Srgrimes		/* wait the thread to get its lwpid */
3481556Srgrimes
3491556Srgrimes		__sigev_list_lock();
3501556Srgrimes		while (tn->tn_lwpid == -1)
3511556Srgrimes			_pthread_cond_wait(&tn->tn_cv, sigev_list_mtx);
3521556Srgrimes		__sigev_list_unlock();
3531556Srgrimes	}
3541556Srgrimes	return (tn);
3551556Srgrimes}
3561556Srgrimes
3571556Srgrimes/*
3581556Srgrimes * The thread receives notification from kernel and creates
3591556Srgrimes * a thread to call user callback function.
3601556Srgrimes */
3611556Srgrimesstatic void *
3621556Srgrimessigev_service_loop(void *arg)
363157601Sstefanf{
3641556Srgrimes	static int failure;
3651556Srgrimes
3661556Srgrimes	siginfo_t si;
3671556Srgrimes	sigset_t set;
3681556Srgrimes	struct sigev_thread *tn;
369157601Sstefanf	struct sigev_node *sn;
3701556Srgrimes	sigev_id_t id;
3711556Srgrimes	pthread_t td;
3721556Srgrimes	int ret;
3731556Srgrimes
3741556Srgrimes	tn = arg;
3751556Srgrimes	thr_self(&tn->tn_lwpid);
3761556Srgrimes	__sigev_list_lock();
3771556Srgrimes	_pthread_cond_broadcast(&tn->tn_cv);
3781556Srgrimes	__sigev_list_unlock();
3791556Srgrimes
3801556Srgrimes	sigemptyset(&set);
3811556Srgrimes	sigaddset(&set, SIGSERVICE);
3821556Srgrimes	for (;;) {
3831556Srgrimes		ret = sigwaitinfo(&set, &si);
3841556Srgrimes
385100351Stjr		__sigev_list_lock();
3861556Srgrimes		if (tn->tn_refcount == 0) {
3871556Srgrimes			LIST_REMOVE(tn, tn_link);
3881556Srgrimes			__sigev_list_unlock();
3891556Srgrimes			free(tn);
3901556Srgrimes			break;
39117987Speter		}
3921556Srgrimes
3931556Srgrimes		if (ret == -1) {
3941556Srgrimes			__sigev_list_unlock();
3951556Srgrimes			continue;
3961556Srgrimes		}
3971556Srgrimes
3981556Srgrimes		id = sigev_get_id(&si);
3991556Srgrimes		sn = __sigev_find(si.si_code, id);
4001556Srgrimes		if (sn == NULL) {
4011556Srgrimes			__sigev_list_unlock();
4021556Srgrimes			continue;
4031556Srgrimes		}
4041556Srgrimes
4051556Srgrimes		sn->sn_info = si;
4061556Srgrimes		if (sn->sn_flags & SNF_SYNC)
4071556Srgrimes			tn->tn_cur = sn;
4081556Srgrimes		else
4091556Srgrimes			tn->tn_cur = NULL;
4101556Srgrimes		sn->sn_flags |= SNF_WORKING;
4111556Srgrimes		__sigev_list_unlock();
4121556Srgrimes
4131556Srgrimes		ret = _pthread_create(&td, &sn->sn_attr, worker_routine, sn);
4141556Srgrimes		if (ret != 0) {
4151556Srgrimes			if (failure++ < 5)
4161556Srgrimes				warnc(ret, "%s:%s failed to create thread.\n",
4171556Srgrimes					__FILE__, __func__);
4181556Srgrimes
4191556Srgrimes			__sigev_list_lock();
4201556Srgrimes			sn->sn_flags &= ~SNF_WORKING;
4211556Srgrimes			if (sn->sn_flags & SNF_REMOVED)
4221556Srgrimes				__sigev_free(sn);
4231556Srgrimes			__sigev_list_unlock();
4241556Srgrimes		} else if (tn->tn_cur) {
425104283Stjr			__sigev_list_lock();
426104283Stjr			while (tn->tn_cur)
427104283Stjr				_pthread_cond_wait(&tn->tn_cv, sigev_list_mtx);
428104283Stjr			__sigev_list_unlock();
429104283Stjr		}
430104283Stjr	}
4311556Srgrimes	return (0);
4321556Srgrimes}
4331556Srgrimes
4341556Srgrimes/*
4351556Srgrimes * newly created worker thread to call user callback function.
4361556Srgrimes */
4371556Srgrimesstatic void *
438157601Sstefanfworker_routine(void *arg)
4391556Srgrimes{
4401556Srgrimes	struct sigev_node *sn = arg;
4411556Srgrimes
4421556Srgrimes	_pthread_cleanup_push(worker_cleanup, sn);
4431556Srgrimes	sn->sn_dispatch(sn);
4441556Srgrimes	_pthread_cleanup_pop(1);
4451556Srgrimes
4461556Srgrimes	return (0);
4471556Srgrimes}
448157601Sstefanf
44917987Speter/* clean up a notification after dispatch. */
45025223Sstevestatic void
4511556Srgrimesworker_cleanup(void *arg)
4521556Srgrimes{
453157601Sstefanf	struct sigev_node *sn = arg;
454157601Sstefanf
4551556Srgrimes	__sigev_list_lock();
456157601Sstefanf	if (sn->sn_flags & SNF_SYNC) {
4571556Srgrimes		sn->sn_tn->tn_cur = NULL;
4581556Srgrimes		_pthread_cond_broadcast(&sn->sn_tn->tn_cv);
4591556Srgrimes	}
4601556Srgrimes	if (sn->sn_flags & SNF_REMOVED)
4611556Srgrimes		__sigev_free(sn);
4621556Srgrimes	else
4631556Srgrimes		sn->sn_flags &= ~SNF_WORKING;
4641556Srgrimes	__sigev_list_unlock();
4651556Srgrimes}
4661556Srgrimes