1*9034ec65Schristos /*	$NetBSD: work_thread.c,v 1.8 2020/05/25 20:47:24 christos Exp $	*/
22b3787f6Schristos 
32b3787f6Schristos /*
42b3787f6Schristos  * work_thread.c - threads implementation for blocking worker child.
52b3787f6Schristos  */
62b3787f6Schristos #include <config.h>
72b3787f6Schristos #include "ntp_workimpl.h"
82b3787f6Schristos 
92b3787f6Schristos #ifdef WORK_THREAD
102b3787f6Schristos 
112b3787f6Schristos #include <stdio.h>
122b3787f6Schristos #include <ctype.h>
132b3787f6Schristos #include <signal.h>
142b3787f6Schristos #ifndef SYS_WINNT
152b3787f6Schristos #include <pthread.h>
162b3787f6Schristos #endif
172b3787f6Schristos 
182b3787f6Schristos #include "ntp_stdlib.h"
192b3787f6Schristos #include "ntp_malloc.h"
202b3787f6Schristos #include "ntp_syslog.h"
212b3787f6Schristos #include "ntpd.h"
222b3787f6Schristos #include "ntp_io.h"
232b3787f6Schristos #include "ntp_assert.h"
242b3787f6Schristos #include "ntp_unixtime.h"
252b3787f6Schristos #include "timespecops.h"
262b3787f6Schristos #include "ntp_worker.h"
272b3787f6Schristos 
282b3787f6Schristos #define CHILD_EXIT_REQ	((blocking_pipe_header *)(intptr_t)-1)
292b3787f6Schristos #define CHILD_GONE_RESP	CHILD_EXIT_REQ
30335f7552Schristos /* Queue size increments:
31335f7552Schristos  * The request queue grows a bit faster than the response queue -- the
32169394abSchristos  * daemon can push requests and pull results faster on avarage than the
33335f7552Schristos  * worker can process requests and push results...  If this really pays
34335f7552Schristos  * off is debatable.
35335f7552Schristos  */
362b3787f6Schristos #define WORKITEMS_ALLOC_INC	16
372b3787f6Schristos #define RESPONSES_ALLOC_INC	4
382b3787f6Schristos 
39335f7552Schristos /* Fiddle with min/max stack sizes. 64kB minimum seems to work, so we
40335f7552Schristos  * set the maximum to 256kB. If the minimum goes below the
41335f7552Schristos  * system-defined minimum stack size, we have to adjust accordingly.
42335f7552Schristos  */
432b3787f6Schristos #ifndef THREAD_MINSTACKSIZE
442b3787f6Schristos # define THREAD_MINSTACKSIZE	(64U * 1024)
452b3787f6Schristos #endif
46335f7552Schristos #ifndef __sun
47335f7552Schristos #if defined(PTHREAD_STACK_MIN) && THREAD_MINSTACKSIZE < PTHREAD_STACK_MIN
48335f7552Schristos # undef THREAD_MINSTACKSIZE
49335f7552Schristos # define THREAD_MINSTACKSIZE PTHREAD_STACK_MIN
50335f7552Schristos #endif
51335f7552Schristos #endif
52335f7552Schristos 
53335f7552Schristos #ifndef THREAD_MAXSTACKSIZE
54335f7552Schristos # define THREAD_MAXSTACKSIZE	(256U * 1024)
55335f7552Schristos #endif
56335f7552Schristos #if THREAD_MAXSTACKSIZE < THREAD_MINSTACKSIZE
57335f7552Schristos # undef  THREAD_MAXSTACKSIZE
58335f7552Schristos # define THREAD_MAXSTACKSIZE THREAD_MINSTACKSIZE
59335f7552Schristos #endif
60335f7552Schristos 
619e1d19ccSchristos /* need a good integer to store a pointer... */
629e1d19ccSchristos #ifndef UINTPTR_T
639e1d19ccSchristos # if defined(UINTPTR_MAX)
649e1d19ccSchristos #  define UINTPTR_T uintptr_t
659e1d19ccSchristos # elif defined(UINT_PTR)
669e1d19ccSchristos #  define UINTPTR_T UINT_PTR
679e1d19ccSchristos # else
689e1d19ccSchristos #  define UINTPTR_T size_t
699e1d19ccSchristos # endif
709e1d19ccSchristos #endif
719e1d19ccSchristos 
722b3787f6Schristos 
732b3787f6Schristos #ifdef SYS_WINNT
74bd25f4c4Schristos 
752b3787f6Schristos # define thread_exit(c)	_endthreadex(c)
76bd25f4c4Schristos # define tickle_sem(sh) ReleaseSemaphore((sh->shnd), 1, NULL)
77bd25f4c4Schristos u_int	WINAPI	blocking_thread(void *);
78bd25f4c4Schristos static BOOL	same_os_sema(const sem_ref obj, void * osobj);
79bd25f4c4Schristos 
802b3787f6Schristos #else
81bd25f4c4Schristos 
829e1d19ccSchristos # define thread_exit(c)	pthread_exit((void*)(UINTPTR_T)(c))
832b3787f6Schristos # define tickle_sem	sem_post
84bd25f4c4Schristos void *		blocking_thread(void *);
85bd25f4c4Schristos static	void	block_thread_signals(sigset_t *);
86bd25f4c4Schristos 
872b3787f6Schristos #endif
882b3787f6Schristos 
892b3787f6Schristos #ifdef WORK_PIPE
902b3787f6Schristos addremove_io_fd_func		addremove_io_fd;
912b3787f6Schristos #else
922b3787f6Schristos addremove_io_semaphore_func	addremove_io_semaphore;
932b3787f6Schristos #endif
942b3787f6Schristos 
952b3787f6Schristos static	void	start_blocking_thread(blocking_child *);
962b3787f6Schristos static	void	start_blocking_thread_internal(blocking_child *);
972b3787f6Schristos static	void	prepare_child_sems(blocking_child *);
982b3787f6Schristos static	int	wait_for_sem(sem_ref, struct timespec *);
99bd25f4c4Schristos static	int	ensure_workitems_empty_slot(blocking_child *);
100bd25f4c4Schristos static	int	ensure_workresp_empty_slot(blocking_child *);
1012b3787f6Schristos static	int	queue_req_pointer(blocking_child *, blocking_pipe_header *);
1022b3787f6Schristos static	void	cleanup_after_child(blocking_child *);
1032b3787f6Schristos 
104335f7552Schristos static sema_type worker_mmutex;
105335f7552Schristos static sem_ref   worker_memlock;
1062b3787f6Schristos 
107335f7552Schristos /* --------------------------------------------------------------------
108335f7552Schristos  * locking the global worker state table (and other global stuff)
109335f7552Schristos  */
110335f7552Schristos void
worker_global_lock(int inOrOut)111335f7552Schristos worker_global_lock(
112335f7552Schristos 	int inOrOut)
113335f7552Schristos {
114335f7552Schristos 	if (worker_memlock) {
115335f7552Schristos 		if (inOrOut)
116335f7552Schristos 			wait_for_sem(worker_memlock, NULL);
117335f7552Schristos 		else
118335f7552Schristos 			tickle_sem(worker_memlock);
119335f7552Schristos 	}
120335f7552Schristos }
121335f7552Schristos 
122335f7552Schristos /* --------------------------------------------------------------------
123335f7552Schristos  * implementation isolation wrapper
124335f7552Schristos  */
1252b3787f6Schristos void
exit_worker(int exitcode)1262b3787f6Schristos exit_worker(
1272b3787f6Schristos 	int	exitcode
1282b3787f6Schristos 	)
1292b3787f6Schristos {
1302b3787f6Schristos 	thread_exit(exitcode);	/* see #define thread_exit */
1312b3787f6Schristos }
1322b3787f6Schristos 
133bd25f4c4Schristos /* --------------------------------------------------------------------
134bd25f4c4Schristos  * sleep for a given time or until the wakup semaphore is tickled.
135bd25f4c4Schristos  */
1362b3787f6Schristos int
worker_sleep(blocking_child * c,time_t seconds)1372b3787f6Schristos worker_sleep(
1382b3787f6Schristos 	blocking_child *	c,
1392b3787f6Schristos 	time_t			seconds
1402b3787f6Schristos 	)
1412b3787f6Schristos {
1422b3787f6Schristos 	struct timespec	until;
1432b3787f6Schristos 	int		rc;
1442b3787f6Schristos 
1452b3787f6Schristos # ifdef HAVE_CLOCK_GETTIME
1462b3787f6Schristos 	if (0 != clock_gettime(CLOCK_REALTIME, &until)) {
1472b3787f6Schristos 		msyslog(LOG_ERR, "worker_sleep: clock_gettime() failed: %m");
1482b3787f6Schristos 		return -1;
1492b3787f6Schristos 	}
1502b3787f6Schristos # else
1512b3787f6Schristos 	if (0 != getclock(TIMEOFDAY, &until)) {
1522b3787f6Schristos 		msyslog(LOG_ERR, "worker_sleep: getclock() failed: %m");
1532b3787f6Schristos 		return -1;
1542b3787f6Schristos 	}
1552b3787f6Schristos # endif
1562b3787f6Schristos 	until.tv_sec += seconds;
1572b3787f6Schristos 	rc = wait_for_sem(c->wake_scheduled_sleep, &until);
1582b3787f6Schristos 	if (0 == rc)
1592b3787f6Schristos 		return -1;
1602b3787f6Schristos 	if (-1 == rc && ETIMEDOUT == errno)
1612b3787f6Schristos 		return 0;
1622b3787f6Schristos 	msyslog(LOG_ERR, "worker_sleep: sem_timedwait: %m");
1632b3787f6Schristos 	return -1;
1642b3787f6Schristos }
1652b3787f6Schristos 
1662b3787f6Schristos 
167bd25f4c4Schristos /* --------------------------------------------------------------------
168bd25f4c4Schristos  * Wake up a worker that takes a nap.
169bd25f4c4Schristos  */
1702b3787f6Schristos void
interrupt_worker_sleep(void)1712b3787f6Schristos interrupt_worker_sleep(void)
1722b3787f6Schristos {
1732b3787f6Schristos 	u_int			idx;
1742b3787f6Schristos 	blocking_child *	c;
1752b3787f6Schristos 
1762b3787f6Schristos 	for (idx = 0; idx < blocking_children_alloc; idx++) {
1772b3787f6Schristos 		c = blocking_children[idx];
1782b3787f6Schristos 		if (NULL == c || NULL == c->wake_scheduled_sleep)
1792b3787f6Schristos 			continue;
1802b3787f6Schristos 		tickle_sem(c->wake_scheduled_sleep);
1812b3787f6Schristos 	}
1822b3787f6Schristos }
1832b3787f6Schristos 
184bd25f4c4Schristos /* --------------------------------------------------------------------
185bd25f4c4Schristos  * Make sure there is an empty slot at the head of the request
186bd25f4c4Schristos  * queue. Tell if the queue is currently empty.
187bd25f4c4Schristos  */
188bd25f4c4Schristos static int
ensure_workitems_empty_slot(blocking_child * c)1892b3787f6Schristos ensure_workitems_empty_slot(
1902b3787f6Schristos 	blocking_child *c
1912b3787f6Schristos 	)
1922b3787f6Schristos {
193bd25f4c4Schristos 	/*
194bd25f4c4Schristos 	** !!! PRECONDITION: caller holds access lock!
195bd25f4c4Schristos 	**
196bd25f4c4Schristos 	** This simply tries to increase the size of the buffer if it
197bd25f4c4Schristos 	** becomes full. The resize operation does *not* maintain the
198bd25f4c4Schristos 	** order of requests, but that should be irrelevant since the
199bd25f4c4Schristos 	** processing is considered asynchronous anyway.
200bd25f4c4Schristos 	**
201bd25f4c4Schristos 	** Return if the buffer is currently empty.
202bd25f4c4Schristos 	*/
203bd25f4c4Schristos 
204bd25f4c4Schristos 	static const size_t each =
205bd25f4c4Schristos 	    sizeof(blocking_children[0]->workitems[0]);
206bd25f4c4Schristos 
2072b3787f6Schristos 	size_t	new_alloc;
208bd25f4c4Schristos 	size_t  slots_used;
209335f7552Schristos 	size_t	sidx;
2102b3787f6Schristos 
211bd25f4c4Schristos 	slots_used = c->head_workitem - c->tail_workitem;
212bd25f4c4Schristos 	if (slots_used >= c->workitems_alloc) {
2132b3787f6Schristos 		new_alloc  = c->workitems_alloc + WORKITEMS_ALLOC_INC;
214bd25f4c4Schristos 		c->workitems = erealloc(c->workitems, new_alloc * each);
215335f7552Schristos 		for (sidx = c->workitems_alloc; sidx < new_alloc; ++sidx)
216335f7552Schristos 		    c->workitems[sidx] = NULL;
217bd25f4c4Schristos 		c->tail_workitem   = 0;
218bd25f4c4Schristos 		c->head_workitem   = c->workitems_alloc;
2192b3787f6Schristos 		c->workitems_alloc = new_alloc;
2202b3787f6Schristos 	}
221335f7552Schristos 	INSIST(NULL == c->workitems[c->head_workitem % c->workitems_alloc]);
222bd25f4c4Schristos 	return (0 == slots_used);
223bd25f4c4Schristos }
2242b3787f6Schristos 
225bd25f4c4Schristos /* --------------------------------------------------------------------
226bd25f4c4Schristos  * Make sure there is an empty slot at the head of the response
227bd25f4c4Schristos  * queue. Tell if the queue is currently empty.
228bd25f4c4Schristos  */
229bd25f4c4Schristos static int
ensure_workresp_empty_slot(blocking_child * c)2302b3787f6Schristos ensure_workresp_empty_slot(
2312b3787f6Schristos 	blocking_child *c
2322b3787f6Schristos 	)
2332b3787f6Schristos {
234bd25f4c4Schristos 	/*
235bd25f4c4Schristos 	** !!! PRECONDITION: caller holds access lock!
236bd25f4c4Schristos 	**
237bd25f4c4Schristos 	** Works like the companion function above.
238bd25f4c4Schristos 	*/
239bd25f4c4Schristos 
240bd25f4c4Schristos 	static const size_t each =
241bd25f4c4Schristos 	    sizeof(blocking_children[0]->responses[0]);
242bd25f4c4Schristos 
2432b3787f6Schristos 	size_t	new_alloc;
244bd25f4c4Schristos 	size_t  slots_used;
245335f7552Schristos 	size_t	sidx;
2462b3787f6Schristos 
247bd25f4c4Schristos 	slots_used = c->head_response - c->tail_response;
248bd25f4c4Schristos 	if (slots_used >= c->responses_alloc) {
2492b3787f6Schristos 		new_alloc  = c->responses_alloc + RESPONSES_ALLOC_INC;
250bd25f4c4Schristos 		c->responses = erealloc(c->responses, new_alloc * each);
251335f7552Schristos 		for (sidx = c->responses_alloc; sidx < new_alloc; ++sidx)
252335f7552Schristos 		    c->responses[sidx] = NULL;
253bd25f4c4Schristos 		c->tail_response   = 0;
254bd25f4c4Schristos 		c->head_response   = c->responses_alloc;
2552b3787f6Schristos 		c->responses_alloc = new_alloc;
2562b3787f6Schristos 	}
257335f7552Schristos 	INSIST(NULL == c->responses[c->head_response % c->responses_alloc]);
258bd25f4c4Schristos 	return (0 == slots_used);
259bd25f4c4Schristos }
2602b3787f6Schristos 
2612b3787f6Schristos 
262bd25f4c4Schristos /* --------------------------------------------------------------------
2632b3787f6Schristos  * queue_req_pointer() - append a work item or idle exit request to
264bd25f4c4Schristos  *			 blocking_workitems[]. Employ proper locking.
2652b3787f6Schristos  */
2662b3787f6Schristos static int
queue_req_pointer(blocking_child * c,blocking_pipe_header * hdr)2672b3787f6Schristos queue_req_pointer(
2682b3787f6Schristos 	blocking_child	*	c,
2692b3787f6Schristos 	blocking_pipe_header *	hdr
2702b3787f6Schristos 	)
2712b3787f6Schristos {
272bd25f4c4Schristos 	size_t qhead;
2732b3787f6Schristos 
274bd25f4c4Schristos 	/* >>>> ACCESS LOCKING STARTS >>>> */
275bd25f4c4Schristos 	wait_for_sem(c->accesslock, NULL);
276bd25f4c4Schristos 	ensure_workitems_empty_slot(c);
277bd25f4c4Schristos 	qhead = c->head_workitem;
278bd25f4c4Schristos 	c->workitems[qhead % c->workitems_alloc] = hdr;
279bd25f4c4Schristos 	c->head_workitem = 1 + qhead;
280bd25f4c4Schristos 	tickle_sem(c->accesslock);
281bd25f4c4Schristos 	/* <<<< ACCESS LOCKING ENDS <<<< */
282bd25f4c4Schristos 
283bd25f4c4Schristos 	/* queue consumer wake-up notification */
284bd25f4c4Schristos 	tickle_sem(c->workitems_pending);
2852b3787f6Schristos 
2862b3787f6Schristos 	return 0;
2872b3787f6Schristos }
2882b3787f6Schristos 
289bd25f4c4Schristos /* --------------------------------------------------------------------
290bd25f4c4Schristos  * API function to make sure a worker is running, a proper private copy
291bd25f4c4Schristos  * of the data is made, the data eneterd into the queue and the worker
292bd25f4c4Schristos  * is signalled.
293bd25f4c4Schristos  */
2942b3787f6Schristos int
send_blocking_req_internal(blocking_child * c,blocking_pipe_header * hdr,void * data)2952b3787f6Schristos send_blocking_req_internal(
2962b3787f6Schristos 	blocking_child *	c,
2972b3787f6Schristos 	blocking_pipe_header *	hdr,
2982b3787f6Schristos 	void *			data
2992b3787f6Schristos 	)
3002b3787f6Schristos {
3012b3787f6Schristos 	blocking_pipe_header *	threadcopy;
3022b3787f6Schristos 	size_t			payload_octets;
3032b3787f6Schristos 
3042b3787f6Schristos 	REQUIRE(hdr != NULL);
3052b3787f6Schristos 	REQUIRE(data != NULL);
3062b3787f6Schristos 	DEBUG_REQUIRE(BLOCKING_REQ_MAGIC == hdr->magic_sig);
3072b3787f6Schristos 
3082b3787f6Schristos 	if (hdr->octets <= sizeof(*hdr))
3092b3787f6Schristos 		return 1;	/* failure */
3102b3787f6Schristos 	payload_octets = hdr->octets - sizeof(*hdr);
3112b3787f6Schristos 
312bd25f4c4Schristos 	if (NULL == c->thread_ref)
3132b3787f6Schristos 		start_blocking_thread(c);
3142b3787f6Schristos 	threadcopy = emalloc(hdr->octets);
3152b3787f6Schristos 	memcpy(threadcopy, hdr, sizeof(*hdr));
3162b3787f6Schristos 	memcpy((char *)threadcopy + sizeof(*hdr), data, payload_octets);
3172b3787f6Schristos 
3182b3787f6Schristos 	return queue_req_pointer(c, threadcopy);
3192b3787f6Schristos }
3202b3787f6Schristos 
321bd25f4c4Schristos /* --------------------------------------------------------------------
322bd25f4c4Schristos  * Wait for the 'incoming queue no longer empty' signal, lock the shared
323bd25f4c4Schristos  * structure and dequeue an item.
324bd25f4c4Schristos  */
3252b3787f6Schristos blocking_pipe_header *
receive_blocking_req_internal(blocking_child * c)3262b3787f6Schristos receive_blocking_req_internal(
3272b3787f6Schristos 	blocking_child *	c
3282b3787f6Schristos 	)
3292b3787f6Schristos {
3302b3787f6Schristos 	blocking_pipe_header *	req;
331bd25f4c4Schristos 	size_t			qhead, qtail;
3322b3787f6Schristos 
333bd25f4c4Schristos 	req = NULL;
3342b3787f6Schristos 	do {
335bd25f4c4Schristos 		/* wait for tickle from the producer side */
336bd25f4c4Schristos 		wait_for_sem(c->workitems_pending, NULL);
3372b3787f6Schristos 
338bd25f4c4Schristos 		/* >>>> ACCESS LOCKING STARTS >>>> */
339bd25f4c4Schristos 		wait_for_sem(c->accesslock, NULL);
340bd25f4c4Schristos 		qhead = c->head_workitem;
341bd25f4c4Schristos 		do {
342bd25f4c4Schristos 			qtail = c->tail_workitem;
343bd25f4c4Schristos 			if (qhead == qtail)
344bd25f4c4Schristos 				break;
345bd25f4c4Schristos 			c->tail_workitem = qtail + 1;
346bd25f4c4Schristos 			qtail %= c->workitems_alloc;
347bd25f4c4Schristos 			req = c->workitems[qtail];
348bd25f4c4Schristos 			c->workitems[qtail] = NULL;
349bd25f4c4Schristos 		} while (NULL == req);
350bd25f4c4Schristos 		tickle_sem(c->accesslock);
351bd25f4c4Schristos 		/* <<<< ACCESS LOCKING ENDS <<<< */
352bd25f4c4Schristos 
353bd25f4c4Schristos 	} while (NULL == req);
354bd25f4c4Schristos 
3552b3787f6Schristos 	INSIST(NULL != req);
3562b3787f6Schristos 	if (CHILD_EXIT_REQ == req) {	/* idled out */
3572b3787f6Schristos 		send_blocking_resp_internal(c, CHILD_GONE_RESP);
3582b3787f6Schristos 		req = NULL;
3592b3787f6Schristos 	}
3602b3787f6Schristos 
3612b3787f6Schristos 	return req;
3622b3787f6Schristos }
3632b3787f6Schristos 
364bd25f4c4Schristos /* --------------------------------------------------------------------
365bd25f4c4Schristos  * Push a response into the return queue and eventually tickle the
366bd25f4c4Schristos  * receiver.
367bd25f4c4Schristos  */
3682b3787f6Schristos int
send_blocking_resp_internal(blocking_child * c,blocking_pipe_header * resp)3692b3787f6Schristos send_blocking_resp_internal(
3702b3787f6Schristos 	blocking_child *	c,
3712b3787f6Schristos 	blocking_pipe_header *	resp
3722b3787f6Schristos 	)
3732b3787f6Schristos {
374bd25f4c4Schristos 	size_t	qhead;
375bd25f4c4Schristos 	int	empty;
3762b3787f6Schristos 
377bd25f4c4Schristos 	/* >>>> ACCESS LOCKING STARTS >>>> */
378bd25f4c4Schristos 	wait_for_sem(c->accesslock, NULL);
379bd25f4c4Schristos 	empty = ensure_workresp_empty_slot(c);
380bd25f4c4Schristos 	qhead = c->head_response;
381bd25f4c4Schristos 	c->responses[qhead % c->responses_alloc] = resp;
382bd25f4c4Schristos 	c->head_response = 1 + qhead;
383bd25f4c4Schristos 	tickle_sem(c->accesslock);
384bd25f4c4Schristos 	/* <<<< ACCESS LOCKING ENDS <<<< */
3852b3787f6Schristos 
386bd25f4c4Schristos 	/* queue consumer wake-up notification */
387bd25f4c4Schristos 	if (empty)
388bd25f4c4Schristos 	{
3892b3787f6Schristos #	    ifdef WORK_PIPE
3909e1d19ccSchristos 		if (1 != write(c->resp_write_pipe, "", 1))
3919e1d19ccSchristos 			msyslog(LOG_WARNING, "async resolver: %s",
3929e1d19ccSchristos 				"failed to notify main thread!");
3932b3787f6Schristos #	    else
394bd25f4c4Schristos 		tickle_sem(c->responses_pending);
3952b3787f6Schristos #	    endif
396bd25f4c4Schristos 	}
3972b3787f6Schristos 	return 0;
3982b3787f6Schristos }
3992b3787f6Schristos 
4002b3787f6Schristos 
4012b3787f6Schristos #ifndef WORK_PIPE
402bd25f4c4Schristos 
403bd25f4c4Schristos /* --------------------------------------------------------------------
404bd25f4c4Schristos  * Check if a (Windows-)hanndle to a semaphore is actually the same we
405bd25f4c4Schristos  * are using inside the sema wrapper.
406bd25f4c4Schristos  */
407bd25f4c4Schristos static BOOL
same_os_sema(const sem_ref obj,void * osh)408bd25f4c4Schristos same_os_sema(
409bd25f4c4Schristos 	const sem_ref	obj,
410bd25f4c4Schristos 	void*		osh
411bd25f4c4Schristos 	)
412bd25f4c4Schristos {
413bd25f4c4Schristos 	return obj && osh && (obj->shnd == (HANDLE)osh);
414bd25f4c4Schristos }
415bd25f4c4Schristos 
416bd25f4c4Schristos /* --------------------------------------------------------------------
417bd25f4c4Schristos  * Find the shared context that associates to an OS handle and make sure
418bd25f4c4Schristos  * the data is dequeued and processed.
419bd25f4c4Schristos  */
4202b3787f6Schristos void
handle_blocking_resp_sem(void * context)4212b3787f6Schristos handle_blocking_resp_sem(
4222b3787f6Schristos 	void *	context
4232b3787f6Schristos 	)
4242b3787f6Schristos {
4252b3787f6Schristos 	blocking_child *	c;
4262b3787f6Schristos 	u_int			idx;
4272b3787f6Schristos 
4282b3787f6Schristos 	c = NULL;
4292b3787f6Schristos 	for (idx = 0; idx < blocking_children_alloc; idx++) {
4302b3787f6Schristos 		c = blocking_children[idx];
431bd25f4c4Schristos 		if (c != NULL &&
432bd25f4c4Schristos 			c->thread_ref != NULL &&
433bd25f4c4Schristos 			same_os_sema(c->responses_pending, context))
4342b3787f6Schristos 			break;
4352b3787f6Schristos 	}
4362b3787f6Schristos 	if (idx < blocking_children_alloc)
4372b3787f6Schristos 		process_blocking_resp(c);
4382b3787f6Schristos }
4392b3787f6Schristos #endif	/* !WORK_PIPE */
4402b3787f6Schristos 
441bd25f4c4Schristos /* --------------------------------------------------------------------
442bd25f4c4Schristos  * Fetch the next response from the return queue. In case of signalling
443bd25f4c4Schristos  * via pipe, make sure the pipe is flushed, too.
444bd25f4c4Schristos  */
4452b3787f6Schristos blocking_pipe_header *
receive_blocking_resp_internal(blocking_child * c)4462b3787f6Schristos receive_blocking_resp_internal(
4472b3787f6Schristos 	blocking_child *	c
4482b3787f6Schristos 	)
4492b3787f6Schristos {
4502b3787f6Schristos 	blocking_pipe_header *	removed;
451bd25f4c4Schristos 	size_t			qhead, qtail, slot;
452bd25f4c4Schristos 
4532b3787f6Schristos #ifdef WORK_PIPE
4542b3787f6Schristos 	int			rc;
4552b3787f6Schristos 	char			scratch[32];
4562b3787f6Schristos 
457bd25f4c4Schristos 	do
4582b3787f6Schristos 		rc = read(c->resp_read_pipe, scratch, sizeof(scratch));
459bd25f4c4Schristos 	while (-1 == rc && EINTR == errno);
4602b3787f6Schristos #endif
461bd25f4c4Schristos 
462bd25f4c4Schristos 	/* >>>> ACCESS LOCKING STARTS >>>> */
463bd25f4c4Schristos 	wait_for_sem(c->accesslock, NULL);
464bd25f4c4Schristos 	qhead = c->head_response;
465bd25f4c4Schristos 	qtail = c->tail_response;
466bd25f4c4Schristos 	for (removed = NULL; !removed && (qhead != qtail); ++qtail) {
467bd25f4c4Schristos 		slot = qtail % c->responses_alloc;
468bd25f4c4Schristos 		removed = c->responses[slot];
469bd25f4c4Schristos 		c->responses[slot] = NULL;
470bd25f4c4Schristos 	}
471bd25f4c4Schristos 	c->tail_response = qtail;
472bd25f4c4Schristos 	tickle_sem(c->accesslock);
473bd25f4c4Schristos 	/* <<<< ACCESS LOCKING ENDS <<<< */
474bd25f4c4Schristos 
4752b3787f6Schristos 	if (NULL != removed) {
4762b3787f6Schristos 		DEBUG_ENSURE(CHILD_GONE_RESP == removed ||
4772b3787f6Schristos 			     BLOCKING_RESP_MAGIC == removed->magic_sig);
4782b3787f6Schristos 	}
4792b3787f6Schristos 	if (CHILD_GONE_RESP == removed) {
4802b3787f6Schristos 		cleanup_after_child(c);
4812b3787f6Schristos 		removed = NULL;
4822b3787f6Schristos 	}
4832b3787f6Schristos 
4842b3787f6Schristos 	return removed;
4852b3787f6Schristos }
4862b3787f6Schristos 
487bd25f4c4Schristos /* --------------------------------------------------------------------
488bd25f4c4Schristos  * Light up a new worker.
489bd25f4c4Schristos  */
4902b3787f6Schristos static void
start_blocking_thread(blocking_child * c)4912b3787f6Schristos start_blocking_thread(
4922b3787f6Schristos 	blocking_child *	c
4932b3787f6Schristos 	)
4942b3787f6Schristos {
4952b3787f6Schristos 
4962b3787f6Schristos 	DEBUG_INSIST(!c->reusable);
4972b3787f6Schristos 
4982b3787f6Schristos 	prepare_child_sems(c);
4992b3787f6Schristos 	start_blocking_thread_internal(c);
5002b3787f6Schristos }
5012b3787f6Schristos 
502bd25f4c4Schristos /* --------------------------------------------------------------------
503bd25f4c4Schristos  * Create a worker thread. There are several differences between POSIX
504bd25f4c4Schristos  * and Windows, of course -- most notably the Windows thread is no
505bd25f4c4Schristos  * detached thread, and we keep the handle around until we want to get
506bd25f4c4Schristos  * rid of the thread. The notification scheme also differs: Windows
507bd25f4c4Schristos  * makes use of semaphores in both directions, POSIX uses a pipe for
508bd25f4c4Schristos  * integration with 'select()' or alike.
509bd25f4c4Schristos  */
5102b3787f6Schristos static void
start_blocking_thread_internal(blocking_child * c)5112b3787f6Schristos start_blocking_thread_internal(
5122b3787f6Schristos 	blocking_child *	c
5132b3787f6Schristos 	)
5142b3787f6Schristos #ifdef SYS_WINNT
5152b3787f6Schristos {
5162b3787f6Schristos 	BOOL	resumed;
5172b3787f6Schristos 
518bd25f4c4Schristos 	c->thread_ref = NULL;
519bd25f4c4Schristos 	(*addremove_io_semaphore)(c->responses_pending->shnd, FALSE);
520bd25f4c4Schristos 	c->thr_table[0].thnd =
5212b3787f6Schristos 		(HANDLE)_beginthreadex(
5222b3787f6Schristos 			NULL,
5232b3787f6Schristos 			0,
5242b3787f6Schristos 			&blocking_thread,
5252b3787f6Schristos 			c,
5262b3787f6Schristos 			CREATE_SUSPENDED,
527bd25f4c4Schristos 			NULL);
5282b3787f6Schristos 
529bd25f4c4Schristos 	if (NULL == c->thr_table[0].thnd) {
5302b3787f6Schristos 		msyslog(LOG_ERR, "start blocking thread failed: %m");
5312b3787f6Schristos 		exit(-1);
5322b3787f6Schristos 	}
5332b3787f6Schristos 	/* remember the thread priority is only within the process class */
534bd25f4c4Schristos 	if (!SetThreadPriority(c->thr_table[0].thnd,
5352b3787f6Schristos 			       THREAD_PRIORITY_BELOW_NORMAL))
5362b3787f6Schristos 		msyslog(LOG_ERR, "Error lowering blocking thread priority: %m");
5372b3787f6Schristos 
538bd25f4c4Schristos 	resumed = ResumeThread(c->thr_table[0].thnd);
5392b3787f6Schristos 	DEBUG_INSIST(resumed);
540bd25f4c4Schristos 	c->thread_ref = &c->thr_table[0];
5412b3787f6Schristos }
5422b3787f6Schristos #else	/* pthreads start_blocking_thread_internal() follows */
5432b3787f6Schristos {
5442b3787f6Schristos # ifdef NEED_PTHREAD_INIT
5452b3787f6Schristos 	static int	pthread_init_called;
5462b3787f6Schristos # endif
5472b3787f6Schristos 	pthread_attr_t	thr_attr;
5482b3787f6Schristos 	int		rc;
5492b3787f6Schristos 	int		pipe_ends[2];	/* read then write */
5502b3787f6Schristos 	int		is_pipe;
5512b3787f6Schristos 	int		flags;
552335f7552Schristos 	size_t		ostacksize;
553335f7552Schristos 	size_t		nstacksize;
5542b3787f6Schristos 	sigset_t	saved_sig_mask;
5552b3787f6Schristos 
556bd25f4c4Schristos 	c->thread_ref = NULL;
557bd25f4c4Schristos 
5582b3787f6Schristos # ifdef NEED_PTHREAD_INIT
5592b3787f6Schristos 	/*
5602b3787f6Schristos 	 * from lib/isc/unix/app.c:
5612b3787f6Schristos 	 * BSDI 3.1 seg faults in pthread_sigmask() if we don't do this.
5622b3787f6Schristos 	 */
5632b3787f6Schristos 	if (!pthread_init_called) {
5642b3787f6Schristos 		pthread_init();
5652b3787f6Schristos 		pthread_init_called = TRUE;
5662b3787f6Schristos 	}
5672b3787f6Schristos # endif
5682b3787f6Schristos 
5692b3787f6Schristos 	rc = pipe_socketpair(&pipe_ends[0], &is_pipe);
5702b3787f6Schristos 	if (0 != rc) {
5712b3787f6Schristos 		msyslog(LOG_ERR, "start_blocking_thread: pipe_socketpair() %m");
5722b3787f6Schristos 		exit(1);
5732b3787f6Schristos 	}
5742b3787f6Schristos 	c->resp_read_pipe = move_fd(pipe_ends[0]);
5752b3787f6Schristos 	c->resp_write_pipe = move_fd(pipe_ends[1]);
5762b3787f6Schristos 	c->ispipe = is_pipe;
5772b3787f6Schristos 	flags = fcntl(c->resp_read_pipe, F_GETFL, 0);
5782b3787f6Schristos 	if (-1 == flags) {
5792b3787f6Schristos 		msyslog(LOG_ERR, "start_blocking_thread: fcntl(F_GETFL) %m");
5802b3787f6Schristos 		exit(1);
5812b3787f6Schristos 	}
5822b3787f6Schristos 	rc = fcntl(c->resp_read_pipe, F_SETFL, O_NONBLOCK | flags);
5832b3787f6Schristos 	if (-1 == rc) {
5842b3787f6Schristos 		msyslog(LOG_ERR,
5852b3787f6Schristos 			"start_blocking_thread: fcntl(F_SETFL, O_NONBLOCK) %m");
5862b3787f6Schristos 		exit(1);
5872b3787f6Schristos 	}
5882b3787f6Schristos 	(*addremove_io_fd)(c->resp_read_pipe, c->ispipe, FALSE);
5892b3787f6Schristos 	pthread_attr_init(&thr_attr);
5902b3787f6Schristos 	pthread_attr_setdetachstate(&thr_attr, PTHREAD_CREATE_DETACHED);
5912b3787f6Schristos #if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \
5922b3787f6Schristos     defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE)
593335f7552Schristos 	rc = pthread_attr_getstacksize(&thr_attr, &ostacksize);
594335f7552Schristos 	if (0 != rc) {
5952b3787f6Schristos 		msyslog(LOG_ERR,
596335f7552Schristos 			"start_blocking_thread: pthread_attr_getstacksize() -> %s",
597335f7552Schristos 			strerror(rc));
598335f7552Schristos 	} else {
599335f7552Schristos 		if (ostacksize < THREAD_MINSTACKSIZE)
600335f7552Schristos 			nstacksize = THREAD_MINSTACKSIZE;
601335f7552Schristos 		else if (ostacksize > THREAD_MAXSTACKSIZE)
602335f7552Schristos 			nstacksize = THREAD_MAXSTACKSIZE;
603335f7552Schristos 		else
604335f7552Schristos 			nstacksize = ostacksize;
605335f7552Schristos 		if (nstacksize != ostacksize)
606335f7552Schristos 			rc = pthread_attr_setstacksize(&thr_attr, nstacksize);
607335f7552Schristos 		if (0 != rc)
6082b3787f6Schristos 			msyslog(LOG_ERR,
609335f7552Schristos 				"start_blocking_thread: pthread_attr_setstacksize(0x%lx -> 0x%lx) -> %s",
610335f7552Schristos 				(u_long)ostacksize, (u_long)nstacksize,
611335f7552Schristos 				strerror(rc));
6122b3787f6Schristos 	}
6132b3787f6Schristos #else
614335f7552Schristos 	UNUSED_ARG(nstacksize);
615335f7552Schristos 	UNUSED_ARG(ostacksize);
6162b3787f6Schristos #endif
6172b3787f6Schristos #if defined(PTHREAD_SCOPE_SYSTEM) && defined(NEED_PTHREAD_SCOPE_SYSTEM)
6182b3787f6Schristos 	pthread_attr_setscope(&thr_attr, PTHREAD_SCOPE_SYSTEM);
6192b3787f6Schristos #endif
6202b3787f6Schristos 	c->thread_ref = emalloc_zero(sizeof(*c->thread_ref));
6212b3787f6Schristos 	block_thread_signals(&saved_sig_mask);
622bd25f4c4Schristos 	rc = pthread_create(&c->thr_table[0], &thr_attr,
6232b3787f6Schristos 			    &blocking_thread, c);
6242b3787f6Schristos 	pthread_sigmask(SIG_SETMASK, &saved_sig_mask, NULL);
6252b3787f6Schristos 	pthread_attr_destroy(&thr_attr);
6262b3787f6Schristos 	if (0 != rc) {
627335f7552Schristos 		msyslog(LOG_ERR, "start_blocking_thread: pthread_create() -> %s",
628335f7552Schristos 			strerror(rc));
6292b3787f6Schristos 		exit(1);
6302b3787f6Schristos 	}
631bd25f4c4Schristos 	c->thread_ref = &c->thr_table[0];
6322b3787f6Schristos }
6332b3787f6Schristos #endif
6342b3787f6Schristos 
635bd25f4c4Schristos /* --------------------------------------------------------------------
6362b3787f6Schristos  * block_thread_signals()
6372b3787f6Schristos  *
6382b3787f6Schristos  * Temporarily block signals used by ntpd main thread, so that signal
6392b3787f6Schristos  * mask inherited by child threads leaves them blocked.  Returns prior
6402b3787f6Schristos  * active signal mask via pmask, to be restored by the main thread
6412b3787f6Schristos  * after pthread_create().
6422b3787f6Schristos  */
6432b3787f6Schristos #ifndef SYS_WINNT
6442b3787f6Schristos void
block_thread_signals(sigset_t * pmask)6452b3787f6Schristos block_thread_signals(
6462b3787f6Schristos 	sigset_t *	pmask
6472b3787f6Schristos 	)
6482b3787f6Schristos {
6492b3787f6Schristos 	sigset_t	block;
6502b3787f6Schristos 
6512b3787f6Schristos 	sigemptyset(&block);
6522b3787f6Schristos # ifdef HAVE_SIGNALED_IO
6532b3787f6Schristos #  ifdef SIGIO
6542b3787f6Schristos 	sigaddset(&block, SIGIO);
6552b3787f6Schristos #  endif
6562b3787f6Schristos #  ifdef SIGPOLL
6572b3787f6Schristos 	sigaddset(&block, SIGPOLL);
6582b3787f6Schristos #  endif
6592b3787f6Schristos # endif	/* HAVE_SIGNALED_IO */
6602b3787f6Schristos 	sigaddset(&block, SIGALRM);
6612b3787f6Schristos 	sigaddset(&block, MOREDEBUGSIG);
6622b3787f6Schristos 	sigaddset(&block, LESSDEBUGSIG);
6632b3787f6Schristos # ifdef SIGDIE1
6642b3787f6Schristos 	sigaddset(&block, SIGDIE1);
6652b3787f6Schristos # endif
6662b3787f6Schristos # ifdef SIGDIE2
6672b3787f6Schristos 	sigaddset(&block, SIGDIE2);
6682b3787f6Schristos # endif
6692b3787f6Schristos # ifdef SIGDIE3
6702b3787f6Schristos 	sigaddset(&block, SIGDIE3);
6712b3787f6Schristos # endif
6722b3787f6Schristos # ifdef SIGDIE4
6732b3787f6Schristos 	sigaddset(&block, SIGDIE4);
6742b3787f6Schristos # endif
6752b3787f6Schristos # ifdef SIGBUS
6762b3787f6Schristos 	sigaddset(&block, SIGBUS);
6772b3787f6Schristos # endif
6782b3787f6Schristos 	sigemptyset(pmask);
6792b3787f6Schristos 	pthread_sigmask(SIG_BLOCK, &block, pmask);
6802b3787f6Schristos }
6812b3787f6Schristos #endif	/* !SYS_WINNT */
6822b3787f6Schristos 
6832b3787f6Schristos 
684bd25f4c4Schristos /* --------------------------------------------------------------------
685bd25f4c4Schristos  * Create & destroy semaphores. This is sufficiently different between
686bd25f4c4Schristos  * POSIX and Windows to warrant wrapper functions and close enough to
687bd25f4c4Schristos  * use the concept of synchronization via semaphore for all platforms.
688bd25f4c4Schristos  */
689bd25f4c4Schristos static sem_ref
create_sema(sema_type * semptr,u_int inival,u_int maxval)690bd25f4c4Schristos create_sema(
691bd25f4c4Schristos 	sema_type*	semptr,
692bd25f4c4Schristos 	u_int		inival,
693bd25f4c4Schristos 	u_int		maxval)
694bd25f4c4Schristos {
695bd25f4c4Schristos #ifdef SYS_WINNT
696bd25f4c4Schristos 
697bd25f4c4Schristos 	long svini, svmax;
698bd25f4c4Schristos 	if (NULL != semptr) {
699bd25f4c4Schristos 		svini = (inival < LONG_MAX)
700bd25f4c4Schristos 		    ? (long)inival : LONG_MAX;
701bd25f4c4Schristos 		svmax = (maxval < LONG_MAX && maxval > 0)
702bd25f4c4Schristos 		    ? (long)maxval : LONG_MAX;
703bd25f4c4Schristos 		semptr->shnd = CreateSemaphore(NULL, svini, svmax, NULL);
704bd25f4c4Schristos 		if (NULL == semptr->shnd)
705bd25f4c4Schristos 			semptr = NULL;
706bd25f4c4Schristos 	}
707bd25f4c4Schristos 
708bd25f4c4Schristos #else
709bd25f4c4Schristos 
710bd25f4c4Schristos 	(void)maxval;
711bd25f4c4Schristos 	if (semptr && sem_init(semptr, FALSE, inival))
712bd25f4c4Schristos 		semptr = NULL;
713bd25f4c4Schristos 
714bd25f4c4Schristos #endif
715bd25f4c4Schristos 
716bd25f4c4Schristos 	return semptr;
717bd25f4c4Schristos }
718bd25f4c4Schristos 
719bd25f4c4Schristos /* ------------------------------------------------------------------ */
720bd25f4c4Schristos static sem_ref
delete_sema(sem_ref obj)721bd25f4c4Schristos delete_sema(
722bd25f4c4Schristos 	sem_ref obj)
723bd25f4c4Schristos {
724bd25f4c4Schristos 
725bd25f4c4Schristos #   ifdef SYS_WINNT
726bd25f4c4Schristos 
727bd25f4c4Schristos 	if (obj) {
728bd25f4c4Schristos 		if (obj->shnd)
729bd25f4c4Schristos 			CloseHandle(obj->shnd);
730bd25f4c4Schristos 		obj->shnd = NULL;
731bd25f4c4Schristos 	}
732bd25f4c4Schristos 
733bd25f4c4Schristos #   else
734bd25f4c4Schristos 
735bd25f4c4Schristos 	if (obj)
736bd25f4c4Schristos 		sem_destroy(obj);
737bd25f4c4Schristos 
738bd25f4c4Schristos #   endif
739bd25f4c4Schristos 
740bd25f4c4Schristos 	return NULL;
741bd25f4c4Schristos }
742bd25f4c4Schristos 
743bd25f4c4Schristos /* --------------------------------------------------------------------
7442b3787f6Schristos  * prepare_child_sems()
7452b3787f6Schristos  *
746bd25f4c4Schristos  * create sync & access semaphores
7472b3787f6Schristos  *
748bd25f4c4Schristos  * All semaphores are cleared, only the access semaphore has 1 unit.
749bd25f4c4Schristos  * Childs wait on 'workitems_pending', then grabs 'sema_access'
750bd25f4c4Schristos  * and dequeues jobs. When done, 'sema_access' is given one unit back.
751bd25f4c4Schristos  *
752bd25f4c4Schristos  * The producer grabs 'sema_access', manages the queue, restores
753bd25f4c4Schristos  * 'sema_access' and puts one unit into 'workitems_pending'.
754bd25f4c4Schristos  *
755bd25f4c4Schristos  * The story goes the same for the response queue.
7562b3787f6Schristos  */
7572b3787f6Schristos static void
prepare_child_sems(blocking_child * c)7582b3787f6Schristos prepare_child_sems(
7592b3787f6Schristos 	blocking_child *c
7602b3787f6Schristos 	)
7612b3787f6Schristos {
762335f7552Schristos 	if (NULL == worker_memlock)
763335f7552Schristos 		worker_memlock = create_sema(&worker_mmutex, 1, 1);
764335f7552Schristos 
765bd25f4c4Schristos 	c->accesslock           = create_sema(&c->sem_table[0], 1, 1);
766bd25f4c4Schristos 	c->workitems_pending    = create_sema(&c->sem_table[1], 0, 0);
767bd25f4c4Schristos 	c->wake_scheduled_sleep = create_sema(&c->sem_table[2], 0, 1);
768bd25f4c4Schristos #   ifndef WORK_PIPE
769bd25f4c4Schristos 	c->responses_pending    = create_sema(&c->sem_table[3], 0, 0);
7702b3787f6Schristos #   endif
771bd25f4c4Schristos }
7722b3787f6Schristos 
773bd25f4c4Schristos /* --------------------------------------------------------------------
774bd25f4c4Schristos  * wait for semaphore. Where the wait can be interrupted, it will
775bd25f4c4Schristos  * internally resume -- When this function returns, there is either no
776bd25f4c4Schristos  * semaphore at all, a timeout occurred, or the caller could
777bd25f4c4Schristos  * successfully take a token from the semaphore.
778bd25f4c4Schristos  *
779bd25f4c4Schristos  * For untimed wait, not checking the result of this function at all is
780bd25f4c4Schristos  * definitely an option.
781bd25f4c4Schristos  */
7822b3787f6Schristos static int
wait_for_sem(sem_ref sem,struct timespec * timeout)7832b3787f6Schristos wait_for_sem(
7842b3787f6Schristos 	sem_ref			sem,
7852b3787f6Schristos 	struct timespec *	timeout		/* wall-clock */
7862b3787f6Schristos 	)
7872b3787f6Schristos #ifdef SYS_WINNT
7882b3787f6Schristos {
7892b3787f6Schristos 	struct timespec now;
7902b3787f6Schristos 	struct timespec delta;
7912b3787f6Schristos 	DWORD		msec;
7922b3787f6Schristos 	DWORD		rc;
7932b3787f6Schristos 
794bd25f4c4Schristos 	if (!(sem && sem->shnd)) {
795bd25f4c4Schristos 		errno = EINVAL;
796bd25f4c4Schristos 		return -1;
797bd25f4c4Schristos 	}
798bd25f4c4Schristos 
7992b3787f6Schristos 	if (NULL == timeout) {
8002b3787f6Schristos 		msec = INFINITE;
8012b3787f6Schristos 	} else {
8022b3787f6Schristos 		getclock(TIMEOFDAY, &now);
8032b3787f6Schristos 		delta = sub_tspec(*timeout, now);
8042b3787f6Schristos 		if (delta.tv_sec < 0) {
8052b3787f6Schristos 			msec = 0;
8062b3787f6Schristos 		} else if ((delta.tv_sec + 1) >= (MAXDWORD / 1000)) {
8072b3787f6Schristos 			msec = INFINITE;
8082b3787f6Schristos 		} else {
8092b3787f6Schristos 			msec = 1000 * (DWORD)delta.tv_sec;
8102b3787f6Schristos 			msec += delta.tv_nsec / (1000 * 1000);
8112b3787f6Schristos 		}
8122b3787f6Schristos 	}
813bd25f4c4Schristos 	rc = WaitForSingleObject(sem->shnd, msec);
8142b3787f6Schristos 	if (WAIT_OBJECT_0 == rc)
8152b3787f6Schristos 		return 0;
8162b3787f6Schristos 	if (WAIT_TIMEOUT == rc) {
8172b3787f6Schristos 		errno = ETIMEDOUT;
8182b3787f6Schristos 		return -1;
8192b3787f6Schristos 	}
8202b3787f6Schristos 	msyslog(LOG_ERR, "WaitForSingleObject unexpected 0x%x", rc);
8212b3787f6Schristos 	errno = EFAULT;
8222b3787f6Schristos 	return -1;
8232b3787f6Schristos }
8242b3787f6Schristos #else	/* pthreads wait_for_sem() follows */
8252b3787f6Schristos {
826bd25f4c4Schristos 	int rc = -1;
8272b3787f6Schristos 
828bd25f4c4Schristos 	if (sem) do {
8292b3787f6Schristos 			if (NULL == timeout)
8302b3787f6Schristos 				rc = sem_wait(sem);
8312b3787f6Schristos 			else
8322b3787f6Schristos 				rc = sem_timedwait(sem, timeout);
833bd25f4c4Schristos 		} while (rc == -1 && errno == EINTR);
834bd25f4c4Schristos 	else
835bd25f4c4Schristos 		errno = EINVAL;
8362b3787f6Schristos 
8372b3787f6Schristos 	return rc;
8382b3787f6Schristos }
8392b3787f6Schristos #endif
8402b3787f6Schristos 
841bd25f4c4Schristos /* --------------------------------------------------------------------
842bd25f4c4Schristos  * blocking_thread - thread functions have WINAPI (aka 'stdcall')
843bd25f4c4Schristos  * calling conventions under Windows and POSIX-defined signature
844bd25f4c4Schristos  * otherwise.
8452b3787f6Schristos  */
8462b3787f6Schristos #ifdef SYS_WINNT
847bd25f4c4Schristos u_int WINAPI
8482b3787f6Schristos #else
8492b3787f6Schristos void *
8502b3787f6Schristos #endif
blocking_thread(void * ThreadArg)8512b3787f6Schristos blocking_thread(
8522b3787f6Schristos 	void *	ThreadArg
8532b3787f6Schristos 	)
8542b3787f6Schristos {
8552b3787f6Schristos 	blocking_child *c;
8562b3787f6Schristos 
8572b3787f6Schristos 	c = ThreadArg;
8582b3787f6Schristos 	exit_worker(blocking_child_common(c));
8592b3787f6Schristos 
8602b3787f6Schristos 	/* NOTREACHED */
8612b3787f6Schristos 	return 0;
8622b3787f6Schristos }
8632b3787f6Schristos 
864bd25f4c4Schristos /* --------------------------------------------------------------------
8652b3787f6Schristos  * req_child_exit() runs in the parent.
866bd25f4c4Schristos  *
867bd25f4c4Schristos  * This function is called from from the idle timer, too, and possibly
868bd25f4c4Schristos  * without a thread being there any longer. Since we have folded up our
869bd25f4c4Schristos  * tent in that case and all the semaphores are already gone, we simply
870bd25f4c4Schristos  * ignore this request in this case.
871bd25f4c4Schristos  *
872bd25f4c4Schristos  * Since the existence of the semaphores is controlled exclusively by
873bd25f4c4Schristos  * the parent, there's no risk of data race here.
8742b3787f6Schristos  */
8752b3787f6Schristos int
req_child_exit(blocking_child * c)8762b3787f6Schristos req_child_exit(
8772b3787f6Schristos 	blocking_child *c
8782b3787f6Schristos 	)
8792b3787f6Schristos {
880bd25f4c4Schristos 	return (c->accesslock)
881bd25f4c4Schristos 	    ? queue_req_pointer(c, CHILD_EXIT_REQ)
882bd25f4c4Schristos 	    : 0;
8832b3787f6Schristos }
8842b3787f6Schristos 
885bd25f4c4Schristos /* --------------------------------------------------------------------
8862b3787f6Schristos  * cleanup_after_child() runs in parent.
8872b3787f6Schristos  */
8882b3787f6Schristos static void
cleanup_after_child(blocking_child * c)8892b3787f6Schristos cleanup_after_child(
8902b3787f6Schristos 	blocking_child *	c
8912b3787f6Schristos 	)
8922b3787f6Schristos {
8932b3787f6Schristos 	DEBUG_INSIST(!c->reusable);
894bd25f4c4Schristos 
8952b3787f6Schristos #   ifdef SYS_WINNT
896bd25f4c4Schristos 	/* The thread was not created in detached state, so we better
897bd25f4c4Schristos 	 * clean up.
898bd25f4c4Schristos 	 */
899bd25f4c4Schristos 	if (c->thread_ref && c->thread_ref->thnd) {
900bd25f4c4Schristos 		WaitForSingleObject(c->thread_ref->thnd, INFINITE);
901bd25f4c4Schristos 		INSIST(CloseHandle(c->thread_ref->thnd));
902bd25f4c4Schristos 		c->thread_ref->thnd = NULL;
903bd25f4c4Schristos 	}
9042b3787f6Schristos #   endif
9052b3787f6Schristos 	c->thread_ref = NULL;
906bd25f4c4Schristos 
907bd25f4c4Schristos 	/* remove semaphores and (if signalling vi IO) pipes */
908bd25f4c4Schristos 
909bd25f4c4Schristos 	c->accesslock           = delete_sema(c->accesslock);
910bd25f4c4Schristos 	c->workitems_pending    = delete_sema(c->workitems_pending);
911bd25f4c4Schristos 	c->wake_scheduled_sleep = delete_sema(c->wake_scheduled_sleep);
912bd25f4c4Schristos 
9132b3787f6Schristos #   ifdef WORK_PIPE
9142b3787f6Schristos 	DEBUG_INSIST(-1 != c->resp_read_pipe);
9152b3787f6Schristos 	DEBUG_INSIST(-1 != c->resp_write_pipe);
9162b3787f6Schristos 	(*addremove_io_fd)(c->resp_read_pipe, c->ispipe, TRUE);
9172b3787f6Schristos 	close(c->resp_write_pipe);
9182b3787f6Schristos 	close(c->resp_read_pipe);
9192b3787f6Schristos 	c->resp_write_pipe = -1;
9202b3787f6Schristos 	c->resp_read_pipe = -1;
9212b3787f6Schristos #   else
922bd25f4c4Schristos 	DEBUG_INSIST(NULL != c->responses_pending);
923bd25f4c4Schristos 	(*addremove_io_semaphore)(c->responses_pending->shnd, TRUE);
924bd25f4c4Schristos 	c->responses_pending = delete_sema(c->responses_pending);
9252b3787f6Schristos #   endif
926bd25f4c4Schristos 
927bd25f4c4Schristos 	/* Is it necessary to check if there are pending requests and
928bd25f4c4Schristos 	 * responses? If so, and if there are, what to do with them?
929bd25f4c4Schristos 	 */
930bd25f4c4Schristos 
931bd25f4c4Schristos 	/* re-init buffer index sequencers */
932bd25f4c4Schristos 	c->head_workitem = 0;
933bd25f4c4Schristos 	c->tail_workitem = 0;
934bd25f4c4Schristos 	c->head_response = 0;
935bd25f4c4Schristos 	c->tail_response = 0;
936bd25f4c4Schristos 
9372b3787f6Schristos 	c->reusable = TRUE;
9382b3787f6Schristos }
9392b3787f6Schristos 
9402b3787f6Schristos 
9412b3787f6Schristos #else	/* !WORK_THREAD follows */
9422b3787f6Schristos char work_thread_nonempty_compilation_unit;
9432b3787f6Schristos #endif
944