sigev_thread.c revision 330897
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice unmodified, this list of conditions, and the following
12 *    disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $FreeBSD: stable/11/lib/librt/sigev_thread.c 330897 2018-03-14 03:19:51Z eadler $
29 *
30 */
31
32#include <sys/types.h>
33
34#include "namespace.h"
35#include <err.h>
36#include <errno.h>
37#include <ucontext.h>
38#include <sys/thr.h>
39#include <stdatomic.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <signal.h>
44#include <pthread.h>
45#include "un-namespace.h"
46
47#include "sigev_thread.h"
48
49LIST_HEAD(sigev_list_head, sigev_node);
50#define HASH_QUEUES		17
51#define	HASH(t, id)		((((id) << 3) + (t)) % HASH_QUEUES)
52
53static struct sigev_list_head	sigev_hash[HASH_QUEUES];
54static struct sigev_list_head	sigev_all;
55static LIST_HEAD(,sigev_thread)	sigev_threads;
56static atomic_int		sigev_generation;
57static pthread_mutex_t		*sigev_list_mtx;
58static pthread_once_t		sigev_once = PTHREAD_ONCE_INIT;
59static pthread_once_t		sigev_once_default = PTHREAD_ONCE_INIT;
60static struct sigev_thread	*sigev_default_thread;
61static pthread_attr_t		sigev_default_attr;
62static int			atfork_registered;
63
64static void	__sigev_fork_prepare(void);
65static void	__sigev_fork_parent(void);
66static void	__sigev_fork_child(void);
67static struct sigev_thread	*sigev_thread_create(int);
68static void	*sigev_service_loop(void *);
69static void	*worker_routine(void *);
70static void	worker_cleanup(void *);
71
72#pragma weak _pthread_create
73
74static void
75attrcopy(pthread_attr_t *src, pthread_attr_t *dst)
76{
77	struct sched_param sched;
78	void *a;
79	size_t u;
80	int v;
81
82	_pthread_attr_getschedpolicy(src, &v);
83	_pthread_attr_setschedpolicy(dst, v);
84
85	_pthread_attr_getinheritsched(src, &v);
86	_pthread_attr_setinheritsched(dst, v);
87
88	_pthread_attr_getschedparam(src, &sched);
89	_pthread_attr_setschedparam(dst, &sched);
90
91	_pthread_attr_getscope(src, &v);
92	_pthread_attr_setscope(dst, v);
93
94	_pthread_attr_getstacksize(src, &u);
95	_pthread_attr_setstacksize(dst, u);
96
97	_pthread_attr_getstackaddr(src, &a);
98	_pthread_attr_setstackaddr(src, a);
99
100	_pthread_attr_getguardsize(src, &u);
101	_pthread_attr_setguardsize(dst, u);
102}
103
104static __inline int
105have_threads(void)
106{
107	return (&_pthread_create != NULL);
108}
109
110void
111__sigev_thread_init(void)
112{
113	static int inited = 0;
114	pthread_mutexattr_t mattr;
115	int i;
116
117	_pthread_mutexattr_init(&mattr);
118	_pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL);
119	sigev_list_mtx = malloc(sizeof(pthread_mutex_t));
120	_pthread_mutex_init(sigev_list_mtx, &mattr);
121	_pthread_mutexattr_destroy(&mattr);
122
123	for (i = 0; i < HASH_QUEUES; ++i)
124		LIST_INIT(&sigev_hash[i]);
125	LIST_INIT(&sigev_all);
126	LIST_INIT(&sigev_threads);
127	sigev_default_thread = NULL;
128	if (atfork_registered == 0) {
129		_pthread_atfork(
130			__sigev_fork_prepare,
131			__sigev_fork_parent,
132			__sigev_fork_child);
133		atfork_registered = 1;
134	}
135	if (!inited) {
136		_pthread_attr_init(&sigev_default_attr);
137		_pthread_attr_setscope(&sigev_default_attr,
138			PTHREAD_SCOPE_SYSTEM);
139		_pthread_attr_setdetachstate(&sigev_default_attr,
140			PTHREAD_CREATE_DETACHED);
141		inited = 1;
142	}
143	sigev_default_thread = sigev_thread_create(0);
144}
145
146int
147__sigev_check_init(void)
148{
149	if (!have_threads())
150		return (-1);
151
152	_pthread_once(&sigev_once, __sigev_thread_init);
153	return (sigev_default_thread != NULL) ? 0 : -1;
154}
155
156static void
157__sigev_fork_prepare(void)
158{
159}
160
161static void
162__sigev_fork_parent(void)
163{
164}
165
166static void
167__sigev_fork_child(void)
168{
169	/*
170	 * This is a hack, the thread libraries really should
171	 * check if the handlers were already registered in
172	 * pthread_atfork().
173	 */
174	atfork_registered = 1;
175	memcpy(&sigev_once, &sigev_once_default, sizeof(sigev_once));
176	__sigev_thread_init();
177}
178
179void
180__sigev_list_lock(void)
181{
182	_pthread_mutex_lock(sigev_list_mtx);
183}
184
185void
186__sigev_list_unlock(void)
187{
188	_pthread_mutex_unlock(sigev_list_mtx);
189}
190
191struct sigev_node *
192__sigev_alloc(int type, const struct sigevent *evp, struct sigev_node *prev,
193	int usedefault)
194{
195	struct sigev_node *sn;
196
197	sn = calloc(1, sizeof(*sn));
198	if (sn != NULL) {
199		sn->sn_value = evp->sigev_value;
200		sn->sn_func  = evp->sigev_notify_function;
201		sn->sn_gen   = atomic_fetch_add_explicit(&sigev_generation, 1,
202		    memory_order_relaxed);
203		sn->sn_type  = type;
204		_pthread_attr_init(&sn->sn_attr);
205		_pthread_attr_setdetachstate(&sn->sn_attr, PTHREAD_CREATE_DETACHED);
206		if (evp->sigev_notify_attributes)
207			attrcopy(evp->sigev_notify_attributes, &sn->sn_attr);
208		if (prev) {
209			__sigev_list_lock();
210			prev->sn_tn->tn_refcount++;
211			__sigev_list_unlock();
212			sn->sn_tn = prev->sn_tn;
213		} else {
214			sn->sn_tn = sigev_thread_create(usedefault);
215			if (sn->sn_tn == NULL) {
216				_pthread_attr_destroy(&sn->sn_attr);
217				free(sn);
218				sn = NULL;
219			}
220		}
221	}
222	return (sn);
223}
224
225void
226__sigev_get_sigevent(struct sigev_node *sn, struct sigevent *newevp,
227	sigev_id_t id)
228{
229	/*
230	 * Build a new sigevent, and tell kernel to deliver SIGLIBRT
231	 * signal to the new thread.
232	 */
233	newevp->sigev_notify = SIGEV_THREAD_ID;
234	newevp->sigev_signo  = SIGLIBRT;
235	newevp->sigev_notify_thread_id = (lwpid_t)sn->sn_tn->tn_lwpid;
236	newevp->sigev_value.sival_ptr = (void *)id;
237}
238
239void
240__sigev_free(struct sigev_node *sn)
241{
242	_pthread_attr_destroy(&sn->sn_attr);
243	free(sn);
244}
245
246struct sigev_node *
247__sigev_find(int type, sigev_id_t id)
248{
249	struct sigev_node *sn;
250	int chain = HASH(type, id);
251
252	LIST_FOREACH(sn, &sigev_hash[chain], sn_link) {
253		if (sn->sn_type == type && sn->sn_id == id)
254			break;
255	}
256	return (sn);
257}
258
259int
260__sigev_register(struct sigev_node *sn)
261{
262	int chain = HASH(sn->sn_type, sn->sn_id);
263
264	LIST_INSERT_HEAD(&sigev_hash[chain], sn, sn_link);
265	return (0);
266}
267
268int
269__sigev_delete(int type, sigev_id_t id)
270{
271	struct sigev_node *sn;
272
273	sn = __sigev_find(type, id);
274	if (sn != NULL)
275		return (__sigev_delete_node(sn));
276	return (0);
277}
278
279int
280__sigev_delete_node(struct sigev_node *sn)
281{
282	LIST_REMOVE(sn, sn_link);
283
284	if (--sn->sn_tn->tn_refcount == 0)
285		_pthread_kill(sn->sn_tn->tn_thread, SIGLIBRT);
286	if (sn->sn_flags & SNF_WORKING)
287		sn->sn_flags |= SNF_REMOVED;
288	else
289		__sigev_free(sn);
290	return (0);
291}
292
293static sigev_id_t
294sigev_get_id(siginfo_t *si)
295{
296	switch(si->si_code) {
297	case SI_TIMER:
298		return (si->si_timerid);
299	case SI_MESGQ:
300		return (si->si_mqd);
301	case SI_ASYNCIO:
302		return (sigev_id_t)si->si_value.sival_ptr;
303	}
304	return (-1);
305}
306
307static struct sigev_thread *
308sigev_thread_create(int usedefault)
309{
310	struct sigev_thread *tn;
311	sigset_t set, oset;
312	int ret;
313
314	if (usedefault && sigev_default_thread) {
315		__sigev_list_lock();
316		sigev_default_thread->tn_refcount++;
317		__sigev_list_unlock();
318		return (sigev_default_thread);
319	}
320
321	tn = malloc(sizeof(*tn));
322	tn->tn_cur = NULL;
323	tn->tn_lwpid = -1;
324	tn->tn_refcount = 1;
325	_pthread_cond_init(&tn->tn_cv, NULL);
326
327	/* for debug */
328	__sigev_list_lock();
329	LIST_INSERT_HEAD(&sigev_threads, tn, tn_link);
330	__sigev_list_unlock();
331
332	sigfillset(&set);	/* SIGLIBRT is masked. */
333	sigdelset(&set, SIGBUS);
334	sigdelset(&set, SIGILL);
335	sigdelset(&set, SIGFPE);
336	sigdelset(&set, SIGSEGV);
337	sigdelset(&set, SIGTRAP);
338	_sigprocmask(SIG_SETMASK, &set, &oset);
339	ret = _pthread_create(&tn->tn_thread, &sigev_default_attr,
340		 sigev_service_loop, tn);
341	_sigprocmask(SIG_SETMASK, &oset, NULL);
342
343	if (ret != 0) {
344		__sigev_list_lock();
345		LIST_REMOVE(tn, tn_link);
346		__sigev_list_unlock();
347		free(tn);
348		tn = NULL;
349	} else {
350		/* wait the thread to get its lwpid */
351
352		__sigev_list_lock();
353		while (tn->tn_lwpid == -1)
354			_pthread_cond_wait(&tn->tn_cv, sigev_list_mtx);
355		__sigev_list_unlock();
356	}
357	return (tn);
358}
359
360/*
361 * The thread receives notification from kernel and creates
362 * a thread to call user callback function.
363 */
364static void *
365sigev_service_loop(void *arg)
366{
367	static int failure;
368
369	siginfo_t si;
370	sigset_t set;
371	struct sigev_thread *tn;
372	struct sigev_node *sn;
373	sigev_id_t id;
374	pthread_t td;
375	int ret;
376
377	tn = arg;
378	thr_self(&tn->tn_lwpid);
379	__sigev_list_lock();
380	_pthread_cond_broadcast(&tn->tn_cv);
381	__sigev_list_unlock();
382
383	sigemptyset(&set);
384	sigaddset(&set, SIGLIBRT);
385	for (;;) {
386		ret = sigwaitinfo(&set, &si);
387
388		__sigev_list_lock();
389		if (tn->tn_refcount == 0) {
390			LIST_REMOVE(tn, tn_link);
391			__sigev_list_unlock();
392			free(tn);
393			break;
394		}
395
396		if (ret == -1) {
397			__sigev_list_unlock();
398			continue;
399		}
400
401		id = sigev_get_id(&si);
402		sn = __sigev_find(si.si_code, id);
403		if (sn == NULL) {
404			__sigev_list_unlock();
405			continue;
406		}
407
408		sn->sn_info = si;
409		if (sn->sn_flags & SNF_SYNC)
410			tn->tn_cur = sn;
411		else
412			tn->tn_cur = NULL;
413		sn->sn_flags |= SNF_WORKING;
414		__sigev_list_unlock();
415
416		ret = _pthread_create(&td, &sn->sn_attr, worker_routine, sn);
417		if (ret != 0) {
418			if (failure++ < 5)
419				warnc(ret, "%s:%s failed to create thread.\n",
420					__FILE__, __func__);
421
422			__sigev_list_lock();
423			sn->sn_flags &= ~SNF_WORKING;
424			if (sn->sn_flags & SNF_REMOVED)
425				__sigev_free(sn);
426			__sigev_list_unlock();
427		} else if (tn->tn_cur) {
428			__sigev_list_lock();
429			while (tn->tn_cur)
430				_pthread_cond_wait(&tn->tn_cv, sigev_list_mtx);
431			__sigev_list_unlock();
432		}
433	}
434	return (0);
435}
436
437/*
438 * newly created worker thread to call user callback function.
439 */
440static void *
441worker_routine(void *arg)
442{
443	struct sigev_node *sn = arg;
444
445	pthread_cleanup_push(worker_cleanup, sn);
446	sn->sn_dispatch(sn);
447	pthread_cleanup_pop(1);
448
449	return (0);
450}
451
452/* clean up a notification after dispatch. */
453static void
454worker_cleanup(void *arg)
455{
456	struct sigev_node *sn = arg;
457
458	__sigev_list_lock();
459	if (sn->sn_flags & SNF_SYNC) {
460		sn->sn_tn->tn_cur = NULL;
461		_pthread_cond_broadcast(&sn->sn_tn->tn_cv);
462	}
463	if (sn->sn_flags & SNF_REMOVED)
464		__sigev_free(sn);
465	else
466		sn->sn_flags &= ~SNF_WORKING;
467	__sigev_list_unlock();
468}
469