2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-09-18 13:56:27 +00:00
Files
bind/lib/isc/task.c

942 lines
21 KiB
C
Raw Normal View History

1998-12-12 20:48:14 +00:00
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
1998-12-12 20:48:14 +00:00
*/
1998-08-17 22:05:58 +00:00
/*! \file */
1999-04-01 03:59:27 +00:00
#include <stdbool.h>
#include <unistd.h>
#include <isc/async.h>
2018-10-11 13:39:04 +00:00
#include <isc/atomic.h>
#include <isc/backtrace.h>
#include <isc/condition.h>
#include <isc/event.h>
#include <isc/job.h>
#include <isc/log.h>
#include <isc/loop.h>
#include <isc/magic.h>
#include <isc/mem.h>
#include <isc/once.h>
#include <isc/print.h>
2018-10-11 13:39:04 +00:00
#include <isc/random.h>
2019-05-16 18:51:39 +02:00
#include <isc/refcount.h>
#include <isc/string.h>
1998-08-18 00:47:55 +00:00
#include <isc/task.h>
#include <isc/thread.h>
#include <isc/tid.h>
#include <isc/time.h>
1999-12-16 22:24:22 +00:00
#include <isc/util.h>
#include <isc/uv.h>
#ifdef HAVE_LIBXML2
#include <libxml/xmlwriter.h>
#define ISC_XMLCHAR (const xmlChar *)
#endif /* HAVE_LIBXML2 */
1998-08-17 22:05:58 +00:00
#ifdef HAVE_JSON_C
#include <json_object.h>
#endif /* HAVE_JSON_C */
#include "loop_p.h"
2009-10-05 17:30:49 +00:00
/*
* Task manager is built around 'as little locking as possible' concept.
* Each thread has his own queue of tasks to be run, if a task is in running
* state it will stay on the runner it's currently on - that helps with data
* locality on CPU.
*
* To make load even some tasks (from task pools) are bound to specific
* queues using isc_task_create. This way load balancing between
* CPUs/queues happens on the higher layer.
*/
1998-10-21 02:26:57 +00:00
#ifdef ISC_TASK_TRACE
#define XTRACE(m) \
fprintf(stderr, "task %p.tid %zu thread %zu: %s\n", task, \
(size_t)task->tid, (size_t)task->tid, (m))
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
#define XTTRACE(t, m) \
fprintf(stderr, "task %p thread %zu: %s\n", (t), (size_t)isc_tid(), (m))
#define XTHREADTRACE(m) \
fprintf(stderr, "thread %zu: %s\n", (size_t)isc_tid(), (m))
#else /* ifdef ISC_TASK_TRACE */
1998-08-17 23:15:50 +00:00
#define XTRACE(m)
#define XTTRACE(t, m)
#define XTHREADTRACE(m)
#endif /* ifdef ISC_TASK_TRACE */
1998-08-17 22:05:58 +00:00
1998-08-18 00:29:57 +00:00
/***
1998-08-19 21:46:15 +00:00
*** Types.
1998-08-18 00:29:57 +00:00
***/
1998-08-19 21:46:15 +00:00
typedef enum {
task_state_idle, /* not doing anything, events queue empty */
task_state_ready, /* waiting in worker's queue */
task_state_running, /* actively processing events */
task_state_done /* shutting down, no events or references */
1998-08-19 21:46:15 +00:00
} task_state_t;
#if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C)
static const char *statenames[] = {
"idle",
"ready",
"running",
"done",
};
#endif /* if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C) */
2020-02-13 14:44:37 -08:00
#define TASK_MAGIC ISC_MAGIC('T', 'A', 'S', 'K')
#define VALID_TASK(t) ISC_MAGIC_VALID(t, TASK_MAGIC)
1998-08-19 21:46:15 +00:00
#if TASKMGR_TRACE
void
isc__taskmgr_dump_active(isc_taskmgr_t *taskmgr);
#endif
struct isc_task {
1998-08-19 21:46:15 +00:00
/* Not locked. */
unsigned int magic;
isc_taskmgr_t *manager;
2020-02-13 14:44:37 -08:00
isc_mutex_t lock;
1998-08-19 21:46:15 +00:00
/* Locked by task lock. */
isc_loop_t *loop;
uint32_t tid;
2020-02-13 14:44:37 -08:00
task_state_t state;
isc_refcount_t references;
isc_eventlist_t events;
2020-02-13 14:44:37 -08:00
unsigned int nevents;
isc_stdtime_t now;
isc_time_t tnow;
char name[16];
void *tag;
1998-08-19 21:46:15 +00:00
/* Locked by task manager lock. */
#if TASKMGR_TRACE
char func[PATH_MAX];
char file[PATH_MAX];
unsigned int line;
void *backtrace[ISC__TASKTRACE_SIZE];
int backtrace_size;
#endif
LINK(isc_task_t) qlink;
LINK(isc_task_t) link;
1998-08-19 21:46:15 +00:00
};
#define TASK_SHUTTINGDOWN(t) (atomic_load_acquire(&(t)->manager->shuttingdown))
#define TASK_TASKMGR_MAGIC ISC_MAGIC('T', 'S', 'K', 'M')
#define VALID_TASKMGR(m) ISC_MAGIC_VALID(m, TASK_TASKMGR_MAGIC)
typedef ISC_LIST(isc_task_t) isc_tasklist_t;
1998-08-19 21:46:15 +00:00
struct isc_taskmgr {
1998-08-19 21:46:15 +00:00
/* Not locked. */
unsigned int magic;
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
isc_refcount_t references;
2020-02-13 14:44:37 -08:00
isc_mem_t *mctx;
isc_mutex_t lock;
isc_loopmgr_t *loopmgr;
uint32_t nloops;
2018-10-11 13:39:04 +00:00
1998-08-19 21:46:15 +00:00
/* Locked by task manager lock. */
isc_mutex_t *locks;
isc_tasklist_t *tasks;
atomic_uint_fast32_t mode;
uint32_t exclusive_req;
atomic_bool shuttingdown;
isc_task_t *excl;
1998-08-19 21:46:15 +00:00
};
1998-08-18 00:29:57 +00:00
static void
task_setstate(isc_task_t *task, task_state_t state);
1998-08-17 22:05:58 +00:00
/***
*** Tasks.
***/
static void
task_destroy(void *arg) {
isc_task_t *task = arg;
isc_loop_t *loop = task->loop;
isc_taskmgr_t *taskmgr = task->manager;
1998-08-17 22:05:58 +00:00
REQUIRE(EMPTY(task->events));
XTRACE("task_finished");
1998-08-17 22:05:58 +00:00
task_setstate(task, task_state_done);
isc_refcount_destroy(&task->references);
LOCK(&taskmgr->locks[task->tid]);
UNLINK(taskmgr->tasks[task->tid], task, link);
UNLOCK(&taskmgr->locks[task->tid]);
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
isc_mutex_destroy(&task->lock);
task->magic = 0;
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
isc_mem_put(loop->mctx, task, sizeof(*task));
isc_taskmgr_detach(&taskmgr);
isc_loop_detach(&loop);
1998-08-17 22:05:58 +00:00
}
ISC_REFCOUNT_IMPL(isc_task, task_destroy);
static isc_result_t
task_run(isc_task_t *task);
static void
task_ready(isc_task_t *task);
static void
task__run(void *arg);
1998-08-17 22:05:58 +00:00
isc_result_t
isc__task_create(isc_taskmgr_t *taskmgr, isc_task_t **taskp,
int tid ISC__TASKFLARG) {
isc_task_t *task = NULL;
isc_loop_t *loop = NULL;
1998-08-17 22:05:58 +00:00
REQUIRE(VALID_TASKMGR(taskmgr));
1998-08-17 22:05:58 +00:00
REQUIRE(taskp != NULL && *taskp == NULL);
REQUIRE(tid >= 0 && tid < (int)taskmgr->nloops);
if (atomic_load(&taskmgr->shuttingdown)) {
return (ISC_R_SHUTTINGDOWN);
}
1998-08-17 22:05:58 +00:00
loop = isc_loop_get(taskmgr->loopmgr, tid);
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
task = isc_mem_get(loop->mctx, sizeof(*task));
*task = (isc_task_t){
.tid = tid,
.state = task_state_idle,
};
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
isc_loop_attach(loop, &task->loop);
#if TASKMGR_TRACE
strlcpy(task->func, func, sizeof(task->func));
strlcpy(task->file, file, sizeof(task->file));
task->line = line;
task->backtrace_size = isc_backtrace(task->backtrace,
ISC__TASKTRACE_SIZE);
#endif
isc_taskmgr_attach(taskmgr, &task->manager);
2018-11-16 15:33:22 +01:00
isc_mutex_init(&task->lock);
2019-05-20 17:00:22 +02:00
2019-05-16 18:51:39 +02:00
isc_refcount_init(&task->references, 1);
1998-08-17 22:05:58 +00:00
INIT_LIST(task->events);
isc_time_settoepoch(&task->tnow);
1998-08-17 22:05:58 +00:00
INIT_LINK(task, link);
INIT_LINK(task, qlink);
1998-08-17 22:05:58 +00:00
task->magic = TASK_MAGIC;
1998-08-17 22:05:58 +00:00
LOCK(&taskmgr->locks[task->tid]);
APPEND(taskmgr->tasks[task->tid], task, link);
UNLOCK(&taskmgr->locks[task->tid]);
*taskp = task;
1998-08-17 22:05:58 +00:00
1998-10-22 01:33:20 +00:00
return (ISC_R_SUCCESS);
1998-08-17 22:05:58 +00:00
}
static void
task_setstate(isc_task_t *task, task_state_t state) {
switch (state) {
case task_state_idle:
INSIST(task->state == task_state_running);
break;
case task_state_ready:
if (task->state == task_state_idle) {
INSIST(EMPTY(task->events));
} else {
INSIST(task->state == task_state_running);
}
break;
case task_state_running:
INSIST(task->state == task_state_ready);
break;
case task_state_done:
INSIST(task->state == task_state_ready ||
task->state == task_state_running ||
task->state == task_state_idle);
break;
default:
UNREACHABLE();
}
1998-08-17 22:05:58 +00:00
task->state = state;
}
static void
task__run(void *arg) {
isc_task_t *task = arg;
isc_result_t result = task_run(task);
switch (result) {
case ISC_R_QUOTA:
task_ready(task);
break;
case ISC_R_SUCCESS:
case ISC_R_NOMORE:
break;
default:
UNREACHABLE();
}
1998-08-17 22:05:58 +00:00
}
/*
* Moves a task onto the appropriate run queue.
*
* Caller must NOT hold queue lock.
*/
static void
task_ready(isc_task_t *task) {
isc_async_run(task->loop, task__run, task);
1998-08-17 22:05:58 +00:00
}
static bool
task_send(isc_task_t *task, isc_event_t **eventp) {
2020-02-13 14:44:37 -08:00
bool was_idle = false;
1998-12-13 23:45:21 +00:00
isc_event_t *event;
/*
* Caller must be holding the task lock.
*/
1998-08-18 19:28:30 +00:00
REQUIRE(eventp != NULL);
event = *eventp;
*eventp = NULL;
1998-08-17 22:05:58 +00:00
REQUIRE(event != NULL);
REQUIRE(event->ev_type > 0);
REQUIRE(task->state != task_state_done);
REQUIRE(!ISC_LINK_LINKED(event, ev_ratelink));
1998-08-17 22:05:58 +00:00
XTRACE("task_send");
if (task->state == task_state_idle) {
was_idle = true;
task_setstate(task, task_state_ready);
isc_task_attach(task, &(isc_task_t *){ NULL });
}
INSIST(task->state == task_state_ready ||
task->state == task_state_running);
ENQUEUE(task->events, event, ev_link);
return (was_idle);
}
void
isc_task_send(isc_task_t *task, isc_event_t **eventp) {
2020-02-13 14:44:37 -08:00
bool was_idle;
/*
* Send '*event' to 'task'.
*/
REQUIRE(VALID_TASK(task));
XTRACE("isc_task_send");
/*
* We're trying hard to hold locks for as short a time as possible.
* We're also trying to hold as few locks as possible. This is why
* some processing is deferred until after the lock is released.
*/
LOCK(&task->lock);
was_idle = task_send(task, eventp);
1998-08-17 22:05:58 +00:00
UNLOCK(&task->lock);
if (was_idle) {
/*
* We need to add this task to the ready queue.
*
* We've waited until now to do it because making a task
* ready requires locking the manager. If we tried to do
* this while holding the task lock, we could deadlock.
1998-08-17 22:05:58 +00:00
*
* We've changed the state to ready, so no one else will
* be trying to add this task to the ready queue. The
* only way to leave the ready state is by executing the
* task. It thus doesn't matter if events are added,
* removed, or a shutdown is started in the interval
* between the time we released the task lock, and the time
* we add the task to the ready queue.
1998-08-17 22:05:58 +00:00
*/
task_ready(task);
1998-08-17 22:05:58 +00:00
}
}
void
isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
isc_task_t *task = NULL;
REQUIRE(taskp != NULL);
task = *taskp;
*taskp = NULL;
REQUIRE(VALID_TASK(task));
XTRACE("isc_task_sendanddetach");
isc_task_send(task, eventp);
isc_task_detach(&task);
1998-08-17 22:05:58 +00:00
}
void
isc_task_setname(isc_task_t *task, const char *name, void *tag) {
2000-01-25 19:25:20 +00:00
/*
* Name 'task'.
*/
REQUIRE(VALID_TASK(task));
LOCK(&task->lock);
strlcpy(task->name, name, sizeof(task->name));
2000-01-25 19:25:20 +00:00
task->tag = tag;
UNLOCK(&task->lock);
}
1998-08-18 00:29:57 +00:00
isc_loopmgr_t *
isc_task_getloopmgr(isc_task_t *task) {
REQUIRE(VALID_TASK(task));
return (task->manager->loopmgr);
}
const char *
isc_task_getname(isc_task_t *task) {
REQUIRE(VALID_TASK(task));
return (task->name);
}
void *
isc_task_gettag(isc_task_t *task) {
REQUIRE(VALID_TASK(task));
return (task->tag);
}
1998-08-17 22:05:58 +00:00
/***
*** Task Manager.
***/
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
static isc_result_t
task_run(isc_task_t *task) {
isc_event_t *event = NULL;
isc_result_t result = ISC_R_UNSET;
isc_eventlist_t events;
1998-08-17 22:05:58 +00:00
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
REQUIRE(VALID_TASK(task));
2018-10-11 13:39:04 +00:00
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
LOCK(&task->lock);
ISC_LIST_INIT(events);
ISC_LIST_MOVE(events, task->events);
REQUIRE(task->state == task_state_ready);
task_setstate(task, task_state_running);
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
XTRACE("running");
XTRACE(task->name);
TIME_NOW(&task->tnow);
task->now = isc_time_seconds(&task->tnow);
UNLOCK(&task->lock);
event = ISC_LIST_HEAD(events);
while (event != NULL) {
isc_event_t *next = ISC_LIST_NEXT(event, ev_link);
ISC_LIST_UNLINK(events, event, ev_link);
/*
* Execute the event action.
*/
XTRACE("execute action");
XTRACE(task->name);
if (event->ev_action != NULL) {
(event->ev_action)(task, event);
}
XTRACE("execution complete");
event = next;
1998-08-17 22:05:58 +00:00
}
LOCK(&task->lock);
if (EMPTY(task->events)) {
/*
* Nothing else to do for this task right now.
*/
XTRACE("empty");
XTRACE("idling");
task_setstate(task, task_state_idle);
result = ISC_R_SUCCESS;
} else {
/*
* More tasks were scheduled.
*/
XTRACE("quantum");
task_setstate(task, task_state_ready);
result = ISC_R_QUOTA;
}
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
UNLOCK(&task->lock);
1998-08-17 22:05:58 +00:00
if (result == ISC_R_SUCCESS) {
isc_task_detach(&task);
}
2009-10-05 17:30:49 +00:00
return (result);
1998-08-17 22:05:58 +00:00
}
static void
taskmgr_destroy(isc_taskmgr_t *taskmgr) {
taskmgr->magic = 0;
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
for (size_t tid = 0; tid < taskmgr->nloops; tid++) {
INSIST(EMPTY(taskmgr->tasks[tid]));
isc_mutex_destroy(&taskmgr->locks[tid]);
}
isc_mem_put(taskmgr->mctx, taskmgr->tasks,
taskmgr->nloops * sizeof(taskmgr->tasks[0]));
isc_mem_put(taskmgr->mctx, taskmgr->locks,
taskmgr->nloops * sizeof(taskmgr->locks[0]));
isc_refcount_destroy(&taskmgr->references);
isc_mutex_destroy(&taskmgr->lock);
isc_mem_putanddetach(&taskmgr->mctx, taskmgr, sizeof(*taskmgr));
1998-08-17 22:05:58 +00:00
}
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
void
isc_taskmgr_attach(isc_taskmgr_t *source, isc_taskmgr_t **targetp) {
REQUIRE(VALID_TASKMGR(source));
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
REQUIRE(targetp != NULL && *targetp == NULL);
isc_refcount_increment(&source->references);
*targetp = source;
}
void
isc_taskmgr_detach(isc_taskmgr_t **managerp) {
REQUIRE(managerp != NULL);
REQUIRE(VALID_TASKMGR(*managerp));
isc_taskmgr_t *manager = *managerp;
*managerp = NULL;
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
if (isc_refcount_decrement(&manager->references) == 1) {
taskmgr_destroy(manager);
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
}
}
static void
taskmgr_teardown(void *arg) {
isc_taskmgr_t *taskmgr = (void *)arg;
uint32_t tid = isc_tid();
isc_task_t *excl = NULL;
REQUIRE(VALID_TASKMGR(taskmgr));
1998-08-17 22:05:58 +00:00
atomic_store(&taskmgr->shuttingdown, true);
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
ISC_LOG_DEBUG(1), "Shutting down task manager");
LOCK(&taskmgr->lock);
if (taskmgr->excl != NULL && taskmgr->excl->tid == tid) {
XTTRACE(taskmgr->excl, "taskmgr_teardown: excl");
excl = taskmgr->excl;
taskmgr->excl = NULL;
}
UNLOCK(&taskmgr->lock);
if (excl != NULL) {
isc_task_detach(&excl);
}
}
void
isc_taskmgr_create(isc_mem_t *mctx, isc_loopmgr_t *loopmgr,
isc_taskmgr_t **taskmgrp) {
isc_taskmgr_t *taskmgr = NULL;
1998-08-17 22:05:58 +00:00
/*
* Create a new task manager.
*/
REQUIRE(taskmgrp != NULL && *taskmgrp == NULL);
1998-08-17 22:05:58 +00:00
taskmgr = isc_mem_get(mctx, sizeof(*taskmgr));
*taskmgr = (isc_taskmgr_t){
.loopmgr = loopmgr,
.magic = TASK_TASKMGR_MAGIC,
.nloops = isc_loopmgr_nloops(loopmgr),
};
1998-08-17 22:05:58 +00:00
isc_mem_attach(mctx, &taskmgr->mctx);
1998-08-17 22:05:58 +00:00
isc_mutex_init(&taskmgr->lock);
1998-08-17 22:05:58 +00:00
taskmgr->tasks = isc_mem_get(
taskmgr->mctx, taskmgr->nloops * sizeof(taskmgr->tasks[0]));
taskmgr->locks = isc_mem_get(
taskmgr->mctx, taskmgr->nloops * sizeof(taskmgr->locks[0]));
1998-08-17 22:05:58 +00:00
for (size_t tid = 0; tid < taskmgr->nloops; tid++) {
isc_mutex_init(&taskmgr->locks[tid]);
ISC_LIST_INIT(taskmgr->tasks[tid]);
}
1998-08-17 22:05:58 +00:00
isc_loopmgr_teardown(loopmgr, taskmgr_teardown, taskmgr);
isc_refcount_init(&taskmgr->references, 1);
2021-06-04 14:56:14 +10:00
*taskmgrp = taskmgr;
}
1998-08-17 22:05:58 +00:00
void
isc_taskmgr_destroy(isc_taskmgr_t **managerp) {
isc_taskmgr_t *manager = NULL;
uint_fast32_t refs;
REQUIRE(managerp != NULL && VALID_TASKMGR(*managerp));
XTHREADTRACE("isc_taskmgr_destroy");
manager = *managerp;
*managerp = NULL;
/*
* The isc_loopmgr is not running, there's nothing that can finish now
*/
refs = isc_refcount_decrement(&manager->references);
#if TASKMGR_TRACE
if (refs > 1) {
isc__taskmgr_dump_active(*managerp);
}
#endif
INSIST(refs == 1);
taskmgr_destroy(manager);
1998-08-17 22:05:58 +00:00
}
void
isc_taskmgr_setexcltask(isc_taskmgr_t *mgr, isc_task_t *task) {
REQUIRE(VALID_TASKMGR(mgr));
REQUIRE(VALID_TASK(task));
2021-06-04 14:56:14 +10:00
LOCK(&task->lock);
REQUIRE(task->tid == 0);
2021-06-04 14:56:14 +10:00
UNLOCK(&task->lock);
LOCK(&mgr->lock);
if (mgr->excl != NULL) {
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
isc_task_detach(&mgr->excl);
}
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
isc_task_attach(task, &mgr->excl);
UNLOCK(&mgr->lock);
}
isc_result_t
isc_taskmgr_excltask(isc_taskmgr_t *mgr, isc_task_t **taskp) {
isc_result_t result;
REQUIRE(VALID_TASKMGR(mgr));
REQUIRE(taskp != NULL && *taskp == NULL);
if (atomic_load(&mgr->shuttingdown)) {
return (ISC_R_SHUTTINGDOWN);
}
LOCK(&mgr->lock);
if (mgr->excl != NULL) {
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
isc_task_attach(mgr->excl, taskp);
result = ISC_R_SUCCESS;
} else {
result = ISC_R_NOTFOUND;
}
UNLOCK(&mgr->lock);
return (result);
}
void
isc_task_beginexclusive(isc_task_t *task) {
isc_taskmgr_t *manager;
bool first;
REQUIRE(VALID_TASK(task));
manager = task->manager;
REQUIRE(task->state == task_state_running);
2018-10-24 13:12:55 -07:00
LOCK(&manager->lock);
REQUIRE(task == manager->excl ||
(atomic_load(&manager->shuttingdown) && manager->excl == NULL));
first = (manager->exclusive_req++ == 0);
UNLOCK(&manager->lock);
if (!first) {
return;
}
if (isc_log_wouldlog(isc_lctx, ISC_LOG_DEBUG(1))) {
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
ISC_LOGMODULE_OTHER, ISC_LOG_DEBUG(1),
"exclusive task mode: %s", "starting");
}
isc_loopmgr_pause(manager->loopmgr);
Refactor taskmgr to run on top of netmgr This commit changes the taskmgr to run the individual tasks on the netmgr internal workers. While an effort has been put into keeping the taskmgr interface intact, couple of changes have been made: * The taskmgr has no concept of universal privileged mode - rather the tasks are either privileged or unprivileged (normal). The privileged tasks are run as a first thing when the netmgr is unpaused. There are now four different queues in in the netmgr: 1. priority queue - netievent on the priority queue are run even when the taskmgr enter exclusive mode and netmgr is paused. This is needed to properly start listening on the interfaces, free resources and resume. 2. privileged task queue - only privileged tasks are queued here and this is the first queue that gets processed when network manager is unpaused using isc_nm_resume(). All netmgr workers need to clean the privileged task queue before they all proceed normal operation. Both task queues are processed when the workers are finished. 3. task queue - only (traditional) task are scheduled here and this queue along with privileged task queues are process when the netmgr workers are finishing. This is needed to process the task shutdown events. 4. normal queue - this is the queue with netmgr events, e.g. reading, sending, callbacks and pretty much everything is processed here. * The isc_taskmgr_create() now requires initialized netmgr (isc_nm_t) object. * The isc_nm_destroy() function now waits for indefinite time, but it will print out the active objects when in tracing mode (-DNETMGR_TRACE=1 and -DNETMGR_TRACE_VERBOSE=1), the netmgr has been made a little bit more asynchronous and it might take longer time to shutdown all the active networking connections. * Previously, the isc_nm_stoplistening() was a synchronous operation. This has been changed and the isc_nm_stoplistening() just schedules the child sockets to stop listening and exits. This was needed to prevent a deadlock as the the (traditional) tasks are now executed on the netmgr threads. * The socket selection logic in isc__nm_udp_send() was flawed, but fortunatelly, it was broken, so we never hit the problem where we created uvreq_t on a socket from nmhandle_t, but then a different socket could be picked up and then we were trying to run the send callback on a socket that had different threadid than currently running.
2021-04-09 11:31:19 +02:00
if (isc_log_wouldlog(isc_lctx, ISC_LOG_DEBUG(1))) {
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
ISC_LOGMODULE_OTHER, ISC_LOG_DEBUG(1),
"exclusive task mode: %s", "started");
}
}
void
isc_task_endexclusive(isc_task_t *task) {
isc_taskmgr_t *manager = NULL;
bool last;
REQUIRE(VALID_TASK(task));
REQUIRE(task->state == task_state_running);
manager = task->manager;
LOCK(&manager->lock);
INSIST(manager->exclusive_req > 0);
last = (--manager->exclusive_req == 0);
UNLOCK(&manager->lock);
if (!last) {
return;
}
if (isc_log_wouldlog(isc_lctx, ISC_LOG_DEBUG(1))) {
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
ISC_LOGMODULE_OTHER, ISC_LOG_DEBUG(1),
"exclusive task mode: %s", "ending");
}
isc_loopmgr_resume(manager->loopmgr);
if (isc_log_wouldlog(isc_lctx, ISC_LOG_DEBUG(1))) {
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
ISC_LOGMODULE_OTHER, ISC_LOG_DEBUG(1),
"exclusive task mode: %s", "ended");
}
}
#ifdef HAVE_LIBXML2
#define TRY0(a) \
do { \
xmlrc = (a); \
if (xmlrc < 0) \
goto error; \
} while (0)
int
isc_taskmgr_renderxml(isc_taskmgr_t *mgr, void *writer0) {
isc_task_t *task = NULL;
2020-02-13 14:44:37 -08:00
int xmlrc;
xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0;
LOCK(&mgr->lock);
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks"));
for (size_t tid = 0; tid < mgr->nloops; tid++) {
for (task = ISC_LIST_HEAD(mgr->tasks[tid]); task != NULL;
task = ISC_LIST_NEXT(task, link))
{
LOCK(&task->lock);
TRY0(xmlTextWriterStartElement(writer,
ISC_XMLCHAR "task"));
if (task->name[0] != 0) {
TRY0(xmlTextWriterStartElement(
writer, ISC_XMLCHAR "name"));
TRY0(xmlTextWriterWriteFormatString(
writer, "%s", task->name));
TRY0(xmlTextWriterEndElement(writer)); /* name
*/
}
TRY0(xmlTextWriterStartElement(writer,
ISC_XMLCHAR "reference"
"s"));
TRY0(xmlTextWriterWriteFormatString(
writer, "%" PRIuFAST32,
isc_refcount_current(&task->references)));
TRY0(xmlTextWriterEndElement(writer)); /* references */
TRY0(xmlTextWriterStartElement(writer,
ISC_XMLCHAR "id"));
TRY0(xmlTextWriterWriteFormatString(writer, "%p",
task));
TRY0(xmlTextWriterEndElement(writer)); /* id */
TRY0(xmlTextWriterStartElement(writer,
ISC_XMLCHAR "state"));
TRY0(xmlTextWriterWriteFormatString(
writer, "%s", statenames[task->state]));
TRY0(xmlTextWriterEndElement(writer)); /* state */
TRY0(xmlTextWriterEndElement(writer));
UNLOCK(&task->lock);
}
}
TRY0(xmlTextWriterEndElement(writer)); /* tasks */
error:
if (task != NULL) {
UNLOCK(&task->lock);
}
UNLOCK(&mgr->lock);
return (xmlrc);
}
#endif /* HAVE_LIBXML2 */
#ifdef HAVE_JSON_C
#define CHECKMEM(m) \
do { \
if (m == NULL) { \
result = ISC_R_NOMEMORY; \
goto error; \
} \
} while (0)
isc_result_t
isc_taskmgr_renderjson(isc_taskmgr_t *mgr, void *tasks0) {
2020-02-13 14:44:37 -08:00
isc_result_t result = ISC_R_SUCCESS;
isc_task_t *task = NULL;
2020-02-13 14:44:37 -08:00
json_object *obj = NULL, *array = NULL, *taskobj = NULL;
json_object *tasks = (json_object *)tasks0;
LOCK(&mgr->lock);
array = json_object_new_array();
CHECKMEM(array);
for (size_t tid = 0; tid < mgr->nloops; tid++) {
for (task = ISC_LIST_HEAD(mgr->tasks[tid]); task != NULL;
task = ISC_LIST_NEXT(task, link))
{
char buf[255];
LOCK(&task->lock);
taskobj = json_object_new_object();
CHECKMEM(taskobj);
json_object_array_add(array, taskobj);
snprintf(buf, sizeof(buf), "%p", task);
obj = json_object_new_string(buf);
CHECKMEM(obj);
json_object_object_add(taskobj, "id", obj);
if (task->name[0] != 0) {
obj = json_object_new_string(task->name);
CHECKMEM(obj);
json_object_object_add(taskobj, "name", obj);
}
obj = json_object_new_int(
isc_refcount_current(&task->references));
CHECKMEM(obj);
json_object_object_add(taskobj, "references", obj);
obj = json_object_new_string(statenames[task->state]);
CHECKMEM(obj);
json_object_object_add(taskobj, "state", obj);
UNLOCK(&task->lock);
}
}
json_object_object_add(tasks, "tasks", array);
array = NULL;
result = ISC_R_SUCCESS;
error:
if (array != NULL) {
json_object_put(array);
}
if (task != NULL) {
UNLOCK(&task->lock);
}
UNLOCK(&mgr->lock);
return (result);
}
#endif /* ifdef HAVE_JSON_C */
#if TASKMGR_TRACE
static void
event_dump(isc_event_t *event) {
fprintf(stderr, " - event: %p\n", event);
fprintf(stderr, " func: %s\n", event->func);
fprintf(stderr, " file: %s\n", event->file);
fprintf(stderr, " line: %u\n", event->line);
fprintf(stderr, " backtrace: |\n");
isc_backtrace_symbols_fd(event->backtrace, event->backtrace_size,
STDERR_FILENO);
}
static void
task_dump(isc_task_t *task) {
LOCK(&task->lock);
fprintf(stderr, "- task: %p\n", task);
fprintf(stderr, " tid: %" PRIu32 "\n", task->tid);
fprintf(stderr, " nevents: %u\n", task->nevents);
fprintf(stderr, " func: %s\n", task->func);
fprintf(stderr, " file: %s\n", task->file);
fprintf(stderr, " line: %u\n", task->line);
fprintf(stderr, " backtrace: |\n");
isc_backtrace_symbols_fd(task->backtrace, task->backtrace_size,
STDERR_FILENO);
fprintf(stderr, "\n");
for (isc_event_t *event = ISC_LIST_HEAD(task->events); event != NULL;
event = ISC_LIST_NEXT(event, ev_link))
{
event_dump(event);
}
UNLOCK(&task->lock);
}
void
isc__taskmgr_dump_active(isc_taskmgr_t *taskmgr) {
LOCK(&taskmgr->lock);
fprintf(stderr, "- taskmgr: %p\n", taskmgr);
for (size_t tid = 0; tid < taskmgr->nloops; tid++) {
for (isc_task_t *task = ISC_LIST_HEAD(taskmgr->tasks[tid]);
task != NULL; task = ISC_LIST_NEXT(task, link))
{
task_dump(task);
}
}
UNLOCK(&taskmgr->lock);
}
#endif