1275970Scy/*
2275970Scy * Copyright (C) 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3275970Scy *
4275970Scy * Permission to use, copy, modify, and/or distribute this software for any
5275970Scy * purpose with or without fee is hereby granted, provided that the above
6275970Scy * copyright notice and this permission notice appear in all copies.
7275970Scy *
8275970Scy * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9275970Scy * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10275970Scy * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11275970Scy * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12275970Scy * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13275970Scy * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14275970Scy * PERFORMANCE OF THIS SOFTWARE.
15275970Scy */
16275970Scy
17275970Scy/* $Id$ */
18275970Scy
19275970Scy/*! \file */
20275970Scy
21275970Scy#include <config.h>
22275970Scy
23275970Scy#include <atf-c.h>
24275970Scy
25275970Scy#include <unistd.h>
26275970Scy
27275970Scy#include <isc/task.h>
28275970Scy#include <isc/util.h>
29275970Scy
30275970Scy#include "../task_p.h"
31275970Scy#include "isctest.h"
32275970Scy
33275970Scy/*
34275970Scy * Helper functions
35275970Scy */
36275970Scy
37275970Scy/* task event handler, sets a boolean to true */
38275970Scyint counter = 0;
39275970Scyisc_mutex_t set_lock;
40275970Scy
41275970Scystatic void
42275970Scyset(isc_task_t *task, isc_event_t *event) {
43275970Scy	int *value = (int *) event->ev_arg;
44275970Scy
45275970Scy	UNUSED(task);
46275970Scy
47275970Scy	isc_event_free(&event);
48275970Scy	LOCK(&set_lock);
49275970Scy	*value = counter++;
50275970Scy	UNLOCK(&set_lock);
51275970Scy}
52275970Scy
53275970Scystatic void
54275970Scyset_and_drop(isc_task_t *task, isc_event_t *event) {
55275970Scy	int *value = (int *) event->ev_arg;
56275970Scy
57275970Scy	UNUSED(task);
58275970Scy
59275970Scy	isc_event_free(&event);
60275970Scy	LOCK(&set_lock);
61275970Scy	*value = (int) isc_taskmgr_mode(taskmgr);
62275970Scy	counter++;
63275970Scy	UNLOCK(&set_lock);
64275970Scy	isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_normal);
65275970Scy}
66275970Scy
67275970Scy/*
68275970Scy * Individual unit tests
69275970Scy */
70275970Scy
71275970Scy/* Create a task */
72275970ScyATF_TC(create_task);
73275970ScyATF_TC_HEAD(create_task, tc) {
74275970Scy	atf_tc_set_md_var(tc, "descr", "create and destroy a task");
75275970Scy}
76275970ScyATF_TC_BODY(create_task, tc) {
77275970Scy	isc_result_t result;
78275970Scy	isc_task_t *task = NULL;
79275970Scy
80275970Scy	UNUSED(tc);
81275970Scy
82275970Scy	result = isc_test_begin(NULL, ISC_TRUE);
83275970Scy	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
84275970Scy
85275970Scy	result = isc_task_create(taskmgr, 0, &task);
86275970Scy	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
87275970Scy
88275970Scy	isc_task_destroy(&task);
89275970Scy	ATF_REQUIRE_EQ(task, NULL);
90275970Scy
91275970Scy	isc_test_end();
92275970Scy}
93275970Scy
94275970Scy/* Process events */
95275970ScyATF_TC(all_events);
96275970ScyATF_TC_HEAD(all_events, tc) {
97275970Scy	atf_tc_set_md_var(tc, "descr", "process task events");
98275970Scy}
99275970ScyATF_TC_BODY(all_events, tc) {
100275970Scy	isc_result_t result;
101275970Scy	isc_task_t *task = NULL;
102275970Scy	isc_event_t *event;
103275970Scy	int a = 0, b = 0;
104275970Scy	int i = 0;
105275970Scy
106275970Scy	UNUSED(tc);
107275970Scy
108275970Scy	counter = 1;
109275970Scy
110275970Scy	result = isc_mutex_init(&set_lock);
111275970Scy	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
112275970Scy
113275970Scy	result = isc_test_begin(NULL, ISC_TRUE);
114275970Scy	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
115275970Scy
116275970Scy	result = isc_task_create(taskmgr, 0, &task);
117275970Scy	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
118275970Scy
119275970Scy	/* First event */
120275970Scy	event = isc_event_allocate(mctx, task, ISC_TASKEVENT_TEST,
121275970Scy				   set, &a, sizeof (isc_event_t));
122275970Scy	ATF_REQUIRE(event != NULL);
123275970Scy
124275970Scy	ATF_CHECK_EQ(a, 0);
125275970Scy	isc_task_send(task, &event);
126275970Scy
127275970Scy	event = isc_event_allocate(mctx, task, ISC_TASKEVENT_TEST,
128275970Scy				   set, &b, sizeof (isc_event_t));
129275970Scy	ATF_REQUIRE(event != NULL);
130275970Scy
131275970Scy	ATF_CHECK_EQ(b, 0);
132275970Scy	isc_task_send(task, &event);
133275970Scy
134275970Scy	while ((a == 0 || b == 0) && i++ < 5000) {
135275970Scy#ifndef ISC_PLATFORM_USETHREADS
136275970Scy		while (isc__taskmgr_ready(taskmgr))
137275970Scy			isc__taskmgr_dispatch(taskmgr);
138275970Scy#endif
139275970Scy		isc_test_nap(1000);
140275970Scy	}
141275970Scy
142275970Scy	ATF_CHECK(a != 0);
143275970Scy	ATF_CHECK(b != 0);
144275970Scy
145275970Scy	isc_task_destroy(&task);
146275970Scy	ATF_REQUIRE_EQ(task, NULL);
147275970Scy
148275970Scy	isc_test_end();
149275970Scy}
150275970Scy
151275970Scy/* Privileged events */
152275970ScyATF_TC(privileged_events);
153275970ScyATF_TC_HEAD(privileged_events, tc) {
154275970Scy	atf_tc_set_md_var(tc, "descr", "process privileged events");
155275970Scy}
156275970ScyATF_TC_BODY(privileged_events, tc) {
157275970Scy	isc_result_t result;
158275970Scy	isc_task_t *task1 = NULL, *task2 = NULL;
159275970Scy	isc_event_t *event;
160275970Scy	int a = 0, b = 0, c = 0, d = 0, e = 0;
161275970Scy	int i = 0;
162275970Scy
163275970Scy	UNUSED(tc);
164275970Scy
165275970Scy	counter = 1;
166275970Scy	result = isc_mutex_init(&set_lock);
167275970Scy	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
168275970Scy
169275970Scy	result = isc_test_begin(NULL, ISC_TRUE);
170275970Scy	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
171275970Scy
172275970Scy#ifdef ISC_PLATFORM_USETHREADS
173275970Scy	/*
174275970Scy	 * Pause the task manager so we can fill up the work queue
175275970Scy	 * without things happening while we do it.
176275970Scy	 */
177275970Scy	isc__taskmgr_pause(taskmgr);
178275970Scy#endif
179275970Scy
180275970Scy	result = isc_task_create(taskmgr, 0, &task1);
181275970Scy	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
182275970Scy	isc_task_setname(task1, "privileged", NULL);
183275970Scy	ATF_CHECK(!isc_task_privilege(task1));
184275970Scy	isc_task_setprivilege(task1, ISC_TRUE);
185275970Scy	ATF_CHECK(isc_task_privilege(task1));
186275970Scy
187275970Scy	result = isc_task_create(taskmgr, 0, &task2);
188275970Scy	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
189275970Scy	isc_task_setname(task2, "normal", NULL);
190275970Scy	ATF_CHECK(!isc_task_privilege(task2));
191275970Scy
192275970Scy	/* First event: privileged */
193275970Scy	event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST,
194275970Scy				   set, &a, sizeof (isc_event_t));
195275970Scy	ATF_REQUIRE(event != NULL);
196275970Scy
197275970Scy	ATF_CHECK_EQ(a, 0);
198275970Scy	isc_task_send(task1, &event);
199275970Scy
200275970Scy	/* Second event: not privileged */
201275970Scy	event = isc_event_allocate(mctx, task2, ISC_TASKEVENT_TEST,
202275970Scy				   set, &b, sizeof (isc_event_t));
203275970Scy	ATF_REQUIRE(event != NULL);
204275970Scy
205275970Scy	ATF_CHECK_EQ(b, 0);
206275970Scy	isc_task_send(task2, &event);
207275970Scy
208275970Scy	/* Third event: privileged */
209275970Scy	event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST,
210275970Scy				   set, &c, sizeof (isc_event_t));
211275970Scy	ATF_REQUIRE(event != NULL);
212275970Scy
213275970Scy	ATF_CHECK_EQ(c, 0);
214275970Scy	isc_task_send(task1, &event);
215275970Scy
216275970Scy	/* Fourth event: privileged */
217275970Scy	event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST,
218275970Scy				   set, &d, sizeof (isc_event_t));
219275970Scy	ATF_REQUIRE(event != NULL);
220275970Scy
221275970Scy	ATF_CHECK_EQ(d, 0);
222275970Scy	isc_task_send(task1, &event);
223275970Scy
224275970Scy	/* Fifth event: not privileged */
225275970Scy	event = isc_event_allocate(mctx, task2, ISC_TASKEVENT_TEST,
226275970Scy				   set, &e, sizeof (isc_event_t));
227275970Scy	ATF_REQUIRE(event != NULL);
228275970Scy
229275970Scy	ATF_CHECK_EQ(e, 0);
230275970Scy	isc_task_send(task2, &event);
231275970Scy
232275970Scy	ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_normal);
233275970Scy	isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_privileged);
234275970Scy	ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_privileged);
235275970Scy
236275970Scy#ifdef ISC_PLATFORM_USETHREADS
237275970Scy	isc__taskmgr_resume(taskmgr);
238275970Scy#endif
239275970Scy
240275970Scy	/* We're waiting for *all* variables to be set */
241275970Scy	while ((a == 0 || b == 0 || c == 0 || d == 0 || e == 0) && i++ < 5000) {
242275970Scy#ifndef ISC_PLATFORM_USETHREADS
243275970Scy		while (isc__taskmgr_ready(taskmgr))
244275970Scy			isc__taskmgr_dispatch(taskmgr);
245275970Scy#endif
246275970Scy		isc_test_nap(1000);
247275970Scy	}
248275970Scy
249275970Scy	/*
250275970Scy	 * We can't guarantee what order the events fire, but
251275970Scy	 * we do know the privileged tasks that set a, c, and d
252275970Scy	 * would have fired first.
253275970Scy	 */
254275970Scy	ATF_CHECK(a <= 3);
255275970Scy	ATF_CHECK(c <= 3);
256275970Scy	ATF_CHECK(d <= 3);
257275970Scy
258275970Scy	/* ...and the non-privileged tasks that set b and e, last */
259275970Scy	ATF_CHECK(b >= 4);
260275970Scy	ATF_CHECK(e >= 4);
261275970Scy
262275970Scy	ATF_CHECK_EQ(counter, 6);
263275970Scy
264275970Scy	isc_task_setprivilege(task1, ISC_FALSE);
265275970Scy	ATF_CHECK(!isc_task_privilege(task1));
266275970Scy
267275970Scy	ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_normal);
268275970Scy
269275970Scy	isc_task_destroy(&task1);
270275970Scy	ATF_REQUIRE_EQ(task1, NULL);
271275970Scy	isc_task_destroy(&task2);
272275970Scy	ATF_REQUIRE_EQ(task2, NULL);
273275970Scy
274275970Scy	isc_test_end();
275275970Scy}
276275970Scy
277275970Scy/*
278275970Scy * Edge case: this tests that the task manager behaves as expected when
279275970Scy * we explicitly set it into normal mode *while* running privileged.
280275970Scy */
281275970ScyATF_TC(privilege_drop);
282275970ScyATF_TC_HEAD(privilege_drop, tc) {
283275970Scy	atf_tc_set_md_var(tc, "descr", "process privileged events");
284275970Scy}
285275970ScyATF_TC_BODY(privilege_drop, tc) {
286275970Scy	isc_result_t result;
287275970Scy	isc_task_t *task1 = NULL, *task2 = NULL;
288275970Scy	isc_event_t *event;
289275970Scy	int a = -1, b = -1, c = -1, d = -1, e = -1;	/* non valid states */
290275970Scy	int i = 0;
291275970Scy
292275970Scy	UNUSED(tc);
293275970Scy
294275970Scy	counter = 1;
295275970Scy	result = isc_mutex_init(&set_lock);
296275970Scy	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
297275970Scy
298275970Scy	result = isc_test_begin(NULL, ISC_TRUE);
299275970Scy	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
300275970Scy
301275970Scy#ifdef ISC_PLATFORM_USETHREADS
302275970Scy	/*
303275970Scy	 * Pause the task manager so we can fill up the work queue
304275970Scy	 * without things happening while we do it.
305275970Scy	 */
306275970Scy	isc__taskmgr_pause(taskmgr);
307275970Scy#endif
308275970Scy
309275970Scy	result = isc_task_create(taskmgr, 0, &task1);
310275970Scy	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
311275970Scy	isc_task_setname(task1, "privileged", NULL);
312275970Scy	ATF_CHECK(!isc_task_privilege(task1));
313275970Scy	isc_task_setprivilege(task1, ISC_TRUE);
314275970Scy	ATF_CHECK(isc_task_privilege(task1));
315275970Scy
316275970Scy	result = isc_task_create(taskmgr, 0, &task2);
317275970Scy	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
318275970Scy	isc_task_setname(task2, "normal", NULL);
319275970Scy	ATF_CHECK(!isc_task_privilege(task2));
320275970Scy
321275970Scy	/* First event: privileged */
322275970Scy	event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST,
323275970Scy				   set_and_drop, &a, sizeof (isc_event_t));
324275970Scy	ATF_REQUIRE(event != NULL);
325275970Scy
326275970Scy	ATF_CHECK_EQ(a, -1);
327275970Scy	isc_task_send(task1, &event);
328275970Scy
329275970Scy	/* Second event: not privileged */
330275970Scy	event = isc_event_allocate(mctx, task2, ISC_TASKEVENT_TEST,
331275970Scy				   set_and_drop, &b, sizeof (isc_event_t));
332275970Scy	ATF_REQUIRE(event != NULL);
333275970Scy
334275970Scy	ATF_CHECK_EQ(b, -1);
335275970Scy	isc_task_send(task2, &event);
336275970Scy
337275970Scy	/* Third event: privileged */
338275970Scy	event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST,
339275970Scy				   set_and_drop, &c, sizeof (isc_event_t));
340275970Scy	ATF_REQUIRE(event != NULL);
341275970Scy
342275970Scy	ATF_CHECK_EQ(c, -1);
343275970Scy	isc_task_send(task1, &event);
344275970Scy
345275970Scy	/* Fourth event: privileged */
346275970Scy	event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST,
347275970Scy				   set_and_drop, &d, sizeof (isc_event_t));
348275970Scy	ATF_REQUIRE(event != NULL);
349275970Scy
350275970Scy	ATF_CHECK_EQ(d, -1);
351275970Scy	isc_task_send(task1, &event);
352275970Scy
353275970Scy	/* Fifth event: not privileged */
354275970Scy	event = isc_event_allocate(mctx, task2, ISC_TASKEVENT_TEST,
355275970Scy				   set_and_drop, &e, sizeof (isc_event_t));
356275970Scy	ATF_REQUIRE(event != NULL);
357275970Scy
358275970Scy	ATF_CHECK_EQ(e, -1);
359275970Scy	isc_task_send(task2, &event);
360275970Scy
361275970Scy	ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_normal);
362275970Scy	isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_privileged);
363275970Scy	ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_privileged);
364275970Scy
365275970Scy#ifdef ISC_PLATFORM_USETHREADS
366275970Scy	isc__taskmgr_resume(taskmgr);
367275970Scy#endif
368275970Scy
369275970Scy	/* We're waiting for all variables to be set. */
370275970Scy	while ((a == -1 || b == -1 || c == -1 || d == -1 || e == -1) &&
371275970Scy	       i++ < 5000) {
372275970Scy#ifndef ISC_PLATFORM_USETHREADS
373275970Scy		while (isc__taskmgr_ready(taskmgr))
374275970Scy			isc__taskmgr_dispatch(taskmgr);
375275970Scy#endif
376275970Scy		isc_test_nap(1000);
377275970Scy	}
378275970Scy
379275970Scy	/*
380275970Scy	 * We can't guarantee what order the events fire, but
381275970Scy	 * we do know *exactly one* of the privileged tasks will
382275970Scy	 * have run in privileged mode...
383275970Scy	 */
384275970Scy	ATF_CHECK(a == isc_taskmgrmode_privileged ||
385275970Scy		  c == isc_taskmgrmode_privileged ||
386275970Scy		  d == isc_taskmgrmode_privileged);
387275970Scy	ATF_CHECK(a + c + d == isc_taskmgrmode_privileged);
388275970Scy
389275970Scy	/* ...and neither of the non-privileged tasks did... */
390275970Scy	ATF_CHECK(b == isc_taskmgrmode_normal || e == isc_taskmgrmode_normal);
391275970Scy
392275970Scy	/* ...but all five of them did run. */
393275970Scy	ATF_CHECK_EQ(counter, 6);
394275970Scy
395275970Scy	ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_normal);
396275970Scy
397275970Scy	isc_task_destroy(&task1);
398275970Scy	ATF_REQUIRE_EQ(task1, NULL);
399275970Scy	isc_task_destroy(&task2);
400275970Scy	ATF_REQUIRE_EQ(task2, NULL);
401275970Scy
402275970Scy	isc_test_end();
403275970Scy}
404275970Scy
405275970Scy/*
406275970Scy * Main
407275970Scy */
408275970ScyATF_TP_ADD_TCS(tp) {
409275970Scy	ATF_TP_ADD_TC(tp, create_task);
410275970Scy	ATF_TP_ADD_TC(tp, all_events);
411275970Scy	ATF_TP_ADD_TC(tp, privileged_events);
412275970Scy	ATF_TP_ADD_TC(tp, privilege_drop);
413275970Scy
414275970Scy	return (atf_no_error());
415275970Scy}
416275970Scy
417