1/*	$NetBSD$	*/
2
3/*
4 * Copyright (C) 2004, 2007, 2009  Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1999-2001  Internet Software Consortium.
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20/* Id: app.c,v 1.9 2009/09/02 23:48:03 tbox Exp  */
21
22#include <config.h>
23
24#include <sys/types.h>
25
26#include <stddef.h>
27#include <stdlib.h>
28#include <errno.h>
29#include <unistd.h>
30#include <process.h>
31
32#include <isc/app.h>
33#include <isc/boolean.h>
34#include <isc/condition.h>
35#include <isc/msgs.h>
36#include <isc/mutex.h>
37#include <isc/event.h>
38#include <isc/platform.h>
39#include <isc/string.h>
40#include <isc/task.h>
41#include <isc/time.h>
42#include <isc/util.h>
43#include <isc/thread.h>
44
45static isc_eventlist_t	on_run;
46static isc_mutex_t	lock;
47static isc_boolean_t	shutdown_requested = ISC_FALSE;
48static isc_boolean_t	running = ISC_FALSE;
49/*
50 * We assume that 'want_shutdown' can be read and written atomically.
51 */
52static isc_boolean_t	want_shutdown = ISC_FALSE;
53/*
54 * We assume that 'want_reload' can be read and written atomically.
55 */
56static isc_boolean_t	want_reload = ISC_FALSE;
57
58static isc_boolean_t	blocked  = ISC_FALSE;
59
60static isc_thread_t	blockedthread;
61
62/* Events to wait for */
63
64#define NUM_EVENTS 2
65
66enum {
67	RELOAD_EVENT,
68	SHUTDOWN_EVENT
69};
70
71static HANDLE hEvents[NUM_EVENTS];
72DWORD  dwWaitResult;
73
74/*
75 * We need to remember which thread is the main thread...
76 */
77static isc_thread_t	main_thread;
78
79isc_result_t
80isc__app_start(void) {
81	isc_result_t result;
82
83	/*
84	 * Start an ISC library application.
85	 */
86
87	main_thread = GetCurrentThread();
88
89	result = isc_mutex_init(&lock);
90	if (result != ISC_R_SUCCESS)
91		return (result);
92
93	/* Create the reload event in a non-signaled state */
94	hEvents[RELOAD_EVENT] = CreateEvent(NULL, FALSE, FALSE, NULL);
95
96	/* Create the shutdown event in a non-signaled state */
97	hEvents[SHUTDOWN_EVENT] = CreateEvent(NULL, FALSE, FALSE, NULL);
98
99	ISC_LIST_INIT(on_run);
100	return (ISC_R_SUCCESS);
101}
102
103isc_result_t
104isc__app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action,
105	      void *arg) {
106	isc_event_t *event;
107	isc_task_t *cloned_task = NULL;
108	isc_result_t result;
109
110
111	LOCK(&lock);
112	if (running) {
113		result = ISC_R_ALREADYRUNNING;
114		goto unlock;
115	}
116
117	/*
118	 * Note that we store the task to which we're going to send the event
119	 * in the event's "sender" field.
120	 */
121	isc_task_attach(task, &cloned_task);
122	event = isc_event_allocate(mctx, cloned_task, ISC_APPEVENT_SHUTDOWN,
123				   action, arg, sizeof(*event));
124	if (event == NULL) {
125		result = ISC_R_NOMEMORY;
126		goto unlock;
127	}
128
129	ISC_LIST_APPEND(on_run, event, ev_link);
130	result = ISC_R_SUCCESS;
131
132 unlock:
133	UNLOCK(&lock);
134	return (result);
135}
136
137isc_result_t
138isc__app_run(void) {
139	isc_event_t *event, *next_event;
140	isc_task_t *task;
141	HANDLE *pHandles = NULL;
142
143	REQUIRE(main_thread == GetCurrentThread());
144	LOCK(&lock);
145	if (!running) {
146		running = ISC_TRUE;
147
148		/*
149		 * Post any on-run events (in FIFO order).
150		 */
151		for (event = ISC_LIST_HEAD(on_run);
152		     event != NULL;
153		     event = next_event) {
154			next_event = ISC_LIST_NEXT(event, ev_link);
155			ISC_LIST_UNLINK(on_run, event, ev_link);
156			task = event->ev_sender;
157			event->ev_sender = NULL;
158			isc_task_sendanddetach(&task, &event);
159		}
160
161	}
162
163	UNLOCK(&lock);
164
165	/*
166	 * There is no danger if isc_app_shutdown() is called before we wait
167	 * for events.
168	 */
169
170	while (!want_shutdown) {
171		dwWaitResult = WaitForMultipleObjects(NUM_EVENTS, hEvents,
172						      FALSE, INFINITE);
173
174		/* See why we returned */
175
176		if (WaitSucceeded(dwWaitResult, NUM_EVENTS)) {
177			/*
178			 * The return was due to one of the events
179			 * being signaled
180			 */
181			switch (WaitSucceededIndex(dwWaitResult)) {
182			case RELOAD_EVENT:
183				want_reload = ISC_TRUE;
184				break;
185
186			case SHUTDOWN_EVENT:
187				want_shutdown = ISC_TRUE;
188				break;
189			}
190		}
191		if (want_reload) {
192			want_reload = ISC_FALSE;
193			return (ISC_R_RELOAD);
194		}
195
196		if (want_shutdown && blocked)
197			exit(-1);
198	}
199
200	return (ISC_R_SUCCESS);
201}
202
203isc_result_t
204isc__app_shutdown(void) {
205	isc_boolean_t want_kill = ISC_TRUE;
206
207	LOCK(&lock);
208	REQUIRE(running);
209
210	if (shutdown_requested)
211		want_kill = ISC_FALSE;		/* We're only signaling once */
212	else
213		shutdown_requested = ISC_TRUE;
214
215	UNLOCK(&lock);
216	if (want_kill)
217		SetEvent(hEvents[SHUTDOWN_EVENT]);
218
219	return (ISC_R_SUCCESS);
220}
221
222isc_result_t
223isc__app_reload(void) {
224	isc_boolean_t want_reload = ISC_TRUE;
225
226	LOCK(&lock);
227	REQUIRE(running);
228
229	/*
230	 * Don't send the reload signal if we're shutting down.
231	 */
232	if (shutdown_requested)
233		want_reload = ISC_FALSE;
234
235	UNLOCK(&lock);
236	if (want_reload)
237		SetEvent(hEvents[RELOAD_EVENT]);
238
239	return (ISC_R_SUCCESS);
240}
241
242void
243isc__app_finish(void) {
244	DESTROYLOCK(&lock);
245}
246
247void
248isc__app_block(void) {
249	REQUIRE(running);
250	REQUIRE(!blocked);
251
252	blocked = ISC_TRUE;
253	blockedthread = GetCurrentThread();
254}
255
256void
257isc__app_unblock(void) {
258	REQUIRE(running);
259	REQUIRE(blocked);
260	blocked = ISC_FALSE;
261	REQUIRE(blockedthread == GetCurrentThread());
262}
263