]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
add isc_task_pause() and isc_task_unpause() functions
authorEvan Hunt <each@isc.org>
Tue, 5 Nov 2019 23:23:33 +0000 (15:23 -0800)
committerEvan Hunt <each@isc.org>
Thu, 7 Nov 2019 19:55:37 +0000 (11:55 -0800)
This allows a task to be temporary disabled so that objects won't be
processed simultaneously by libuv events and isc_task events. When a
task is paused, currently running events may complete, but no further
event will added to the run queue will be executed until the task is
unpaused.

lib/isc/include/isc/task.h
lib/isc/task.c

index b2d931ef369421ed92aad511500f0f0aa97d2024..40fa7db29cfe07d3babcfcd7b006a7cae0de9f65 100644 (file)
@@ -571,6 +571,22 @@ isc_task_endexclusive(isc_task_t *task);
  *             exclusive access by calling isc_task_spl().
  */
 
+void
+isc_task_pause(isc_task_t *task0);
+void
+isc_task_unpause(isc_task_t *task0);
+/*%<
+ * Pause/unpause this task. Pausing a task removes it from the ready
+ * queue if it is present there; this ensures that the task will not
+ * run again until unpaused. This is necessary when the libuv network
+ * thread executes a function which schedules task manager events; this
+ * prevents the task manager from executing the next event in a task
+ * before the network thread has finished.
+ *
+ * Requires:
+ *\li  'task' is a valid task, and is not already paused or shutting down.
+ */
+
 void
 isc_task_getcurrenttime(isc_task_t *task, isc_stdtime_t *t);
 void
index 538fde35b6eb77f22b68d03ff98cc0ca0574d8cf..81009538c03babf153ad81bb7fb145c1d5b826e3 100644 (file)
@@ -78,8 +78,8 @@
  ***/
 
 typedef enum {
-       task_state_idle, task_state_ready, task_state_running,
-       task_state_done
+       task_state_idle, task_state_ready, task_state_paused,
+       task_state_running, task_state_done
 } task_state_t;
 
 #if defined(HAVE_LIBXML2) || defined(HAVE_JSON_C)
@@ -371,6 +371,7 @@ task_shutdown(isc__task_t *task) {
                        was_idle = true;
                }
                INSIST(task->state == task_state_ready ||
+                      task->state == task_state_paused ||
                       task->state == task_state_running);
 
                /*
@@ -491,7 +492,8 @@ task_send(isc__task_t *task, isc_event_t **eventp, int c) {
                task->state = task_state_ready;
        }
        INSIST(task->state == task_state_ready ||
-              task->state == task_state_running);
+              task->state == task_state_running ||
+              task->state == task_state_paused);
        ENQUEUE(task->events, event, ev_link);
        task->nevents++;
        *eventp = NULL;
@@ -1134,12 +1136,15 @@ dispatch(isc__taskmgr_t *manager, unsigned int threadid) {
                                                        event);
                                                LOCK(&task->lock);
                                        }
+                                       XTRACE("execution complete");
                                        dispatch_count++;
                                }
 
-                               if (isc_refcount_current(&task->references) == 0 &&
+                               if (isc_refcount_current(
+                                                &task->references) == 0 &&
                                    EMPTY(task->events) &&
-                                   !TASK_SHUTTINGDOWN(task)) {
+                                   !TASK_SHUTTINGDOWN(task))
+                               {
                                        bool was_idle;
 
                                        /*
@@ -1174,16 +1179,19 @@ dispatch(isc__taskmgr_t *manager, unsigned int threadid) {
                                         * right now.
                                         */
                                        XTRACE("empty");
-                                       if (isc_refcount_current(&task->references) == 0 &&
-                                           TASK_SHUTTINGDOWN(task)) {
+                                       if (isc_refcount_current(
+                                                    &task->references) == 0 &&
+                                           TASK_SHUTTINGDOWN(task))
+                                       {
                                                /*
                                                 * The task is done.
                                                 */
                                                XTRACE("done");
                                                finished = true;
                                                task->state = task_state_done;
-                                       } else
+                                       } else {
                                                task->state = task_state_idle;
+                                       }
                                        done = true;
                                } else if (dispatch_count >= task->quantum) {
                                        /*
@@ -1643,6 +1651,55 @@ isc_task_endexclusive(isc_task_t *task0) {
        UNLOCK(&manager->halt_lock);
 }
 
+void
+isc_task_pause(isc_task_t *task0) {
+       REQUIRE(ISCAPI_TASK_VALID(task0));
+       isc__task_t *task = (isc__task_t *)task0;
+       isc__taskmgr_t *manager = task->manager;
+       bool running = false;
+
+       LOCK(&task->lock);
+       INSIST(task->state == task_state_idle ||
+              task->state == task_state_ready ||
+              task->state == task_state_running);
+       running = (task->state == task_state_running);
+       task->state = task_state_paused;
+       UNLOCK(&task->lock);
+
+       if (running) {
+               return;
+       }
+
+       LOCK(&manager->queues[task->threadid].lock);
+       if (ISC_LINK_LINKED(task, ready_link)) {
+               DEQUEUE(manager->queues[task->threadid].ready_tasks,
+                       task, ready_link);
+       }
+       UNLOCK(&manager->queues[task->threadid].lock);
+}
+
+void
+isc_task_unpause(isc_task_t *task0) {
+       isc__task_t *task = (isc__task_t *)task0;
+       bool was_idle = false;
+
+       REQUIRE(ISCAPI_TASK_VALID(task0));
+
+       LOCK(&task->lock);
+       INSIST(task->state == task_state_paused);
+       if (!EMPTY(task->events)) {
+               task->state = task_state_ready;
+               was_idle = true;
+       } else {
+               task->state = task_state_idle;
+       }
+       UNLOCK(&task->lock);
+
+       if (was_idle) {
+               task_ready(task);
+       }
+}
+
 void
 isc_task_setprivilege(isc_task_t *task0, bool priv) {
        REQUIRE(ISCAPI_TASK_VALID(task0));