1998-12-12 20:48:14 +00:00
|
|
|
/*
|
2018-02-23 09:53:12 +01:00
|
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
2000-08-01 01:33:37 +00:00
|
|
|
*
|
2016-06-27 14:56:38 +10:00
|
|
|
* 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
|
2020-09-14 16:20:40 -07:00
|
|
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
2018-02-23 09:53:12 +01:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
2018-04-17 09:32:20 -07:00
|
|
|
/*! \file */
|
1999-04-01 03:59:27 +00:00
|
|
|
|
1999-05-18 19:23:13 +00:00
|
|
|
/*
|
|
|
|
* XXXRTH Need to document the states a task can be in, and the rules
|
|
|
|
* for changing states.
|
|
|
|
*/
|
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
#include <stdbool.h>
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
#include <unistd.h>
|
2018-04-17 08:29:14 -07:00
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
#include <isc/app.h>
|
2018-10-11 13:39:04 +00:00
|
|
|
#include <isc/atomic.h>
|
2000-12-26 21:45:08 +00:00
|
|
|
#include <isc/condition.h>
|
1999-05-10 23:00:30 +00:00
|
|
|
#include <isc/event.h>
|
2001-06-04 19:33:39 +00:00
|
|
|
#include <isc/magic.h>
|
2000-04-28 04:26:08 +00:00
|
|
|
#include <isc/mem.h>
|
2013-04-10 13:49:57 -07:00
|
|
|
#include <isc/once.h>
|
2000-08-29 22:55:57 +00:00
|
|
|
#include <isc/platform.h>
|
2015-05-23 14:21:51 +02:00
|
|
|
#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>
|
2020-02-12 13:59:18 +01:00
|
|
|
#include <isc/string.h>
|
1998-08-18 00:47:55 +00:00
|
|
|
#include <isc/task.h>
|
2000-12-26 21:45:08 +00:00
|
|
|
#include <isc/thread.h>
|
2015-10-02 12:32:42 -07:00
|
|
|
#include <isc/time.h>
|
1999-12-16 22:24:22 +00:00
|
|
|
#include <isc/util.h>
|
2019-06-24 14:25:55 +02:00
|
|
|
|
|
|
|
#ifdef HAVE_LIBXML2
|
|
|
|
#include <libxml/xmlwriter.h>
|
|
|
|
#define ISC_XMLCHAR (const xmlChar *)
|
|
|
|
#endif /* HAVE_LIBXML2 */
|
1998-08-17 22:05:58 +00:00
|
|
|
|
2019-06-24 12:21:47 +02:00
|
|
|
#ifdef HAVE_JSON_C
|
|
|
|
#include <json_object.h>
|
|
|
|
#endif /* HAVE_JSON_C */
|
|
|
|
|
2021-04-27 00:07:43 +02:00
|
|
|
#include "task_p.h"
|
2009-10-05 17:30:49 +00:00
|
|
|
|
2018-10-22 10:57:05 +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, if a task is in idle
|
|
|
|
* state it can be woken up on a specific runner with isc_task_sendto - that
|
|
|
|
* helps with data locality on CPU.
|
2018-11-21 09:50:50 +00:00
|
|
|
*
|
|
|
|
* To make load even some tasks (from task pools) are bound to specific
|
|
|
|
* queues using isc_task_create_bound. This way load balancing between
|
|
|
|
* CPUs/queues happens on the higher layer.
|
2018-10-22 10:57:05 +00:00
|
|
|
*/
|
|
|
|
|
1998-10-21 02:26:57 +00:00
|
|
|
#ifdef ISC_TASK_TRACE
|
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 XTRACE(m) \
|
|
|
|
fprintf(stderr, "task %p thread %zu: %s\n", task, isc_tid_v, (m))
|
|
|
|
#define XTTRACE(t, m) \
|
|
|
|
fprintf(stderr, "task %p thread %zu: %s\n", (t), isc_tid_v, (m))
|
|
|
|
#define XTHREADTRACE(m) fprintf(stderr, "thread %zu: %s\n", isc_tid_v, (m))
|
2020-02-13 21:48:23 +01:00
|
|
|
#else /* ifdef ISC_TASK_TRACE */
|
1998-08-17 23:15:50 +00:00
|
|
|
#define XTRACE(m)
|
1999-07-10 01:00:05 +00:00
|
|
|
#define XTTRACE(t, m)
|
1999-05-10 23:00:30 +00:00
|
|
|
#define XTHREADTRACE(m)
|
2020-02-13 21:48:23 +01:00
|
|
|
#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 {
|
2020-02-12 13:59:18 +01:00
|
|
|
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 */
|
1998-08-19 21:46:15 +00:00
|
|
|
} task_state_t;
|
|
|
|
|
2019-02-06 11:56:42 +01:00
|
|
|
#if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C)
|
2007-02-13 02:49:08 +00:00
|
|
|
static const char *statenames[] = {
|
Fix a race in taskmgr between worker and task pausing/unpausing.
To reproduce the race - create a task, send two events to it, first one
must take some time. Then, from the outside, pause(), unpause() and detach()
the task.
When the long-running event is processed by the task it is in
task_state_running state. When we called pause() the state changed to
task_state_paused, on unpause we checked that there are events in the task
queue, changed the state to task_state_ready and enqueued the task on the
workers readyq. We then detach the task.
The dispatch() is done with processing the event, it processes the second
event in the queue, and then shuts down the task and frees it (as it's not
referenced anymore). Dispatcher then takes the, already freed, task from
the queue where it was wrongly put, causing an use-after free and,
subsequently, either an assertion failure or a segmentation fault.
The probability of this happening is very slim, yet it might happen under a
very high load, more probably on a recursive resolver than on an
authoritative.
The fix introduces a new 'task_state_pausing' state - to which tasks
are moved if they're being paused while still running. They are moved
to task_state_paused state when dispatcher is done with them, and
if we unpause a task in paused state it's moved back to task_state_running
and not requeued.
2020-01-20 11:39:14 +01:00
|
|
|
"idle", "ready", "paused", "pausing", "running", "done",
|
2007-02-13 02:49:08 +00:00
|
|
|
};
|
2020-02-13 21:48:23 +01:00
|
|
|
#endif /* if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C) */
|
2007-02-13 02:49:08 +00:00
|
|
|
|
2020-02-13 14:44:37 -08:00
|
|
|
#define TASK_MAGIC ISC_MAGIC('T', 'A', 'S', 'K')
|
2020-02-12 13:59:18 +01:00
|
|
|
#define VALID_TASK(t) ISC_MAGIC_VALID(t, TASK_MAGIC)
|
1998-08-19 21:46:15 +00:00
|
|
|
|
2021-04-19 11:12:58 +02:00
|
|
|
struct isc_task {
|
1998-08-19 21:46:15 +00:00
|
|
|
/* Not locked. */
|
2021-04-19 11:12:58 +02:00
|
|
|
unsigned int magic;
|
|
|
|
isc_taskmgr_t *manager;
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_mutex_t lock;
|
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
|
|
|
int threadid;
|
1998-08-19 21:46:15 +00:00
|
|
|
/* Locked by task lock. */
|
2020-02-13 14:44:37 -08:00
|
|
|
task_state_t state;
|
2020-01-28 09:08:10 +01:00
|
|
|
int pause_cnt;
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_refcount_t references;
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
isc_refcount_t running;
|
2020-02-12 13:59:18 +01:00
|
|
|
isc_eventlist_t events;
|
|
|
|
isc_eventlist_t on_shutdown;
|
2020-02-13 14:44:37 -08:00
|
|
|
unsigned int nevents;
|
|
|
|
unsigned int quantum;
|
|
|
|
isc_stdtime_t now;
|
|
|
|
isc_time_t tnow;
|
|
|
|
char name[16];
|
|
|
|
void *tag;
|
|
|
|
bool bound;
|
2019-12-12 15:22:10 +01:00
|
|
|
/* Protected by atomics */
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
atomic_bool shuttingdown;
|
|
|
|
atomic_bool privileged;
|
1998-08-19 21:46:15 +00:00
|
|
|
/* Locked by task manager lock. */
|
2021-04-19 11:12:58 +02:00
|
|
|
LINK(isc_task_t) link;
|
1998-08-19 21:46:15 +00:00
|
|
|
};
|
|
|
|
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
#define TASK_SHUTTINGDOWN(t) (atomic_load_acquire(&(t)->shuttingdown))
|
|
|
|
#define TASK_PRIVILEGED(t) (atomic_load_acquire(&(t)->privileged))
|
1999-05-10 23:00:30 +00:00
|
|
|
|
2020-02-12 13:59:18 +01:00
|
|
|
#define TASK_MANAGER_MAGIC ISC_MAGIC('T', 'S', 'K', 'M')
|
2020-02-13 14:44:37 -08:00
|
|
|
#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TASK_MANAGER_MAGIC)
|
1998-08-19 21:46:15 +00:00
|
|
|
|
2021-04-19 11:12:58 +02:00
|
|
|
struct isc_taskmgr {
|
1998-08-19 21:46:15 +00:00
|
|
|
/* Not locked. */
|
2021-04-19 11:12:58 +02:00
|
|
|
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;
|
2020-02-12 13:59:18 +01:00
|
|
|
atomic_uint_fast32_t tasks_count;
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
isc_nm_t *netmgr;
|
2018-10-11 13:39:04 +00:00
|
|
|
|
1998-08-19 21:46:15 +00:00
|
|
|
/* Locked by task manager lock. */
|
2020-02-12 13:59:18 +01:00
|
|
|
unsigned int default_quantum;
|
2021-04-19 11:12:58 +02:00
|
|
|
LIST(isc_task_t) tasks;
|
2021-05-06 19:41:49 -07:00
|
|
|
atomic_uint_fast32_t mode;
|
2020-02-13 14:44:37 -08:00
|
|
|
atomic_bool exclusive_req;
|
|
|
|
atomic_bool exiting;
|
2015-02-27 12:34:43 +11:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Multiple threads can read/write 'excl' at the same time, so we need
|
|
|
|
* to protect the access. We can't use 'lock' since isc_task_detach()
|
|
|
|
* will try to acquire it.
|
|
|
|
*/
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_mutex_t excl_lock;
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_t *excl;
|
1998-08-19 21:46:15 +00:00
|
|
|
};
|
1998-08-18 00:29:57 +00:00
|
|
|
|
2020-02-12 13:59:18 +01:00
|
|
|
#define DEFAULT_DEFAULT_QUANTUM 25
|
1998-08-18 00:29:57 +00:00
|
|
|
|
2009-09-01 00:22:28 +00:00
|
|
|
/*%
|
2013-04-10 13:49:57 -07:00
|
|
|
* The following are intended for internal use (indicated by "isc__"
|
|
|
|
* prefix) but are not declared as static, allowing direct access from
|
|
|
|
* unit tests etc.
|
2009-09-01 00:22:28 +00:00
|
|
|
*/
|
|
|
|
|
2020-02-14 08:14:03 +01:00
|
|
|
bool
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_purgeevent(isc_task_t *task, isc_event_t *event);
|
2020-02-14 08:14:03 +01:00
|
|
|
void
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_taskmgr_setexcltask(isc_taskmgr_t *mgr, isc_task_t *task);
|
2020-02-14 08:14:03 +01:00
|
|
|
isc_result_t
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_taskmgr_excltask(isc_taskmgr_t *mgr, isc_task_t **taskp);
|
2018-10-25 15:01:25 +00:00
|
|
|
|
1998-08-17 22:05:58 +00:00
|
|
|
/***
|
|
|
|
*** Tasks.
|
|
|
|
***/
|
|
|
|
|
|
|
|
static void
|
2021-04-19 11:12:58 +02:00
|
|
|
task_finished(isc_task_t *task) {
|
|
|
|
isc_taskmgr_t *manager = 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
|
|
|
isc_mem_t *mctx = manager->mctx;
|
1998-08-17 22:05:58 +00:00
|
|
|
REQUIRE(EMPTY(task->events));
|
2012-05-14 10:06:05 -07:00
|
|
|
REQUIRE(task->nevents == 0);
|
1998-12-16 02:02:10 +00:00
|
|
|
REQUIRE(EMPTY(task->on_shutdown));
|
1999-07-10 01:00:05 +00:00
|
|
|
REQUIRE(task->state == task_state_done);
|
|
|
|
|
|
|
|
XTRACE("task_finished");
|
1998-08-17 22:05:58 +00:00
|
|
|
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
isc_refcount_destroy(&task->running);
|
2019-07-23 08:27:30 -04:00
|
|
|
isc_refcount_destroy(&task->references);
|
|
|
|
|
1998-08-17 22:05:58 +00:00
|
|
|
LOCK(&manager->lock);
|
|
|
|
UNLINK(manager->tasks, task, link);
|
2019-01-18 11:47:43 +01:00
|
|
|
atomic_fetch_sub(&manager->tasks_count, 1);
|
2018-10-11 13:39:04 +00:00
|
|
|
UNLOCK(&manager->lock);
|
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
|
|
|
|
2018-11-19 10:31:09 +00:00
|
|
|
isc_mutex_destroy(&task->lock);
|
2021-04-19 11:12:58 +02:00
|
|
|
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(mctx, task, sizeof(*task));
|
|
|
|
|
2021-05-10 11:36:54 +02:00
|
|
|
isc_taskmgr_detach(&manager);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
isc_result_t
|
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_create(isc_taskmgr_t *manager, unsigned int quantum,
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_task_t **taskp) {
|
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
|
|
|
return (isc_task_create_bound(manager, quantum, taskp, -1));
|
2018-11-21 09:50:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
isc_result_t
|
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_create_bound(isc_taskmgr_t *manager, unsigned int quantum,
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_task_t **taskp, int threadid) {
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_t *task;
|
2020-02-13 14:44:37 -08:00
|
|
|
bool exiting;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
REQUIRE(VALID_MANAGER(manager));
|
|
|
|
REQUIRE(taskp != NULL && *taskp == NULL);
|
|
|
|
|
2000-12-06 00:30:32 +00:00
|
|
|
XTRACE("isc_task_create");
|
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(manager->mctx, sizeof(*task));
|
|
|
|
*task = (isc_task_t){ 0 };
|
|
|
|
|
|
|
|
isc_taskmgr_attach(manager, &task->manager);
|
2018-11-16 15:33:22 +01:00
|
|
|
|
2018-11-21 09:50:50 +00:00
|
|
|
if (threadid == -1) {
|
|
|
|
/*
|
|
|
|
* Task is not pinned to a queue, it's threadid will be
|
2020-02-20 14:49:36 -08:00
|
|
|
* chosen when first task will be sent to it - either
|
2018-11-21 09:50:50 +00:00
|
|
|
* randomly or specified by isc_task_sendto.
|
|
|
|
*/
|
|
|
|
task->bound = false;
|
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->threadid = -1;
|
2018-11-21 09:50:50 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Task is pinned to a queue, it'll always be run
|
|
|
|
* by a specific thread.
|
|
|
|
*/
|
|
|
|
task->bound = true;
|
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->threadid = threadid;
|
2018-11-21 09:50:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
isc_mutex_init(&task->lock);
|
1998-08-17 22:05:58 +00:00
|
|
|
task->state = task_state_idle;
|
2020-01-28 09:08:10 +01:00
|
|
|
task->pause_cnt = 0;
|
2019-05-20 17:00:22 +02:00
|
|
|
|
2019-05-16 18:51:39 +02:00
|
|
|
isc_refcount_init(&task->references, 1);
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
isc_refcount_init(&task->running, 0);
|
1998-08-17 22:05:58 +00:00
|
|
|
INIT_LIST(task->events);
|
1998-12-16 02:02:10 +00:00
|
|
|
INIT_LIST(task->on_shutdown);
|
2012-05-14 10:06:05 -07:00
|
|
|
task->nevents = 0;
|
2018-11-21 09:50:50 +00:00
|
|
|
task->quantum = (quantum > 0) ? quantum : manager->default_quantum;
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
atomic_init(&task->shuttingdown, false);
|
|
|
|
atomic_init(&task->privileged, false);
|
2003-10-25 00:09:14 +00:00
|
|
|
task->now = 0;
|
2015-10-02 12:32:42 -07:00
|
|
|
isc_time_settoepoch(&task->tnow);
|
2001-11-27 01:56:32 +00:00
|
|
|
memset(task->name, 0, sizeof(task->name));
|
2000-01-25 19:25:20 +00:00
|
|
|
task->tag = NULL;
|
1998-08-17 22:05:58 +00:00
|
|
|
INIT_LINK(task, link);
|
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
exiting = false;
|
1998-08-17 22:05:58 +00:00
|
|
|
LOCK(&manager->lock);
|
2019-01-18 11:47:43 +01:00
|
|
|
if (!atomic_load_relaxed(&manager->exiting)) {
|
2000-01-17 23:41:33 +00:00
|
|
|
APPEND(manager->tasks, task, link);
|
2019-01-18 11:47:43 +01:00
|
|
|
atomic_fetch_add(&manager->tasks_count, 1);
|
2018-11-21 09:50:50 +00:00
|
|
|
} else {
|
2018-04-17 08:29:14 -07:00
|
|
|
exiting = true;
|
2018-11-21 09:50:50 +00:00
|
|
|
}
|
1998-08-17 22:05:58 +00:00
|
|
|
UNLOCK(&manager->lock);
|
|
|
|
|
2000-01-17 23:41:33 +00:00
|
|
|
if (exiting) {
|
2018-11-19 10:31:09 +00:00
|
|
|
isc_mutex_destroy(&task->lock);
|
2021-05-10 11:36:54 +02:00
|
|
|
isc_taskmgr_detach(&task->manager);
|
2001-11-27 01:56:32 +00:00
|
|
|
isc_mem_put(manager->mctx, task, sizeof(*task));
|
2000-01-17 23:41:33 +00:00
|
|
|
return (ISC_R_SHUTTINGDOWN);
|
|
|
|
}
|
|
|
|
|
2021-04-19 11:12:58 +02:00
|
|
|
task->magic = TASK_MAGIC;
|
|
|
|
*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
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
void
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
|
1999-05-18 19:23:13 +00:00
|
|
|
/*
|
|
|
|
* Attach *targetp to source.
|
|
|
|
*/
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1999-05-18 19:23:13 +00:00
|
|
|
REQUIRE(VALID_TASK(source));
|
|
|
|
REQUIRE(targetp != NULL && *targetp == NULL);
|
1998-08-17 22:05:58 +00:00
|
|
|
|
2000-12-06 00:30:32 +00:00
|
|
|
XTTRACE(source, "isc_task_attach");
|
1999-07-10 01:00:05 +00:00
|
|
|
|
2019-05-16 18:51:39 +02:00
|
|
|
isc_refcount_increment(&source->references);
|
1999-05-18 19:23:13 +00:00
|
|
|
|
2021-04-19 11:12:58 +02:00
|
|
|
*targetp = source;
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
static inline bool
|
2021-04-19 11:12:58 +02:00
|
|
|
task_shutdown(isc_task_t *task) {
|
2020-02-13 14:44:37 -08:00
|
|
|
bool was_idle = false;
|
1999-07-10 01:00:05 +00:00
|
|
|
isc_event_t *event, *prev;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-07-10 01:00:05 +00:00
|
|
|
/*
|
|
|
|
* Caller must be holding the task's lock.
|
|
|
|
*/
|
|
|
|
|
|
|
|
XTRACE("task_shutdown");
|
|
|
|
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
if (atomic_compare_exchange_strong(&task->shuttingdown,
|
|
|
|
&(bool){ false }, true)) {
|
2018-11-23 21:35:01 +01:00
|
|
|
XTRACE("shutting down");
|
1999-07-10 01:00:05 +00:00
|
|
|
if (task->state == task_state_idle) {
|
|
|
|
INSIST(EMPTY(task->events));
|
|
|
|
task->state = task_state_ready;
|
2018-04-17 08:29:14 -07:00
|
|
|
was_idle = true;
|
1999-07-10 01:00:05 +00:00
|
|
|
}
|
|
|
|
INSIST(task->state == task_state_ready ||
|
2019-11-05 15:23:33 -08:00
|
|
|
task->state == task_state_paused ||
|
Fix a race in taskmgr between worker and task pausing/unpausing.
To reproduce the race - create a task, send two events to it, first one
must take some time. Then, from the outside, pause(), unpause() and detach()
the task.
When the long-running event is processed by the task it is in
task_state_running state. When we called pause() the state changed to
task_state_paused, on unpause we checked that there are events in the task
queue, changed the state to task_state_ready and enqueued the task on the
workers readyq. We then detach the task.
The dispatch() is done with processing the event, it processes the second
event in the queue, and then shuts down the task and frees it (as it's not
referenced anymore). Dispatcher then takes the, already freed, task from
the queue where it was wrongly put, causing an use-after free and,
subsequently, either an assertion failure or a segmentation fault.
The probability of this happening is very slim, yet it might happen under a
very high load, more probably on a recursive resolver than on an
authoritative.
The fix introduces a new 'task_state_pausing' state - to which tasks
are moved if they're being paused while still running. They are moved
to task_state_paused state when dispatcher is done with them, and
if we unpause a task in paused state it's moved back to task_state_running
and not requeued.
2020-01-20 11:39:14 +01:00
|
|
|
task->state == task_state_pausing ||
|
1999-07-10 01:00:05 +00:00
|
|
|
task->state == task_state_running);
|
2011-09-02 21:15:39 +00:00
|
|
|
|
1999-07-10 01:00:05 +00:00
|
|
|
/*
|
|
|
|
* Note that we post shutdown events LIFO.
|
|
|
|
*/
|
2020-02-12 13:59:18 +01:00
|
|
|
for (event = TAIL(task->on_shutdown); event != NULL;
|
1999-07-10 01:00:05 +00:00
|
|
|
event = prev) {
|
2000-04-17 19:22:44 +00:00
|
|
|
prev = PREV(event, ev_link);
|
|
|
|
DEQUEUE(task->on_shutdown, event, ev_link);
|
|
|
|
ENQUEUE(task->events, event, ev_link);
|
2012-05-14 10:06:05 -07:00
|
|
|
task->nevents++;
|
1999-07-10 01:00:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (was_idle);
|
|
|
|
}
|
|
|
|
|
2011-09-02 21:15:39 +00:00
|
|
|
/*
|
|
|
|
* Moves a task onto the appropriate run queue.
|
|
|
|
*
|
2020-01-28 09:08:10 +01:00
|
|
|
* Caller must NOT hold queue lock.
|
2011-09-02 21:15:39 +00:00
|
|
|
*/
|
1999-07-10 01:00:05 +00:00
|
|
|
static inline void
|
2021-04-19 11:12:58 +02:00
|
|
|
task_ready(isc_task_t *task) {
|
|
|
|
isc_taskmgr_t *manager = task->manager;
|
1999-07-10 01:00:05 +00:00
|
|
|
REQUIRE(VALID_MANAGER(manager));
|
|
|
|
|
|
|
|
XTRACE("task_ready");
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
|
|
|
|
isc_refcount_increment0(&task->running);
|
|
|
|
isc_nm_task_enqueue(manager->netmgr, task, task->threadid);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
isc_task_ready(isc_task_t *task) {
|
|
|
|
task_ready(task);
|
1999-07-10 01:00:05 +00:00
|
|
|
}
|
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
static inline bool
|
2021-04-19 11:12:58 +02:00
|
|
|
task_detach(isc_task_t *task) {
|
1999-07-10 01:00:05 +00:00
|
|
|
/*
|
|
|
|
* Caller must be holding the task lock.
|
|
|
|
*/
|
|
|
|
|
|
|
|
XTRACE("detach");
|
|
|
|
|
2019-05-16 18:51:39 +02:00
|
|
|
if (isc_refcount_decrement(&task->references) == 1 &&
|
2020-02-13 14:44:37 -08:00
|
|
|
task->state == task_state_idle)
|
|
|
|
{
|
1999-09-23 21:30:26 +00:00
|
|
|
INSIST(EMPTY(task->events));
|
|
|
|
/*
|
|
|
|
* There are no references to this task, and no
|
|
|
|
* pending events. We could try to optimize and
|
|
|
|
* either initiate shutdown or clean up the task,
|
|
|
|
* depending on its state, but it's easier to just
|
2000-08-29 22:30:14 +00:00
|
|
|
* make the task ready and allow run() or the event
|
|
|
|
* loop to deal with shutting down and termination.
|
1999-09-23 21:30:26 +00:00
|
|
|
*/
|
|
|
|
task->state = task_state_ready;
|
2018-04-17 08:29:14 -07:00
|
|
|
return (true);
|
1999-07-10 01:00:05 +00:00
|
|
|
}
|
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
return (false);
|
1999-07-10 01:00:05 +00:00
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
void
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_task_detach(isc_task_t **taskp) {
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_t *task;
|
2020-02-13 14:44:37 -08:00
|
|
|
bool was_idle;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1999-05-18 19:23:13 +00:00
|
|
|
/*
|
|
|
|
* Detach *taskp from its task.
|
|
|
|
*/
|
|
|
|
|
1998-08-17 22:05:58 +00:00
|
|
|
REQUIRE(taskp != NULL);
|
2021-04-19 11:12:58 +02:00
|
|
|
task = *taskp;
|
1998-08-17 22:05:58 +00:00
|
|
|
REQUIRE(VALID_TASK(task));
|
|
|
|
|
1999-05-10 23:00:30 +00:00
|
|
|
XTRACE("isc_task_detach");
|
|
|
|
|
1998-08-17 22:05:58 +00:00
|
|
|
LOCK(&task->lock);
|
1999-09-23 21:30:26 +00:00
|
|
|
was_idle = task_detach(task);
|
1998-08-17 22:05:58 +00:00
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
2020-02-13 21:48:23 +01:00
|
|
|
if (was_idle) {
|
1999-07-10 01:00:05 +00:00
|
|
|
task_ready(task);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
*taskp = NULL;
|
|
|
|
}
|
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
static inline bool
|
2021-04-19 11:12:58 +02:00
|
|
|
task_send(isc_task_t *task, isc_event_t **eventp, int c) {
|
2020-02-13 14:44:37 -08:00
|
|
|
bool was_idle = false;
|
1998-12-13 23:45:21 +00:00
|
|
|
isc_event_t *event;
|
2000-08-01 01:33:37 +00:00
|
|
|
|
1999-05-18 19:23:13 +00:00
|
|
|
/*
|
1999-07-10 01:00:05 +00:00
|
|
|
* Caller must be holding the task lock.
|
1999-05-18 19:23:13 +00:00
|
|
|
*/
|
|
|
|
|
1998-08-18 19:28:30 +00:00
|
|
|
REQUIRE(eventp != NULL);
|
|
|
|
event = *eventp;
|
2020-02-08 04:37:54 -08:00
|
|
|
*eventp = NULL;
|
1998-08-17 22:05:58 +00:00
|
|
|
REQUIRE(event != NULL);
|
2000-04-17 19:22:44 +00:00
|
|
|
REQUIRE(event->ev_type > 0);
|
1999-09-23 21:30:26 +00:00
|
|
|
REQUIRE(task->state != task_state_done);
|
2017-01-12 14:12:05 +11:00
|
|
|
REQUIRE(!ISC_LINK_LINKED(event, ev_ratelink));
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1999-07-10 01:00:05 +00:00
|
|
|
XTRACE("task_send");
|
|
|
|
|
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 (task->bound) {
|
|
|
|
c = task->threadid;
|
|
|
|
} else if (c < 0) {
|
|
|
|
c = -1;
|
|
|
|
}
|
|
|
|
|
1999-09-23 21:30:26 +00:00
|
|
|
if (task->state == task_state_idle) {
|
2018-04-17 08:29:14 -07:00
|
|
|
was_idle = true;
|
2018-10-11 13:39:04 +00:00
|
|
|
task->threadid = c;
|
1999-09-23 21:30:26 +00:00
|
|
|
INSIST(EMPTY(task->events));
|
|
|
|
task->state = task_state_ready;
|
1998-12-16 02:02:10 +00:00
|
|
|
}
|
1999-09-23 21:30:26 +00:00
|
|
|
INSIST(task->state == task_state_ready ||
|
2019-11-05 15:23:33 -08:00
|
|
|
task->state == task_state_running ||
|
Fix a race in taskmgr between worker and task pausing/unpausing.
To reproduce the race - create a task, send two events to it, first one
must take some time. Then, from the outside, pause(), unpause() and detach()
the task.
When the long-running event is processed by the task it is in
task_state_running state. When we called pause() the state changed to
task_state_paused, on unpause we checked that there are events in the task
queue, changed the state to task_state_ready and enqueued the task on the
workers readyq. We then detach the task.
The dispatch() is done with processing the event, it processes the second
event in the queue, and then shuts down the task and frees it (as it's not
referenced anymore). Dispatcher then takes the, already freed, task from
the queue where it was wrongly put, causing an use-after free and,
subsequently, either an assertion failure or a segmentation fault.
The probability of this happening is very slim, yet it might happen under a
very high load, more probably on a recursive resolver than on an
authoritative.
The fix introduces a new 'task_state_pausing' state - to which tasks
are moved if they're being paused while still running. They are moved
to task_state_paused state when dispatcher is done with them, and
if we unpause a task in paused state it's moved back to task_state_running
and not requeued.
2020-01-20 11:39:14 +01:00
|
|
|
task->state == task_state_paused ||
|
|
|
|
task->state == task_state_pausing);
|
2000-04-17 19:22:44 +00:00
|
|
|
ENQUEUE(task->events, event, ev_link);
|
2012-05-14 10:06:05 -07:00
|
|
|
task->nevents++;
|
1999-07-10 01:00:05 +00:00
|
|
|
|
1999-09-23 21:30:26 +00:00
|
|
|
return (was_idle);
|
2000-08-01 01:33:37 +00:00
|
|
|
}
|
1999-07-10 01:00:05 +00:00
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
void
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_send(isc_task_t *task, isc_event_t **eventp) {
|
|
|
|
isc_task_sendto(task, eventp, -1);
|
2018-10-11 13:39:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
|
2018-10-11 13:39:04 +00:00
|
|
|
isc_task_sendtoanddetach(taskp, eventp, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_sendto(isc_task_t *task, isc_event_t **eventp, int c) {
|
2020-02-13 14:44:37 -08:00
|
|
|
bool was_idle;
|
1999-07-10 01:00:05 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Send '*event' to 'task'.
|
|
|
|
*/
|
|
|
|
|
|
|
|
REQUIRE(VALID_TASK(task));
|
2018-10-25 06:31:53 +00:00
|
|
|
XTRACE("isc_task_send");
|
1999-07-10 01:00:05 +00:00
|
|
|
|
2019-01-18 11:47:43 +01:00
|
|
|
/*
|
|
|
|
* 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);
|
2018-10-11 13:39:04 +00:00
|
|
|
was_idle = task_send(task, eventp, c);
|
1998-08-17 22:05:58 +00:00
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
|
|
|
if (was_idle) {
|
|
|
|
/*
|
|
|
|
* We need to add this task to the ready queue.
|
|
|
|
*
|
1999-07-10 01:00:05 +00:00
|
|
|
* 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
|
1999-05-10 23:00:30 +00:00
|
|
|
* 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,
|
1999-07-10 01:00:05 +00:00
|
|
|
* removed, or a shutdown is started in the interval
|
1999-05-10 23:00:30 +00:00
|
|
|
* 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
|
|
|
*/
|
1999-07-10 01:00:05 +00:00
|
|
|
task_ready(task);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
1999-07-10 01:00:05 +00:00
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
void
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_task_sendtoanddetach(isc_task_t **taskp, isc_event_t **eventp, int c) {
|
|
|
|
bool idle1, idle2;
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_t *task;
|
1999-07-10 01:00:05 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Send '*event' to '*taskp' and then detach '*taskp' from its
|
|
|
|
* task.
|
|
|
|
*/
|
|
|
|
|
|
|
|
REQUIRE(taskp != NULL);
|
2021-04-19 11:12:58 +02:00
|
|
|
task = *taskp;
|
1999-07-10 01:00:05 +00:00
|
|
|
REQUIRE(VALID_TASK(task));
|
2018-10-25 06:31:53 +00:00
|
|
|
XTRACE("isc_task_sendanddetach");
|
|
|
|
|
2019-01-18 11:47:43 +01:00
|
|
|
LOCK(&task->lock);
|
2018-10-11 13:39:04 +00:00
|
|
|
idle1 = task_send(task, eventp, c);
|
1999-09-23 21:30:26 +00:00
|
|
|
idle2 = task_detach(task);
|
1999-07-10 01:00:05 +00:00
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
1999-09-23 21:30:26 +00:00
|
|
|
/*
|
|
|
|
* If idle1, then idle2 shouldn't be true as well since we're holding
|
|
|
|
* the task lock, and thus the task cannot switch from ready back to
|
|
|
|
* idle.
|
|
|
|
*/
|
|
|
|
INSIST(!(idle1 && idle2));
|
1999-07-10 01:00:05 +00:00
|
|
|
|
2020-02-13 21:48:23 +01:00
|
|
|
if (idle1 || idle2) {
|
1999-09-23 21:30:26 +00:00
|
|
|
task_ready(task);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-07-10 01:00:05 +00:00
|
|
|
|
|
|
|
*taskp = NULL;
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-12 13:59:18 +01:00
|
|
|
#define PURGE_OK(event) (((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
|
1999-07-10 01:00:05 +00:00
|
|
|
|
|
|
|
static unsigned int
|
2021-04-19 11:12:58 +02:00
|
|
|
dequeue_events(isc_task_t *task, void *sender, isc_eventtype_t first,
|
2020-02-12 13:59:18 +01:00
|
|
|
isc_eventtype_t last, void *tag, isc_eventlist_t *events,
|
2020-02-13 14:44:37 -08:00
|
|
|
bool purging) {
|
1998-12-13 23:45:21 +00:00
|
|
|
isc_event_t *event, *next_event;
|
1999-07-10 01:00:05 +00:00
|
|
|
unsigned int count = 0;
|
1999-05-18 19:23:13 +00:00
|
|
|
|
1998-10-13 20:22:22 +00:00
|
|
|
REQUIRE(VALID_TASK(task));
|
1999-05-18 19:23:13 +00:00
|
|
|
REQUIRE(last >= first);
|
1998-10-13 20:22:22 +00:00
|
|
|
|
1999-07-10 01:00:05 +00:00
|
|
|
XTRACE("dequeue_events");
|
1999-06-15 23:18:18 +00:00
|
|
|
|
1998-10-13 20:22:22 +00:00
|
|
|
/*
|
1999-07-10 01:00:05 +00:00
|
|
|
* Events matching 'sender', whose type is >= first and <= last, and
|
|
|
|
* whose tag is 'tag' will be dequeued. If 'purging', matching events
|
|
|
|
* which are marked as unpurgable will not be dequeued.
|
1998-12-16 02:02:10 +00:00
|
|
|
*
|
1999-07-10 01:00:05 +00:00
|
|
|
* sender == NULL means "any sender", and tag == NULL means "any tag".
|
1998-10-13 20:22:22 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
LOCK(&task->lock);
|
1999-07-10 01:00:05 +00:00
|
|
|
|
|
|
|
for (event = HEAD(task->events); event != NULL; event = next_event) {
|
2000-04-17 19:22:44 +00:00
|
|
|
next_event = NEXT(event, ev_link);
|
|
|
|
if (event->ev_type >= first && event->ev_type <= last &&
|
|
|
|
(sender == NULL || event->ev_sender == sender) &&
|
|
|
|
(tag == NULL || event->ev_tag == tag) &&
|
2020-02-13 14:44:37 -08:00
|
|
|
(!purging || PURGE_OK(event)))
|
|
|
|
{
|
2000-04-17 19:22:44 +00:00
|
|
|
DEQUEUE(task->events, event, ev_link);
|
2012-05-14 10:06:05 -07:00
|
|
|
task->nevents--;
|
2000-04-17 19:22:44 +00:00
|
|
|
ENQUEUE(*events, event, ev_link);
|
1999-07-10 01:00:05 +00:00
|
|
|
count++;
|
1998-10-13 20:22:22 +00:00
|
|
|
}
|
|
|
|
}
|
1999-07-10 01:00:05 +00:00
|
|
|
|
1998-10-13 20:22:22 +00:00
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
1999-07-10 01:00:05 +00:00
|
|
|
return (count);
|
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
unsigned int
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first,
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_eventtype_t last, void *tag) {
|
|
|
|
unsigned int count;
|
1999-07-10 01:00:05 +00:00
|
|
|
isc_eventlist_t events;
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_event_t *event, *next_event;
|
2018-08-06 13:00:55 +02:00
|
|
|
REQUIRE(VALID_TASK(task));
|
1999-07-10 01:00:05 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Purge events from a task's event queue.
|
|
|
|
*/
|
|
|
|
|
|
|
|
XTRACE("isc_task_purgerange");
|
|
|
|
|
|
|
|
ISC_LIST_INIT(events);
|
|
|
|
|
2020-02-12 13:59:18 +01:00
|
|
|
count = dequeue_events(task, sender, first, last, tag, &events, true);
|
1999-07-10 01:00:05 +00:00
|
|
|
|
|
|
|
for (event = HEAD(events); event != NULL; event = next_event) {
|
2000-04-17 19:22:44 +00:00
|
|
|
next_event = NEXT(event, ev_link);
|
2017-11-30 10:31:44 +11:00
|
|
|
ISC_LIST_UNLINK(events, event, ev_link);
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_event_free(&event);
|
1998-10-13 20:22:22 +00:00
|
|
|
}
|
1998-11-06 01:44:44 +00:00
|
|
|
|
1999-07-10 01:00:05 +00:00
|
|
|
/*
|
|
|
|
* Note that purging never changes the state of the task.
|
|
|
|
*/
|
|
|
|
|
|
|
|
return (count);
|
1998-10-13 20:22:22 +00:00
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
unsigned int
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
|
|
|
|
void *tag) {
|
1999-05-18 19:23:13 +00:00
|
|
|
/*
|
|
|
|
* Purge events from a task's event queue.
|
|
|
|
*/
|
2018-08-06 13:00:55 +02:00
|
|
|
REQUIRE(VALID_TASK(task));
|
1999-05-18 19:23:13 +00:00
|
|
|
|
1999-07-10 01:00:05 +00:00
|
|
|
XTRACE("isc_task_purge");
|
|
|
|
|
2018-08-06 13:00:55 +02:00
|
|
|
return (isc_task_purgerange(task, sender, type, type, tag));
|
1999-05-18 19:23:13 +00:00
|
|
|
}
|
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
bool
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_purgeevent(isc_task_t *task, isc_event_t *event) {
|
1999-06-12 01:10:32 +00:00
|
|
|
isc_event_t *curr_event, *next_event;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Purge 'event' from a task's event queue.
|
1999-07-10 01:00:05 +00:00
|
|
|
*
|
|
|
|
* XXXRTH: WARNING: This method may be removed before beta.
|
1999-06-12 01:10:32 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
REQUIRE(VALID_TASK(task));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If 'event' is on the task's event queue, it will be purged,
|
|
|
|
* unless it is marked as unpurgeable. 'event' does not have to be
|
|
|
|
* on the task's event queue; in fact, it can even be an invalid
|
|
|
|
* pointer. Purging only occurs if the event is actually on the task's
|
|
|
|
* event queue.
|
|
|
|
*
|
|
|
|
* Purging never changes the state of the task.
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCK(&task->lock);
|
2020-02-12 13:59:18 +01:00
|
|
|
for (curr_event = HEAD(task->events); curr_event != NULL;
|
2020-02-13 14:44:37 -08:00
|
|
|
curr_event = next_event)
|
|
|
|
{
|
2000-04-17 19:22:44 +00:00
|
|
|
next_event = NEXT(curr_event, ev_link);
|
1999-07-10 01:00:05 +00:00
|
|
|
if (curr_event == event && PURGE_OK(event)) {
|
2000-04-17 19:22:44 +00:00
|
|
|
DEQUEUE(task->events, curr_event, ev_link);
|
2012-05-14 10:06:05 -07:00
|
|
|
task->nevents--;
|
1999-06-12 01:10:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
2020-02-13 21:48:23 +01:00
|
|
|
if (curr_event == NULL) {
|
2018-04-17 08:29:14 -07:00
|
|
|
return (false);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1999-06-12 01:10:32 +00:00
|
|
|
|
|
|
|
isc_event_free(&curr_event);
|
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
return (true);
|
1999-06-12 01:10:32 +00:00
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
unsigned int
|
2018-08-06 13:00:55 +02:00
|
|
|
isc_task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first,
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_eventtype_t last, void *tag, isc_eventlist_t *events) {
|
1999-07-10 01:00:05 +00:00
|
|
|
/*
|
|
|
|
* Remove events from a task's event queue.
|
|
|
|
*/
|
2018-08-06 13:00:55 +02:00
|
|
|
REQUIRE(VALID_TASK(task));
|
1999-07-10 01:00:05 +00:00
|
|
|
|
|
|
|
XTRACE("isc_task_unsendrange");
|
|
|
|
|
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
|
|
|
return (dequeue_events(task, sender, first, last, tag, events, false));
|
1999-07-10 01:00:05 +00:00
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
unsigned int
|
2020-02-12 13:59:18 +01:00
|
|
|
isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, void *tag,
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_eventlist_t *events) {
|
1999-07-10 01:00:05 +00:00
|
|
|
/*
|
|
|
|
* Remove events from a task's event queue.
|
|
|
|
*/
|
|
|
|
|
|
|
|
XTRACE("isc_task_unsend");
|
|
|
|
|
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
|
|
|
return (dequeue_events(task, sender, type, type, tag, events, false));
|
1999-07-10 01:00:05 +00:00
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
isc_result_t
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg) {
|
2020-02-13 14:44:37 -08:00
|
|
|
bool disallowed = false;
|
1998-12-16 02:02:10 +00:00
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
|
|
isc_event_t *event;
|
|
|
|
|
1999-05-18 19:23:13 +00:00
|
|
|
/*
|
|
|
|
* Send a shutdown event with action 'action' and argument 'arg' when
|
|
|
|
* 'task' is shutdown.
|
|
|
|
*/
|
|
|
|
|
1998-12-16 02:02:10 +00:00
|
|
|
REQUIRE(VALID_TASK(task));
|
1999-05-18 19:23:13 +00:00
|
|
|
REQUIRE(action != NULL);
|
1998-12-16 02:02:10 +00:00
|
|
|
|
2020-02-12 13:59:18 +01:00
|
|
|
event = isc_event_allocate(task->manager->mctx, NULL,
|
|
|
|
ISC_TASKEVENT_SHUTDOWN, action, arg,
|
2001-11-27 01:56:32 +00:00
|
|
|
sizeof(*event));
|
1998-12-16 02:02:10 +00:00
|
|
|
|
1999-09-23 21:30:26 +00:00
|
|
|
if (TASK_SHUTTINGDOWN(task)) {
|
2018-04-17 08:29:14 -07:00
|
|
|
disallowed = true;
|
1999-07-14 22:15:29 +00:00
|
|
|
result = ISC_R_SHUTTINGDOWN;
|
2019-12-12 15:22:10 +01:00
|
|
|
} else {
|
|
|
|
LOCK(&task->lock);
|
2000-04-17 19:22:44 +00:00
|
|
|
ENQUEUE(task->on_shutdown, event, ev_link);
|
2019-12-12 15:22:10 +01:00
|
|
|
UNLOCK(&task->lock);
|
|
|
|
}
|
1998-12-16 02:02:10 +00:00
|
|
|
|
2020-02-13 21:48:23 +01:00
|
|
|
if (disallowed) {
|
2001-11-27 01:56:32 +00:00
|
|
|
isc_mem_put(task->manager->mctx, event, sizeof(*event));
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1998-12-16 02:02:10 +00:00
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
void
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_shutdown(isc_task_t *task) {
|
2020-02-13 14:44:37 -08:00
|
|
|
bool was_idle;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
1999-05-18 19:23:13 +00:00
|
|
|
/*
|
|
|
|
* Shutdown 'task'.
|
|
|
|
*/
|
|
|
|
|
1998-08-17 22:05:58 +00:00
|
|
|
REQUIRE(VALID_TASK(task));
|
|
|
|
|
|
|
|
LOCK(&task->lock);
|
1999-07-10 01:00:05 +00:00
|
|
|
was_idle = task_shutdown(task);
|
1998-08-17 22:05:58 +00:00
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
2020-02-13 21:48:23 +01:00
|
|
|
if (was_idle) {
|
1999-07-10 01:00:05 +00:00
|
|
|
task_ready(task);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
1998-08-18 00:29:57 +00:00
|
|
|
}
|
1998-08-17 22:05:58 +00:00
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
void
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_task_destroy(isc_task_t **taskp) {
|
1999-05-18 19:23:13 +00:00
|
|
|
/*
|
|
|
|
* Destroy '*taskp'.
|
|
|
|
*/
|
|
|
|
|
1998-08-18 00:29:57 +00:00
|
|
|
REQUIRE(taskp != NULL);
|
|
|
|
|
1998-10-21 02:26:57 +00:00
|
|
|
isc_task_shutdown(*taskp);
|
|
|
|
isc_task_detach(taskp);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
void
|
2021-04-19 11:12:58 +02:00
|
|
|
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);
|
2017-09-14 18:11:56 +10:00
|
|
|
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
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
const char *
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_getname(isc_task_t *task) {
|
2009-09-01 00:22:28 +00:00
|
|
|
REQUIRE(VALID_TASK(task));
|
|
|
|
|
2000-07-26 17:11:14 +00:00
|
|
|
return (task->name);
|
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
void *
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_gettag(isc_task_t *task) {
|
2009-09-01 00:22:28 +00:00
|
|
|
REQUIRE(VALID_TASK(task));
|
|
|
|
|
2000-07-26 17:11:14 +00:00
|
|
|
return (task->tag);
|
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
void
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_getcurrenttime(isc_task_t *task, isc_stdtime_t *t) {
|
2003-10-25 00:09:14 +00:00
|
|
|
REQUIRE(VALID_TASK(task));
|
|
|
|
REQUIRE(t != NULL);
|
|
|
|
|
|
|
|
LOCK(&task->lock);
|
|
|
|
*t = task->now;
|
|
|
|
UNLOCK(&task->lock);
|
|
|
|
}
|
2000-07-26 17:11:14 +00:00
|
|
|
|
2015-10-02 12:32:42 -07:00
|
|
|
void
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_getcurrenttimex(isc_task_t *task, isc_time_t *t) {
|
2015-10-02 12:32:42 -07:00
|
|
|
REQUIRE(VALID_TASK(task));
|
|
|
|
REQUIRE(t != NULL);
|
|
|
|
|
|
|
|
LOCK(&task->lock);
|
|
|
|
*t = task->tnow;
|
|
|
|
UNLOCK(&task->lock);
|
|
|
|
}
|
|
|
|
|
1998-08-17 22:05:58 +00:00
|
|
|
/***
|
|
|
|
*** Task Manager.
|
|
|
|
***/
|
2011-09-02 21:15:39 +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
|
|
|
static isc_result_t
|
|
|
|
task_run(isc_task_t *task) {
|
|
|
|
unsigned int dispatch_count = 0;
|
|
|
|
bool finished = false;
|
|
|
|
isc_event_t *event = NULL;
|
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
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);
|
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
|
|
|
* 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.
|
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
|
|
|
if (task->state != task_state_ready) {
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
goto done;
|
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
|
|
|
}
|
2011-09-02 21:15:39 +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
|
|
|
INSIST(task->state == task_state_ready);
|
|
|
|
task->state = task_state_running;
|
|
|
|
XTRACE("running");
|
|
|
|
XTRACE(task->name);
|
|
|
|
TIME_NOW(&task->tnow);
|
|
|
|
task->now = isc_time_seconds(&task->tnow);
|
2000-08-01 01:33:37 +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
|
|
|
while (true) {
|
|
|
|
if (!EMPTY(task->events)) {
|
|
|
|
event = HEAD(task->events);
|
|
|
|
DEQUEUE(task->events, event, ev_link);
|
|
|
|
task->nevents--;
|
2018-10-22 09:37:17 +00:00
|
|
|
|
2018-10-23 09:39:56 +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
|
|
|
* Execute the event action.
|
2018-10-23 09:39:56 +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
|
|
|
XTRACE("execute action");
|
|
|
|
XTRACE(task->name);
|
|
|
|
if (event->ev_action != NULL) {
|
|
|
|
UNLOCK(&task->lock);
|
|
|
|
(event->ev_action)(task, event);
|
|
|
|
LOCK(&task->lock);
|
2018-10-23 08:47:44 +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
|
|
|
XTRACE("execution complete");
|
|
|
|
dispatch_count++;
|
2018-10-22 09:37:17 +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
|
|
|
if (isc_refcount_current(&task->references) == 0 &&
|
|
|
|
EMPTY(task->events) && !TASK_SHUTTINGDOWN(task))
|
|
|
|
{
|
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
|
|
|
* There are no references and no pending events for
|
|
|
|
* this task, which means it will not become runnable
|
|
|
|
* again via an external action (such as sending an
|
|
|
|
* event or detaching).
|
|
|
|
*
|
|
|
|
* We initiate shutdown to prevent it from becoming a
|
|
|
|
* zombie.
|
|
|
|
*
|
|
|
|
* We do this here instead of in the "if
|
|
|
|
* EMPTY(task->events)" block below because:
|
|
|
|
*
|
|
|
|
* If we post no shutdown events, we want the task
|
|
|
|
* to finish.
|
|
|
|
*
|
|
|
|
* If we did post shutdown events, will still want
|
|
|
|
* the task's quantum to be applied.
|
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
|
|
|
INSIST(!task_shutdown(task));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (EMPTY(task->events)) {
|
2020-01-28 09:08:10 +01: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
|
|
|
* Nothing else to do for this task right now.
|
2020-01-28 09:08:10 +01: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
|
|
|
XTRACE("empty");
|
|
|
|
if (isc_refcount_current(&task->references) == 0 &&
|
|
|
|
TASK_SHUTTINGDOWN(task)) {
|
|
|
|
/*
|
|
|
|
* The task is done.
|
|
|
|
*/
|
|
|
|
XTRACE("done");
|
|
|
|
task->state = task_state_done;
|
|
|
|
} else {
|
|
|
|
if (task->state == task_state_running) {
|
|
|
|
XTRACE("idling");
|
|
|
|
task->state = task_state_idle;
|
2020-01-28 09:08:10 +01:00
|
|
|
} else if (task->state == task_state_pausing) {
|
|
|
|
XTRACE("pausing");
|
|
|
|
task->state = task_state_paused;
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
2020-02-13 21:48:23 +01: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
|
|
|
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) {
|
2018-10-25 12:41:59 +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
|
|
|
* Our quantum has expired, but there is more work to be
|
|
|
|
* done. We'll requeue it to the ready queue later.
|
2018-10-25 12:41:59 +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
|
|
|
* We don't check quantum until dispatching at least one
|
|
|
|
* event, so the minimum quantum is one.
|
2018-10-25 12:41:59 +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
|
|
|
XTRACE("quantum");
|
|
|
|
task->state = task_state_ready;
|
|
|
|
result = ISC_R_QUOTA;
|
|
|
|
break;
|
2011-09-02 21:15:39 +00:00
|
|
|
}
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
|
|
|
|
done:
|
|
|
|
if (isc_refcount_decrement(&task->running) == 1 &&
|
|
|
|
task->state == task_state_done)
|
|
|
|
{
|
|
|
|
finished = true;
|
|
|
|
}
|
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
|
|
|
|
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 (finished) {
|
|
|
|
task_finished(task);
|
|
|
|
}
|
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
|
|
|
return (result);
|
|
|
|
}
|
2009-10-05 17:30:49 +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
|
|
|
isc_result_t
|
|
|
|
isc_task_run(isc_task_t *task) {
|
|
|
|
return (task_run(task));
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2021-04-19 11:12:58 +02:00
|
|
|
manager_free(isc_taskmgr_t *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
|
|
|
isc_refcount_destroy(&manager->references);
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
isc_nm_detach(&manager->netmgr);
|
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
|
|
|
|
2018-11-19 10:31:09 +00:00
|
|
|
isc_mutex_destroy(&manager->lock);
|
2020-05-28 12:34:37 +02:00
|
|
|
isc_mutex_destroy(&manager->excl_lock);
|
2021-04-19 11:12:58 +02:00
|
|
|
manager->magic = 0;
|
2018-10-22 09:37:17 +00:00
|
|
|
isc_mem_putanddetach(&manager->mctx, manager, sizeof(*manager));
|
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_MANAGER(source));
|
|
|
|
REQUIRE(targetp != NULL && *targetp == NULL);
|
|
|
|
|
|
|
|
isc_refcount_increment(&source->references);
|
|
|
|
|
|
|
|
*targetp = source;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-05-10 11:36:54 +02:00
|
|
|
isc_taskmgr_detach(isc_taskmgr_t **managerp) {
|
|
|
|
REQUIRE(managerp != NULL);
|
|
|
|
REQUIRE(VALID_MANAGER(*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) {
|
|
|
|
manager_free(manager);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
isc_result_t
|
2021-04-27 00:07:43 +02:00
|
|
|
isc__taskmgr_create(isc_mem_t *mctx, unsigned int default_quantum, isc_nm_t *nm,
|
|
|
|
isc_taskmgr_t **managerp) {
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_taskmgr_t *manager;
|
1998-10-22 01:33:20 +00:00
|
|
|
|
1999-05-18 19:23:13 +00:00
|
|
|
/*
|
|
|
|
* Create a new task manager.
|
|
|
|
*/
|
|
|
|
|
1999-03-07 19:21:49 +00:00
|
|
|
REQUIRE(managerp != NULL && *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
|
|
|
REQUIRE(nm != NULL);
|
1998-08-17 22:05:58 +00:00
|
|
|
|
2001-11-27 01:56:32 +00:00
|
|
|
manager = isc_mem_get(mctx, sizeof(*manager));
|
2021-04-19 11:12:58 +02:00
|
|
|
*manager = (isc_taskmgr_t){ .magic = TASK_MANAGER_MAGIC };
|
2019-11-05 13:55:54 -08:00
|
|
|
|
2018-11-16 15:33:22 +01:00
|
|
|
isc_mutex_init(&manager->lock);
|
|
|
|
isc_mutex_init(&manager->excl_lock);
|
2018-10-22 09:37:17 +00:00
|
|
|
|
2018-11-08 19:34:51 -08:00
|
|
|
if (default_quantum == 0) {
|
2000-08-29 22:30:14 +00:00
|
|
|
default_quantum = DEFAULT_DEFAULT_QUANTUM;
|
2018-11-08 19:34:51 -08:00
|
|
|
}
|
2000-08-29 22:30:14 +00:00
|
|
|
manager->default_quantum = default_quantum;
|
2019-11-05 15:23:33 -08:00
|
|
|
|
|
|
|
if (nm != NULL) {
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
isc_nm_attach(nm, &manager->netmgr);
|
2019-11-05 15:23:33 -08:00
|
|
|
}
|
|
|
|
|
2000-08-29 22:30:14 +00:00
|
|
|
INIT_LIST(manager->tasks);
|
2019-07-08 17:30:06 +02:00
|
|
|
atomic_init(&manager->exiting, false);
|
2021-05-06 19:41:49 -07:00
|
|
|
atomic_init(&manager->mode, isc_taskmgrmode_normal);
|
2019-05-20 17:00:22 +02:00
|
|
|
atomic_store_relaxed(&manager->exclusive_req, false);
|
1998-08-17 22:05:58 +00:00
|
|
|
|
2000-04-12 01:34:16 +00:00
|
|
|
isc_mem_attach(mctx, &manager->mctx);
|
|
|
|
|
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_init(&manager->references, 1);
|
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
|
|
|
*managerp = manager;
|
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
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
void
|
2021-04-27 10:28:40 +02:00
|
|
|
isc__taskmgr_shutdown(isc_taskmgr_t *manager) {
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_t *task;
|
1998-08-17 22:05:58 +00:00
|
|
|
|
|
|
|
REQUIRE(VALID_MANAGER(manager));
|
|
|
|
|
2021-05-05 11:51:39 +02:00
|
|
|
XTHREADTRACE("isc_taskmgr_shutdown");
|
1998-08-17 22:05:58 +00:00
|
|
|
/*
|
|
|
|
* Only one non-worker thread may ever call this routine.
|
|
|
|
* If a worker thread wants to initiate shutdown of the
|
|
|
|
* task manager, it should ask some non-worker thread to call
|
1998-10-21 02:26:57 +00:00
|
|
|
* isc_taskmgr_destroy(), e.g. by signalling a condition variable
|
1998-08-17 22:05:58 +00:00
|
|
|
* that the startup thread is sleeping on.
|
|
|
|
*/
|
|
|
|
|
2012-07-19 23:00:21 +10:00
|
|
|
/*
|
|
|
|
* Detach the exclusive task before acquiring the manager lock
|
|
|
|
*/
|
2015-02-27 12:34:43 +11:00
|
|
|
LOCK(&manager->excl_lock);
|
2020-02-13 21:48:23 +01:00
|
|
|
if (manager->excl != NULL) {
|
2020-02-12 13:59:18 +01:00
|
|
|
isc_task_detach((isc_task_t **)&manager->excl);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2015-02-27 12:34:43 +11:00
|
|
|
UNLOCK(&manager->excl_lock);
|
2012-07-19 23:00:21 +10:00
|
|
|
|
1998-08-17 22:05:58 +00:00
|
|
|
/*
|
|
|
|
* Unlike elsewhere, we're going to hold this lock a long time.
|
|
|
|
* We need to do so, because otherwise the list of tasks could
|
|
|
|
* change while we were traversing it.
|
|
|
|
*
|
|
|
|
* This is also the only function where we will hold both the
|
|
|
|
* task manager lock and a task lock at the same time.
|
|
|
|
*/
|
|
|
|
|
|
|
|
LOCK(&manager->lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure we only get called once.
|
|
|
|
*/
|
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
|
|
|
INSIST(atomic_compare_exchange_strong(&manager->exiting,
|
|
|
|
&(bool){ false }, true));
|
2011-09-02 21:15:39 +00:00
|
|
|
|
1998-08-17 22:05:58 +00:00
|
|
|
/*
|
1999-05-10 23:00:30 +00:00
|
|
|
* Post shutdown event(s) to every task (if they haven't already been
|
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
|
|
|
* posted).
|
1998-08-17 22:05:58 +00:00
|
|
|
*/
|
2020-02-13 14:44:37 -08:00
|
|
|
for (task = HEAD(manager->tasks); task != NULL; task = NEXT(task, link))
|
|
|
|
{
|
1998-08-17 22:05:58 +00:00
|
|
|
LOCK(&task->lock);
|
2018-10-18 18:16:25 +00:00
|
|
|
if (task_shutdown(task)) {
|
2018-10-22 12:26:27 +00:00
|
|
|
task->threadid = 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
|
|
|
task_ready(task);
|
2018-10-18 18:16:25 +00:00
|
|
|
}
|
1998-08-17 22:05:58 +00:00
|
|
|
UNLOCK(&task->lock);
|
|
|
|
}
|
2018-10-22 12:26:27 +00:00
|
|
|
|
1998-10-23 23:50:15 +00:00
|
|
|
UNLOCK(&manager->lock);
|
2021-04-27 10:28:40 +02:00
|
|
|
}
|
1998-08-17 22:05:58 +00:00
|
|
|
|
2021-04-27 10:28:40 +02:00
|
|
|
void
|
|
|
|
isc__taskmgr_destroy(isc_taskmgr_t **managerp) {
|
|
|
|
REQUIRE(managerp != NULL && VALID_MANAGER(*managerp));
|
1998-08-17 22:05:58 +00:00
|
|
|
|
2021-04-27 10:28:40 +02:00
|
|
|
isc_taskmgr_t *manager = *managerp;
|
1998-08-17 22:05:58 +00:00
|
|
|
*managerp = NULL;
|
2021-04-27 10:28:40 +02:00
|
|
|
|
|
|
|
XTHREADTRACE("isc_taskmgr_destroy");
|
|
|
|
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
#ifdef ISC_TASK_TRACE
|
|
|
|
int counter = 0;
|
|
|
|
while (isc_refcount_current(&manager->references) > 1 &&
|
|
|
|
counter++ < 1000) {
|
|
|
|
usleep(10 * 1000);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
while (isc_refcount_current(&manager->references) > 1) {
|
|
|
|
usleep(10 * 1000);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-04-27 10:28:40 +02:00
|
|
|
REQUIRE(isc_refcount_decrement(&manager->references) == 1);
|
|
|
|
manager_free(manager);
|
1998-08-17 22:05:58 +00:00
|
|
|
}
|
2000-08-29 22:30:14 +00:00
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
void
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_taskmgr_setexcltask(isc_taskmgr_t *mgr, isc_task_t *task) {
|
2012-07-19 23:00:21 +10:00
|
|
|
REQUIRE(VALID_MANAGER(mgr));
|
|
|
|
REQUIRE(VALID_TASK(task));
|
2021-05-06 16:11:43 +02:00
|
|
|
REQUIRE(task->threadid == 0);
|
|
|
|
|
2015-02-27 12:34:43 +11:00
|
|
|
LOCK(&mgr->excl_lock);
|
2020-02-13 21:48:23 +01:00
|
|
|
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);
|
2020-02-13 21:48:23 +01: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
|
|
|
isc_task_attach(task, &mgr->excl);
|
2015-02-27 12:34:43 +11:00
|
|
|
UNLOCK(&mgr->excl_lock);
|
2012-07-19 23:00:21 +10:00
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
isc_result_t
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_taskmgr_excltask(isc_taskmgr_t *mgr, isc_task_t **taskp) {
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
2012-07-19 23:00:21 +10:00
|
|
|
|
|
|
|
REQUIRE(VALID_MANAGER(mgr));
|
|
|
|
REQUIRE(taskp != NULL && *taskp == NULL);
|
|
|
|
|
2015-02-27 12:34:43 +11:00
|
|
|
LOCK(&mgr->excl_lock);
|
2020-02-13 21:48:23 +01:00
|
|
|
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);
|
2020-02-13 21:48:23 +01:00
|
|
|
} else {
|
2015-02-27 12:34:43 +11:00
|
|
|
result = ISC_R_NOTFOUND;
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2015-02-27 12:34:43 +11:00
|
|
|
UNLOCK(&mgr->excl_lock);
|
2012-07-19 23:00:21 +10:00
|
|
|
|
2015-02-27 12:34:43 +11:00
|
|
|
return (result);
|
2012-07-19 23:00:21 +10:00
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
isc_result_t
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_beginexclusive(isc_task_t *task) {
|
|
|
|
isc_taskmgr_t *manager;
|
2018-10-22 09:37:17 +00:00
|
|
|
|
2018-08-06 13:00:55 +02:00
|
|
|
REQUIRE(VALID_TASK(task));
|
2012-07-19 23:00:21 +10:00
|
|
|
|
2019-09-27 12:02:41 +02:00
|
|
|
manager = task->manager;
|
|
|
|
|
2001-02-13 04:11:44 +00:00
|
|
|
REQUIRE(task->state == task_state_running);
|
2018-10-24 13:12:55 -07:00
|
|
|
|
|
|
|
LOCK(&manager->excl_lock);
|
2018-10-23 09:39:56 +00:00
|
|
|
REQUIRE(task == task->manager->excl ||
|
2019-01-18 11:47:43 +01:00
|
|
|
(atomic_load_relaxed(&task->manager->exiting) &&
|
2020-02-12 13:59:18 +01:00
|
|
|
task->manager->excl == NULL));
|
2018-10-24 13:12:55 -07:00
|
|
|
UNLOCK(&manager->excl_lock);
|
2012-07-19 23:00:21 +10: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
|
|
|
if (!atomic_compare_exchange_strong(&manager->exclusive_req,
|
|
|
|
&(bool){ false }, true))
|
2020-02-13 14:44:37 -08:00
|
|
|
{
|
2001-02-13 04:11:44 +00:00
|
|
|
return (ISC_R_LOCKBUSY);
|
|
|
|
}
|
2018-10-22 09:37:17 +00:00
|
|
|
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
isc_nm_pause(manager->netmgr);
|
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
|
|
|
|
2001-02-13 04:11:44 +00:00
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
void
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_endexclusive(isc_task_t *task) {
|
|
|
|
isc_taskmgr_t *manager;
|
2009-09-01 00:22:28 +00:00
|
|
|
|
2018-08-06 13:00:55 +02:00
|
|
|
REQUIRE(VALID_TASK(task));
|
2001-02-13 04:11:44 +00:00
|
|
|
REQUIRE(task->state == task_state_running);
|
2019-09-27 12:02:41 +02:00
|
|
|
manager = task->manager;
|
|
|
|
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
isc_nm_resume(manager->netmgr);
|
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(atomic_compare_exchange_strong(&manager->exclusive_req,
|
|
|
|
&(bool){ true }, false));
|
2001-02-13 04:11:44 +00:00
|
|
|
}
|
2007-02-13 02:49:08 +00:00
|
|
|
|
2019-11-05 15:23:33 -08:00
|
|
|
void
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_pause(isc_task_t *task) {
|
|
|
|
REQUIRE(VALID_TASK(task));
|
2019-11-05 15:23:33 -08:00
|
|
|
|
|
|
|
LOCK(&task->lock);
|
2020-01-28 09:08:10 +01:00
|
|
|
task->pause_cnt++;
|
|
|
|
if (task->pause_cnt > 1) {
|
|
|
|
/*
|
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
|
|
|
* Someone already paused this task, just increase
|
2020-01-28 09:08:10 +01:00
|
|
|
* the number of pausing clients.
|
|
|
|
*/
|
|
|
|
UNLOCK(&task->lock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-05 15:23:33 -08:00
|
|
|
INSIST(task->state == task_state_idle ||
|
|
|
|
task->state == task_state_ready ||
|
|
|
|
task->state == task_state_running);
|
Fix a race in taskmgr between worker and task pausing/unpausing.
To reproduce the race - create a task, send two events to it, first one
must take some time. Then, from the outside, pause(), unpause() and detach()
the task.
When the long-running event is processed by the task it is in
task_state_running state. When we called pause() the state changed to
task_state_paused, on unpause we checked that there are events in the task
queue, changed the state to task_state_ready and enqueued the task on the
workers readyq. We then detach the task.
The dispatch() is done with processing the event, it processes the second
event in the queue, and then shuts down the task and frees it (as it's not
referenced anymore). Dispatcher then takes the, already freed, task from
the queue where it was wrongly put, causing an use-after free and,
subsequently, either an assertion failure or a segmentation fault.
The probability of this happening is very slim, yet it might happen under a
very high load, more probably on a recursive resolver than on an
authoritative.
The fix introduces a new 'task_state_pausing' state - to which tasks
are moved if they're being paused while still running. They are moved
to task_state_paused state when dispatcher is done with them, and
if we unpause a task in paused state it's moved back to task_state_running
and not requeued.
2020-01-20 11:39:14 +01:00
|
|
|
if (task->state == task_state_running) {
|
|
|
|
task->state = task_state_pausing;
|
|
|
|
} else {
|
|
|
|
task->state = task_state_paused;
|
|
|
|
}
|
2019-11-05 15:23:33 -08:00
|
|
|
UNLOCK(&task->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_unpause(isc_task_t *task) {
|
2020-02-13 14:44:37 -08:00
|
|
|
bool was_idle = false;
|
2019-11-05 15:23:33 -08:00
|
|
|
|
2021-04-19 11:12:58 +02:00
|
|
|
REQUIRE(VALID_TASK(task));
|
2019-11-05 15:23:33 -08:00
|
|
|
|
|
|
|
LOCK(&task->lock);
|
2020-01-28 09:08:10 +01:00
|
|
|
task->pause_cnt--;
|
|
|
|
INSIST(task->pause_cnt >= 0);
|
|
|
|
if (task->pause_cnt > 0) {
|
|
|
|
UNLOCK(&task->lock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
Fix a race in taskmgr between worker and task pausing/unpausing.
To reproduce the race - create a task, send two events to it, first one
must take some time. Then, from the outside, pause(), unpause() and detach()
the task.
When the long-running event is processed by the task it is in
task_state_running state. When we called pause() the state changed to
task_state_paused, on unpause we checked that there are events in the task
queue, changed the state to task_state_ready and enqueued the task on the
workers readyq. We then detach the task.
The dispatch() is done with processing the event, it processes the second
event in the queue, and then shuts down the task and frees it (as it's not
referenced anymore). Dispatcher then takes the, already freed, task from
the queue where it was wrongly put, causing an use-after free and,
subsequently, either an assertion failure or a segmentation fault.
The probability of this happening is very slim, yet it might happen under a
very high load, more probably on a recursive resolver than on an
authoritative.
The fix introduces a new 'task_state_pausing' state - to which tasks
are moved if they're being paused while still running. They are moved
to task_state_paused state when dispatcher is done with them, and
if we unpause a task in paused state it's moved back to task_state_running
and not requeued.
2020-01-20 11:39:14 +01:00
|
|
|
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;
|
2019-11-05 15:23:33 -08:00
|
|
|
} else {
|
|
|
|
task->state = task_state_idle;
|
|
|
|
}
|
Fix a race in taskmgr between worker and task pausing/unpausing.
To reproduce the race - create a task, send two events to it, first one
must take some time. Then, from the outside, pause(), unpause() and detach()
the task.
When the long-running event is processed by the task it is in
task_state_running state. When we called pause() the state changed to
task_state_paused, on unpause we checked that there are events in the task
queue, changed the state to task_state_ready and enqueued the task on the
workers readyq. We then detach the task.
The dispatch() is done with processing the event, it processes the second
event in the queue, and then shuts down the task and frees it (as it's not
referenced anymore). Dispatcher then takes the, already freed, task from
the queue where it was wrongly put, causing an use-after free and,
subsequently, either an assertion failure or a segmentation fault.
The probability of this happening is very slim, yet it might happen under a
very high load, more probably on a recursive resolver than on an
authoritative.
The fix introduces a new 'task_state_pausing' state - to which tasks
are moved if they're being paused while still running. They are moved
to task_state_paused state when dispatcher is done with them, and
if we unpause a task in paused state it's moved back to task_state_running
and not requeued.
2020-01-20 11:39:14 +01:00
|
|
|
if (task->state == task_state_idle && !EMPTY(task->events)) {
|
|
|
|
task->state = task_state_ready;
|
|
|
|
was_idle = true;
|
|
|
|
}
|
2019-11-05 15:23:33 -08:00
|
|
|
UNLOCK(&task->lock);
|
|
|
|
|
|
|
|
if (was_idle) {
|
|
|
|
task_ready(task);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-06 19:41:49 -07:00
|
|
|
void
|
|
|
|
isc_taskmgr_setmode(isc_taskmgr_t *manager, isc_taskmgrmode_t mode) {
|
|
|
|
atomic_store(&manager->mode, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
isc_taskmgrmode_t
|
|
|
|
isc_taskmgr_mode(isc_taskmgr_t *manager) {
|
|
|
|
return (atomic_load(&manager->mode));
|
|
|
|
}
|
|
|
|
|
2013-04-10 13:49:57 -07:00
|
|
|
void
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_task_setprivilege(isc_task_t *task, bool priv) {
|
|
|
|
REQUIRE(VALID_TASK(task));
|
Make the netmgr queue processing quantized
There was a theoretical possibility of clogging up the queue processing
with an endless loop where currently processing netievent would schedule
new netievent that would get processed immediately. This wasn't such a
problem when only netmgr netievents were processed, but with the
addition of the tasks, there are at least two situation where this could
happen:
1. In lib/dns/zone.c:setnsec3param() the task would get re-enqueued
when the zone was not yet fully loaded.
2. Tasks have internal quantum for maximum number of isc_events to be
processed, when the task quantum is reached, the task would get
rescheduled and then immediately processed by the netmgr queue
processing.
As the isc_queue doesn't have a mechanism to atomically move the queue,
this commit adds a mechanism to quantize the queue, so enqueueing new
netievents will never stop processing other uv_loop_t events.
The default quantum size is 128.
Since the queue used in the network manager allows items to be enqueued
more than once, tasks are now reference-counted around task_ready()
and task_run(). task_ready() now has a public API wrapper,
isc_task_ready(), that the netmgr can use to reschedule processing
of a task if the quantum has been reached.
Incidental changes: Cleaned up some unused fields left in isc_task_t
and isc_taskmgr_t after the last refactoring, and changed atomic
flags to atomic_bools for easier manipulation.
2021-04-27 12:03:34 +02:00
|
|
|
|
|
|
|
atomic_store_release(&task->privileged, priv);
|
2011-09-02 21:15:39 +00:00
|
|
|
}
|
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
bool
|
2021-05-06 19:41:49 -07:00
|
|
|
isc_task_getprivilege(isc_task_t *task) {
|
2018-08-06 13:00:55 +02:00
|
|
|
REQUIRE(VALID_TASK(task));
|
2011-09-02 21:15:39 +00:00
|
|
|
|
2019-12-12 15:22:10 +01:00
|
|
|
return (TASK_PRIVILEGED(task));
|
2011-09-02 21:15:39 +00:00
|
|
|
}
|
|
|
|
|
2021-05-06 19:41:49 -07:00
|
|
|
bool
|
|
|
|
isc_task_privileged(isc_task_t *task) {
|
|
|
|
REQUIRE(VALID_TASK(task));
|
|
|
|
|
|
|
|
return (isc_taskmgr_mode(task->manager) && TASK_PRIVILEGED(task));
|
|
|
|
}
|
|
|
|
|
2018-04-17 08:29:14 -07:00
|
|
|
bool
|
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_exiting(isc_task_t *task) {
|
2010-12-03 22:05:19 +00:00
|
|
|
REQUIRE(VALID_TASK(task));
|
2019-12-12 15:22:10 +01:00
|
|
|
|
2010-12-03 22:05:19 +00:00
|
|
|
return (TASK_SHUTTINGDOWN(task));
|
|
|
|
}
|
|
|
|
|
2013-03-13 14:24:50 -07:00
|
|
|
#ifdef HAVE_LIBXML2
|
2020-02-12 13:59:18 +01:00
|
|
|
#define TRY0(a) \
|
|
|
|
do { \
|
|
|
|
xmlrc = (a); \
|
|
|
|
if (xmlrc < 0) \
|
|
|
|
goto error; \
|
|
|
|
} while (0)
|
2012-11-01 10:22:11 +11:00
|
|
|
int
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_taskmgr_renderxml(isc_taskmgr_t *mgr, void *writer0) {
|
|
|
|
isc_task_t *task = NULL;
|
2020-02-13 14:44:37 -08:00
|
|
|
int xmlrc;
|
2019-06-24 14:25:55 +02:00
|
|
|
xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0;
|
2007-02-13 02:49:08 +00:00
|
|
|
|
|
|
|
LOCK(&mgr->lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write out the thread-model, and some details about each depending
|
|
|
|
* on which type is enabled.
|
|
|
|
*/
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "thread-model"));
|
|
|
|
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "type"));
|
|
|
|
TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "threaded"));
|
|
|
|
TRY0(xmlTextWriterEndElement(writer)); /* type */
|
2007-02-13 02:49:08 +00:00
|
|
|
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "default-quantum"));
|
|
|
|
TRY0(xmlTextWriterWriteFormatString(writer, "%d",
|
|
|
|
mgr->default_quantum));
|
|
|
|
TRY0(xmlTextWriterEndElement(writer)); /* default-quantum */
|
2007-02-13 02:49:08 +00:00
|
|
|
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterEndElement(writer)); /* thread-model */
|
2007-02-13 02:49:08 +00:00
|
|
|
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks"));
|
2007-02-13 02:49:08 +00:00
|
|
|
task = ISC_LIST_HEAD(mgr->tasks);
|
|
|
|
while (task != NULL) {
|
|
|
|
LOCK(&task->lock);
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "task"));
|
2007-02-13 02:49:08 +00:00
|
|
|
|
|
|
|
if (task->name[0] != 0) {
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterStartElement(writer,
|
|
|
|
ISC_XMLCHAR "name"));
|
|
|
|
TRY0(xmlTextWriterWriteFormatString(writer, "%s",
|
2020-02-12 13:59:18 +01:00
|
|
|
task->name));
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterEndElement(writer)); /* name */
|
2007-02-13 02:49:08 +00:00
|
|
|
}
|
|
|
|
|
2020-02-12 13:59:18 +01:00
|
|
|
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "reference"
|
|
|
|
"s"));
|
|
|
|
TRY0(xmlTextWriterWriteFormatString(
|
|
|
|
writer, "%" PRIuFAST32,
|
|
|
|
isc_refcount_current(&task->references)));
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterEndElement(writer)); /* references */
|
2007-02-13 02:49:08 +00:00
|
|
|
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id"));
|
|
|
|
TRY0(xmlTextWriterWriteFormatString(writer, "%p", task));
|
|
|
|
TRY0(xmlTextWriterEndElement(writer)); /* id */
|
2007-02-13 02:49:08 +00:00
|
|
|
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "state"));
|
|
|
|
TRY0(xmlTextWriterWriteFormatString(writer, "%s",
|
2020-02-12 13:59:18 +01:00
|
|
|
statenames[task->state]));
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterEndElement(writer)); /* state */
|
2007-02-13 02:49:08 +00:00
|
|
|
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "quantum"));
|
|
|
|
TRY0(xmlTextWriterWriteFormatString(writer, "%d",
|
|
|
|
task->quantum));
|
|
|
|
TRY0(xmlTextWriterEndElement(writer)); /* quantum */
|
2007-02-13 02:49:08 +00:00
|
|
|
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "events"));
|
|
|
|
TRY0(xmlTextWriterWriteFormatString(writer, "%d",
|
|
|
|
task->nevents));
|
|
|
|
TRY0(xmlTextWriterEndElement(writer)); /* events */
|
2012-05-14 10:06:05 -07:00
|
|
|
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterEndElement(writer));
|
2007-02-13 02:49:08 +00:00
|
|
|
|
|
|
|
UNLOCK(&task->lock);
|
|
|
|
task = ISC_LIST_NEXT(task, link);
|
|
|
|
}
|
2012-11-01 10:22:11 +11:00
|
|
|
TRY0(xmlTextWriterEndElement(writer)); /* tasks */
|
2007-02-13 02:49:08 +00:00
|
|
|
|
2020-02-12 13:59:18 +01:00
|
|
|
error:
|
2020-02-13 21:48:23 +01:00
|
|
|
if (task != NULL) {
|
2012-11-01 10:22:11 +11:00
|
|
|
UNLOCK(&task->lock);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2007-02-13 02:49:08 +00:00
|
|
|
UNLOCK(&mgr->lock);
|
2012-11-01 10:22:11 +11:00
|
|
|
|
|
|
|
return (xmlrc);
|
2007-02-13 02:49:08 +00:00
|
|
|
}
|
2013-03-13 14:24:50 -07:00
|
|
|
#endif /* HAVE_LIBXML2 */
|
|
|
|
|
2019-02-06 11:56:42 +01:00
|
|
|
#ifdef HAVE_JSON_C
|
2020-02-12 13:59:18 +01:00
|
|
|
#define CHECKMEM(m) \
|
|
|
|
do { \
|
|
|
|
if (m == NULL) { \
|
|
|
|
result = ISC_R_NOMEMORY; \
|
|
|
|
goto error; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
2013-03-13 14:24:50 -07:00
|
|
|
|
|
|
|
isc_result_t
|
2021-04-19 11:12:58 +02:00
|
|
|
isc_taskmgr_renderjson(isc_taskmgr_t *mgr, void *tasks0) {
|
2020-02-13 14:44:37 -08:00
|
|
|
isc_result_t result = ISC_R_SUCCESS;
|
2021-04-19 11:12:58 +02:00
|
|
|
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;
|
2013-03-13 14:24:50 -07:00
|
|
|
|
|
|
|
LOCK(&mgr->lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write out the thread-model, and some details about each depending
|
|
|
|
* on which type is enabled.
|
|
|
|
*/
|
|
|
|
obj = json_object_new_string("threaded");
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(tasks, "thread-model", obj);
|
|
|
|
|
|
|
|
obj = json_object_new_int(mgr->default_quantum);
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(tasks, "default-quantum", obj);
|
|
|
|
|
|
|
|
array = json_object_new_array();
|
|
|
|
CHECKMEM(array);
|
|
|
|
|
2020-02-12 13:59:18 +01:00
|
|
|
for (task = ISC_LIST_HEAD(mgr->tasks); task != NULL;
|
2020-02-13 14:44:37 -08:00
|
|
|
task = ISC_LIST_NEXT(task, link))
|
|
|
|
{
|
2013-03-13 14:24:50 -07:00
|
|
|
char buf[255];
|
|
|
|
|
|
|
|
LOCK(&task->lock);
|
|
|
|
|
|
|
|
taskobj = json_object_new_object();
|
|
|
|
CHECKMEM(taskobj);
|
|
|
|
json_object_array_add(array, taskobj);
|
|
|
|
|
2017-10-03 14:54:19 +11:00
|
|
|
snprintf(buf, sizeof(buf), "%p", task);
|
2013-03-13 14:24:50 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-02-12 13:59:18 +01:00
|
|
|
obj = json_object_new_int(
|
|
|
|
isc_refcount_current(&task->references));
|
2013-03-13 14:24:50 -07:00
|
|
|
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);
|
|
|
|
|
|
|
|
obj = json_object_new_int(task->quantum);
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(taskobj, "quantum", obj);
|
|
|
|
|
|
|
|
obj = json_object_new_int(task->nevents);
|
|
|
|
CHECKMEM(obj);
|
|
|
|
json_object_object_add(taskobj, "events", obj);
|
|
|
|
|
|
|
|
UNLOCK(&task->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
json_object_object_add(tasks, "tasks", array);
|
|
|
|
array = NULL;
|
|
|
|
result = ISC_R_SUCCESS;
|
|
|
|
|
2020-02-12 13:59:18 +01:00
|
|
|
error:
|
2020-02-13 21:48:23 +01:00
|
|
|
if (array != NULL) {
|
2013-03-13 14:24:50 -07:00
|
|
|
json_object_put(array);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2013-03-13 14:24:50 -07:00
|
|
|
|
2020-02-13 21:48:23 +01:00
|
|
|
if (task != NULL) {
|
2013-03-13 14:24:50 -07:00
|
|
|
UNLOCK(&task->lock);
|
2020-02-13 21:48:23 +01:00
|
|
|
}
|
2013-03-13 14:24:50 -07:00
|
|
|
UNLOCK(&mgr->lock);
|
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
2020-02-13 21:48:23 +01:00
|
|
|
#endif /* ifdef HAVE_JSON_C */
|