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