* exclusive access by calling isc_task_spl().
*/
-void
-isc_task_pause(isc_task_t *task0);
-void
-isc_task_unpause(isc_task_t *task0);
-/*%<
- * Pause/unpause this task. Pausing a task removes it from the ready
- * queue if it is present there; this ensures that the task will not
- * run again until unpaused. This is necessary when the libuv network
- * thread executes a function which schedules task manager events; this
- * prevents the task manager from executing the next event in a task
- * before the network thread has finished.
- *
- * Requires:
- *\li 'task' is a valid task, and is not already paused or shutting down.
- */
-
-void
-isc_task_getcurrenttime(isc_task_t *task, isc_stdtime_t *t);
-void
-isc_task_getcurrenttimex(isc_task_t *task, isc_time_t *t);
-/*%<
- * Provide the most recent timestamp on the task. The timestamp is considered
- * as the "current time".
- *
- * isc_task_getcurrentime() returns the time in one-second granularity;
- * isc_task_getcurrentimex() returns it in nanosecond granularity.
- *
- * Requires:
- *\li 'task' is a valid task.
- *\li 't' is a valid non NULL pointer.
- *
- * Ensures:
- *\li '*t' has the "current time".
- */
-
bool
isc_task_exiting(isc_task_t *t);
/*%<
typedef enum {
task_state_idle, /* not doing anything, events queue empty */
task_state_ready, /* waiting in worker's queue */
- task_state_paused, /* not running, paused */
- task_state_pausing, /* running, waiting to be paused */
task_state_running, /* actively processing events */
task_state_done /* shutting down, no events or references */
} task_state_t;
#if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C)
static const char *statenames[] = {
- "idle", "ready", "paused", "pausing", "running", "done",
+ "idle",
+ "ready",
+ "running",
+ "done",
};
#endif /* if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C) */
int threadid;
/* Locked by task lock. */
task_state_t state;
- int pause_cnt;
isc_refcount_t references;
isc_refcount_t running;
isc_eventlist_t events;
isc_mutex_init(&task->lock);
task->state = task_state_idle;
- task->pause_cnt = 0;
isc_refcount_init(&task->references, 1);
isc_refcount_init(&task->running, 0);
was_idle = true;
}
INSIST(task->state == task_state_ready ||
- task->state == task_state_paused ||
- task->state == task_state_pausing ||
task->state == task_state_running);
/*
task->state = task_state_ready;
}
INSIST(task->state == task_state_ready ||
- task->state == task_state_running ||
- task->state == task_state_paused ||
- task->state == task_state_pausing);
+ task->state == task_state_running);
ENQUEUE(task->events, event, ev_link);
task->nevents++;
return (task->tag);
}
-void
-isc_task_getcurrenttime(isc_task_t *task, isc_stdtime_t *t) {
- REQUIRE(VALID_TASK(task));
- REQUIRE(t != NULL);
-
- LOCK(&task->lock);
- *t = task->now;
- UNLOCK(&task->lock);
-}
-
-void
-isc_task_getcurrenttimex(isc_task_t *task, isc_time_t *t) {
- REQUIRE(VALID_TASK(task));
- REQUIRE(t != NULL);
-
- LOCK(&task->lock);
- *t = task->tnow;
- UNLOCK(&task->lock);
-}
-
/***
*** Task Manager.
***/
REQUIRE(VALID_TASK(task));
LOCK(&task->lock);
- /*
- * It is possible because that we have a paused task in the queue - it
- * might have been paused in the meantime and we never hold both queue
- * and task lock to avoid deadlocks, just bail then.
- */
+ /* FIXME */
if (task->state != task_state_ready) {
goto done;
}
*/
XTRACE("done");
task->state = task_state_done;
- } else {
- if (task->state == task_state_running) {
- XTRACE("idling");
- task->state = task_state_idle;
- } else if (task->state == task_state_pausing) {
- XTRACE("pausing");
- task->state = task_state_paused;
- }
+ } else if (task->state == task_state_running) {
+ XTRACE("idling");
+ task->state = task_state_idle;
}
break;
- } else if (task->state == task_state_pausing) {
- /*
- * We got a pause request on this task, stop working on
- * it and switch the state to paused.
- */
- XTRACE("pausing");
- task->state = task_state_paused;
- break;
} else if (dispatch_count >= task->quantum) {
/*
* Our quantum has expired, but there is more work to be
&(bool){ true }, false));
}
-void
-isc_task_pause(isc_task_t *task) {
- REQUIRE(VALID_TASK(task));
-
- LOCK(&task->lock);
- task->pause_cnt++;
- if (task->pause_cnt > 1) {
- /*
- * Someone already paused this task, just increase
- * the number of pausing clients.
- */
- UNLOCK(&task->lock);
- return;
- }
-
- INSIST(task->state == task_state_idle ||
- task->state == task_state_ready ||
- task->state == task_state_running);
- if (task->state == task_state_running) {
- task->state = task_state_pausing;
- } else {
- task->state = task_state_paused;
- }
- UNLOCK(&task->lock);
-}
-
-void
-isc_task_unpause(isc_task_t *task) {
- bool was_idle = false;
-
- REQUIRE(VALID_TASK(task));
-
- LOCK(&task->lock);
- task->pause_cnt--;
- INSIST(task->pause_cnt >= 0);
- if (task->pause_cnt > 0) {
- UNLOCK(&task->lock);
- return;
- }
-
- INSIST(task->state == task_state_paused ||
- task->state == task_state_pausing);
- /* If the task was pausing we can't reschedule it */
- if (task->state == task_state_pausing) {
- task->state = task_state_running;
- } else {
- task->state = task_state_idle;
- }
- if (task->state == task_state_idle && !EMPTY(task->events)) {
- task->state = task_state_ready;
- was_idle = true;
- }
- UNLOCK(&task->lock);
-
- if (was_idle) {
- task_ready(task);
- }
-}
-
void
isc_taskmgr_setmode(isc_taskmgr_t *manager, isc_taskmgrmode_t mode) {
atomic_store(&manager->mode, mode);
atomic_int_fast32_t counter;
static int active[10];
-static atomic_bool done, done2;
+static atomic_bool done;
static int
_setup(void **state) {
assert_null(task2);
}
-static void
-sleep_cb(isc_task_t *task, isc_event_t *event) {
- UNUSED(task);
- int p = *(int *)event->ev_arg;
- if (p == 1) {
- /*
- * Signal the main thread that we're running, so that
- * it can trigger the race.
- */
- LOCK(&lock);
- atomic_store(&done2, true);
- SIGNAL(&cv);
- UNLOCK(&lock);
- /*
- * Wait for the operations in the main thread to be finished.
- */
- LOCK(&lock);
- while (!atomic_load(&done)) {
- WAIT(&cv, &lock);
- }
- UNLOCK(&lock);
- } else {
- /*
- * Wait for the operations in the main thread to be finished.
- */
- LOCK(&lock);
- atomic_store(&done2, true);
- SIGNAL(&cv);
- UNLOCK(&lock);
- }
- isc_event_free(&event);
-}
-
-static void
-pause_unpause(void **state) {
- isc_result_t result;
- isc_task_t *task = NULL;
- isc_event_t *event1, *event2 = NULL;
- UNUSED(state);
- atomic_store(&done, false);
- atomic_store(&done2, false);
-
- result = isc_task_create(taskmgr, 0, &task);
- assert_int_equal(result, ISC_R_SUCCESS);
-
- event1 = isc_event_allocate(test_mctx, task, ISC_TASKEVENT_TEST,
- sleep_cb, &(int){ 1 }, sizeof(isc_event_t));
- assert_non_null(event1);
- event2 = isc_event_allocate(test_mctx, task, ISC_TASKEVENT_TEST,
- sleep_cb, &(int){ 2 }, sizeof(isc_event_t));
- assert_non_null(event2);
- isc_task_send(task, &event1);
- isc_task_send(task, &event2);
- /* Wait for event1 to be running */
- LOCK(&lock);
- while (!atomic_load(&done2)) {
- WAIT(&cv, &lock);
- }
- UNLOCK(&lock);
- /* Pause-unpause-detach is what causes the race */
- isc_task_pause(task);
- isc_task_unpause(task);
- isc_task_detach(&task);
- /* Signal event1 to finish */
- LOCK(&lock);
- atomic_store(&done2, false);
- atomic_store(&done, true);
- SIGNAL(&cv);
- UNLOCK(&lock);
- /* Wait for event2 to finish */
- LOCK(&lock);
- while (!atomic_load(&done2)) {
- WAIT(&cv, &lock);
- }
- UNLOCK(&lock);
-}
-
/*
* Basic task functions:
*/
cmocka_unit_test_setup_teardown(all_events, _setup, _teardown),
cmocka_unit_test_setup_teardown(basic, _setup2, _teardown),
cmocka_unit_test_setup_teardown(create_task, _setup, _teardown),
- cmocka_unit_test_setup_teardown(pause_unpause, _setup,
- _teardown),
cmocka_unit_test_setup_teardown(post_shutdown, _setup2,
_teardown),
cmocka_unit_test_setup_teardown(privilege_drop, _setup,
isc_task_detach
isc_task_endexclusive
isc_task_exiting
-isc_task_getcurrenttime
-isc_task_getcurrenttimex
isc_task_getprivilege
isc_task_onshutdown
-isc_task_pause
isc_task_privileged
isc_task_purge
isc_task_purgeevent
isc_task_setname
isc_task_setprivilege
isc_task_shutdown
-isc_task_unpause
isc_task_unsend
isc_taskmgr_excltask
isc_taskmgr_mode
#include <isc/stdio.h>
#include <isc/string.h>
#include <isc/task.h>
+#include <isc/thread.h>
#include <isc/timer.h>
#include <isc/util.h>
client->state = NS_CLIENTSTATE_READY;
- isc_task_pause(client->task);
if (client->handle == NULL) {
isc_nmhandle_setdata(handle, client, ns__client_reset_cb,
ns__client_put_cb);
ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
"dropped request: suspicious port");
- isc_task_unpause(client->task);
return;
}
#endif /* if NS_CLIENT_DROPPORT */
ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
"dropped request: blackholed peer");
- isc_task_unpause(client->task);
return;
}
* There isn't enough header to determine whether
* this was a request or a response. Drop it.
*/
- isc_task_unpause(client->task);
return;
}
*/
if ((flags & DNS_MESSAGEFLAG_QR) != 0) {
CTRACE("unexpected response");
- isc_task_unpause(client->task);
return;
}
result = DNS_R_FORMERR;
}
ns_client_error(client, result);
- isc_task_unpause(client->task);
return;
}
*/
if ((client->sctx->options & NS_SERVER_EDNSFORMERR) != 0) {
ns_client_error(client, DNS_R_FORMERR);
- isc_task_unpause(client->task);
return;
}
*/
if ((client->sctx->options & NS_SERVER_EDNSNOTIMP) != 0) {
ns_client_error(client, DNS_R_NOTIMP);
- isc_task_unpause(client->task);
return;
}
*/
if ((client->sctx->options & NS_SERVER_EDNSREFUSED) != 0) {
ns_client_error(client, DNS_R_REFUSED);
- isc_task_unpause(client->task);
return;
}
*/
if ((client->sctx->options & NS_SERVER_DROPEDNS) != 0) {
ns_client_drop(client, ISC_R_SUCCESS);
- isc_task_unpause(client->task);
return;
}
result = process_opt(client, opt);
if (result != ISC_R_SUCCESS) {
- isc_task_unpause(client->task);
return;
}
}
result = dns_message_reply(client->message, true);
if (result != ISC_R_SUCCESS) {
ns_client_error(client, result);
- isc_task_unpause(client->task);
return;
}
}
ns_client_send(client);
- isc_task_unpause(client->task);
return;
}
ns_client_dumpmessage(client, "message class could not be "
"determined");
ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_FORMERR);
- isc_task_unpause(client->task);
return;
}
"no matching view in class '%s'", classname);
ns_client_dumpmessage(client, "no matching view in class");
ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
- isc_task_unpause(client->task);
return;
}
client->message->opcode == dns_opcode_update))
{
ns_client_error(client, sigresult);
- isc_task_unpause(client->task);
return;
}
}
CTRACE("unknown opcode");
ns_client_error(client, DNS_R_NOTIMP);
}
-
- isc_task_unpause(client->task);
}
isc_result_t
MTRACE("clienttask");
int tid = isc_nm_tid();
- if (tid < 0) {
- tid = isc_random_uniform(manager->ncpus);
- }
+ REQUIRE(tid >= 0);
+ REQUIRE(tid < manager->ncpus);
isc_task_attach(manager->taskpool[tid], taskp);
}
}
}
isc_mem_put(manager->mctx, manager->taskpool,
- manager->ncpus * sizeof(isc_task_t *));
+ manager->ncpus * sizeof(manager->taskpool[0]));
ns_server_detach(&manager->sctx);
isc_mem_put(manager->mctx, manager, sizeof(*manager));
ns_interface_attach(interface, &manager->interface);
manager->exiting = false;
- manager->taskpool = isc_mem_get(mctx,
- manager->ncpus * sizeof(isc_task_t *));
+ manager->taskpool = isc_mem_get(
+ mctx, manager->ncpus * sizeof(manager->taskpool[0]));
for (i = 0; i < manager->ncpus; i++) {
manager->taskpool[i] = NULL;
result = isc_task_create_bound(manager->taskmgr, 20,