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
43 static isc_eventlist_t on_run;
44 static isc_mutex_t lock;
45 static isc_boolean_t shutdown_requested = ISC_FALSE;
46 static isc_boolean_t running = ISC_FALSE;
47 /*
48 * We assume that 'want_shutdown' can be read and written atomically.
49 */
50 static isc_boolean_t want_shutdown = ISC_FALSE;
51 /*
52 * We assume that 'want_reload' can be read and written atomically.
53 */
54 static isc_boolean_t want_reload = ISC_FALSE;
55
56 static isc_boolean_t blocked = ISC_FALSE;
57
58 static isc_thread_t blockedthread;
59
60 /* Events to wait for */
61
62 #define NUM_EVENTS 2
63
64 enum {
65 RELOAD_EVENT,
66 SHUTDOWN_EVENT
67 };
68
69 static HANDLE hEvents[NUM_EVENTS];
70 DWORD dwWaitResult;
71
72 /*
73 * We need to remember which thread is the main thread...
74 */
75 static isc_thread_t main_thread;
76
77 isc_result_t
isc__app_start(void)78 isc__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
101 isc_result_t
isc__app_onrun(isc_mem_t * mctx,isc_task_t * task,isc_taskaction_t action,void * arg)102 isc__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
135 isc_result_t
isc__app_run(void)136 isc__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
201 isc_result_t
isc__app_shutdown(void)202 isc__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
220 isc_result_t
isc__app_reload(void)221 isc__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
240 void
isc__app_finish(void)241 isc__app_finish(void) {
242 DESTROYLOCK(&lock);
243 }
244
245 void
isc__app_block(void)246 isc__app_block(void) {
247 REQUIRE(running);
248 REQUIRE(!blocked);
249
250 blocked = ISC_TRUE;
251 blockedthread = GetCurrentThread();
252 }
253
254 void
isc__app_unblock(void)255 isc__app_unblock(void) {
256 REQUIRE(running);
257 REQUIRE(blocked);
258 blocked = ISC_FALSE;
259 REQUIRE(blockedthread == GetCurrentThread());
260 }
261