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