1 /*	$NetBSD: shutdown_test.c,v 1.7 2014/12/10 04:37:53 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004, 2007, 2011, 2013  Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (C) 1998-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: shutdown_test.c,v 1.25 2011/08/28 23:46:41 tbox Exp  */
21 
22 #include <config.h>
23 
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include <isc/app.h>
28 #include <isc/mem.h>
29 #include <isc/print.h>
30 #include <isc/task.h>
31 #include <isc/time.h>
32 #include <isc/timer.h>
33 #include <isc/util.h>
34 
35 typedef struct {
36 	isc_mem_t *	mctx;
37 	isc_task_t *	task;
38 	isc_timer_t *	timer;
39 	unsigned int	ticks;
40 	char	        name[16];
41 	isc_boolean_t	exiting;
42 	isc_task_t *	peer;
43 } t_info;
44 
45 #define MAX_TASKS	3
46 #define T2_SHUTDOWNOK	(ISC_EVENTCLASS(1024) + 0)
47 #define T2_SHUTDOWNDONE	(ISC_EVENTCLASS(1024) + 1)
48 #define FOO_EVENT	(ISC_EVENTCLASS(1024) + 2)
49 
50 static t_info			tasks[MAX_TASKS];
51 static unsigned int		task_count;
52 static isc_taskmgr_t *		task_manager;
53 static isc_timermgr_t *		timer_manager;
54 
55 static void
56 t1_shutdown(isc_task_t *task, isc_event_t *event) {
57 	t_info *info = event->ev_arg;
58 
59 	printf("task %s (%p) t1_shutdown\n", info->name, task);
60 	isc_task_detach(&info->task);
61 	isc_event_free(&event);
62 }
63 
64 static void
65 t2_shutdown(isc_task_t *task, isc_event_t *event) {
66 	t_info *info = event->ev_arg;
67 
68 	printf("task %s (%p) t2_shutdown\n", info->name, task);
69 	info->exiting = ISC_TRUE;
70 	isc_event_free(&event);
71 }
72 
73 static void
74 shutdown_action(isc_task_t *task, isc_event_t *event) {
75 	t_info *info = event->ev_arg;
76 	isc_event_t *nevent;
77 
78 	INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
79 
80 	printf("task %s (%p) shutdown\n", info->name, task);
81 	if (strcmp(info->name, "0") == 0) {
82 		isc_timer_detach(&info->timer);
83 		nevent = isc_event_allocate(info->mctx, info, T2_SHUTDOWNOK,
84 					    t2_shutdown, &tasks[1],
85 					    sizeof(*event));
86 		RUNTIME_CHECK(nevent != NULL);
87 		info->exiting = ISC_TRUE;
88 		isc_task_sendanddetach(&info->peer, &nevent);
89 	}
90 	isc_event_free(&event);
91 }
92 
93 static void
94 foo_event(isc_task_t *task, isc_event_t *event) {
95 	printf("task(%p) foo\n", task);
96 	isc_event_free(&event);
97 }
98 
99 static void
100 tick(isc_task_t *task, isc_event_t *event) {
101 	t_info *info = event->ev_arg;
102 	isc_event_t *nevent;
103 
104 	INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
105 
106 	printf("task %s (%p) tick\n", info->name, task);
107 
108 	info->ticks++;
109 	if (strcmp(info->name, "1") == 0) {
110 		if (info->ticks == 10) {
111 			RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
112 		} else if (info->ticks >= 15 && info->exiting) {
113 			isc_timer_detach(&info->timer);
114 			isc_task_detach(&info->task);
115 			nevent = isc_event_allocate(info->mctx, info,
116 						    T2_SHUTDOWNDONE,
117 						    t1_shutdown, &tasks[0],
118 						    sizeof(*event));
119 			RUNTIME_CHECK(nevent != NULL);
120 			isc_task_send(info->peer, &nevent);
121 			isc_task_detach(&info->peer);
122 		}
123 	} else if (strcmp(info->name, "foo") == 0) {
124 		isc_timer_detach(&info->timer);
125 		nevent = isc_event_allocate(info->mctx, info,
126 					    FOO_EVENT,
127 					    foo_event, task,
128 					    sizeof(*event));
129 		RUNTIME_CHECK(nevent != NULL);
130 		isc_task_sendanddetach(&task, &nevent);
131 	}
132 
133 	isc_event_free(&event);
134 }
135 
136 static t_info *
137 new_task(isc_mem_t *mctx, const char *name) {
138 	t_info *ti;
139 	isc_time_t expires;
140 	isc_interval_t interval;
141 
142 	RUNTIME_CHECK(task_count < MAX_TASKS);
143 	ti = &tasks[task_count];
144 	ti->mctx = mctx;
145 	ti->task = NULL;
146 	ti->timer = NULL;
147 	ti->ticks = 0;
148 	if (name != NULL) {
149 		INSIST(strlen(name) < sizeof(ti->name));
150 		strcpy(ti->name, name);
151 	} else
152 		sprintf(ti->name, "%d", task_count);
153 	RUNTIME_CHECK(isc_task_create(task_manager, 0, &ti->task) ==
154 		      ISC_R_SUCCESS);
155 	RUNTIME_CHECK(isc_task_onshutdown(ti->task, shutdown_action, ti) ==
156 		      ISC_R_SUCCESS);
157 
158 	isc_time_settoepoch(&expires);
159 	isc_interval_set(&interval, 1, 0);
160 	RUNTIME_CHECK(isc_timer_create(timer_manager, isc_timertype_ticker,
161 				       &expires, &interval, ti->task,
162 				       tick, ti, &ti->timer) ==
163 		      ISC_R_SUCCESS);
164 
165 	task_count++;
166 
167 	return (ti);
168 }
169 
170 int
171 main(int argc, char *argv[]) {
172 	unsigned int workers;
173 	t_info *t1, *t2;
174 	isc_task_t *task;
175 	isc_mem_t *mctx, *mctx2;
176 
177 	RUNTIME_CHECK(isc_app_start() == ISC_R_SUCCESS);
178 
179 	if (argc > 1) {
180 		workers = atoi(argv[1]);
181 		if (workers < 1)
182 			workers = 1;
183 		if (workers > 8192)
184 			workers = 8192;
185 	} else
186 		workers = 2;
187 	printf("%d workers\n", workers);
188 
189 	mctx = NULL;
190 	RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS);
191 	mctx2 = NULL;
192 	RUNTIME_CHECK(isc_mem_create(0, 0, &mctx2) == ISC_R_SUCCESS);
193 	RUNTIME_CHECK(isc_taskmgr_create(mctx, workers, 0, &task_manager) ==
194 		      ISC_R_SUCCESS);
195 	RUNTIME_CHECK(isc_timermgr_create(mctx, &timer_manager) ==
196 		      ISC_R_SUCCESS);
197 
198 	t1 = new_task(mctx, NULL);
199 	t2 = new_task(mctx2, NULL);
200 	isc_task_attach(t2->task, &t1->peer);
201 	isc_task_attach(t1->task, &t2->peer);
202 
203 	/*
204 	 * Test run-triggered shutdown.
205 	 */
206 	(void)new_task(mctx2, "foo");
207 
208 	/*
209 	 * Test implicit shutdown.
210 	 */
211 	task = NULL;
212 	RUNTIME_CHECK(isc_task_create(task_manager, 0, &task) ==
213 		      ISC_R_SUCCESS);
214 	isc_task_detach(&task);
215 
216 	/*
217 	 * Test anti-zombie code.
218 	 */
219 	RUNTIME_CHECK(isc_task_create(task_manager, 0, &task) ==
220 		      ISC_R_SUCCESS);
221 	isc_task_detach(&task);
222 
223 	RUNTIME_CHECK(isc_app_run() == ISC_R_SUCCESS);
224 
225 	isc_taskmgr_destroy(&task_manager);
226 	isc_timermgr_destroy(&timer_manager);
227 
228 	printf("Statistics for mctx:\n");
229 	isc_mem_stats(mctx, stdout);
230 	isc_mem_destroy(&mctx);
231 	printf("Statistics for mctx2:\n");
232 	isc_mem_stats(mctx2, stdout);
233 	isc_mem_destroy(&mctx2);
234 
235 	isc_app_finish();
236 
237 	return (0);
238 }
239