This is an automated email from the ASF dual-hosted git repository.

alexey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git


The following commit(s) were added to refs/heads/master by this push:
     new a62127d68 [util] correct invariant in ThreadPoolToken::Transition()
a62127d68 is described below

commit a62127d6808c40a298127ef371ae194f4185d1a6
Author: Alexey Serbin <[email protected]>
AuthorDate: Wed Mar 19 10:38:27 2025 -0700

    [util] correct invariant in ThreadPoolToken::Transition()
    
    This patch corrects the invariant check when transitioning from
    State::RUNNING to State::GRACEFUL_QUIESCING in ThreadPoolToken::Close().
    Without this fix, a couple of test scenarios in threadpool-test
    would fail with output like below:
    
      F20250319 10:15:25.060393 1859138 threadpool.cc:344] Check failed: 
active_threads_ > 0 (0 vs. 0)
    
    I also added extra checks for into ThreadPoolToken::Transition() and
    updated the inline documentation for ThreadPoolToken::State.
    
    This is a follow-up to 4e5cd00b62e6be94e410abf98dda6fb066c6e1bb.
    
    Change-Id: I6cfcdff743ad0074446ab9664dda43e4a9cb23ea
    Reviewed-on: http://gerrit.cloudera.org:8080/22642
    Tested-by: Alexey Serbin <[email protected]>
    Reviewed-by: Abhishek Chennaka <[email protected]>
---
 src/kudu/util/threadpool.cc | 42 ++++++++++++++++++++++++++++--------------
 src/kudu/util/threadpool.h  | 15 +++++++++------
 2 files changed, 37 insertions(+), 20 deletions(-)

diff --git a/src/kudu/util/threadpool.cc b/src/kudu/util/threadpool.cc
index b3c57d630..de60d1d87 100644
--- a/src/kudu/util/threadpool.cc
+++ b/src/kudu/util/threadpool.cc
@@ -203,9 +203,9 @@ void ThreadPoolToken::Close() {
 
   switch (state_) {
     case State::IDLE:
-      // If there aren't any outstanding tasks, quiesce the token immediately.
-      // Otherwise, transition from IDLE into GRACEFUL_QUIESCING state.
-      Transition(entries_.empty() ? State::QUIESCED : 
State::GRACEFUL_QUIESCING);
+      // There aren't any outstanding tasks and no active worker threads,
+      // so quiesce the token immediately.
+      Transition(State::QUIESCED);
       return;
     case State::RUNNING:
       // Unconditionally transition the token into GRACEFUL_QUIESCING state.
@@ -322,10 +322,8 @@ void ThreadPoolToken::Transition(State new_state) {
   switch (state_) {
     case State::IDLE:
       CHECK(new_state == State::RUNNING ||
-            new_state == State::GRACEFUL_QUIESCING ||
             new_state == State::QUIESCED);
-      if (new_state == State::RUNNING ||
-          new_state == State::GRACEFUL_QUIESCING) {
+      if (new_state == State::RUNNING) {
         CHECK(!entries_.empty());
       } else {
         CHECK(entries_.empty());
@@ -337,19 +335,32 @@ void ThreadPoolToken::Transition(State new_state) {
             new_state == State::GRACEFUL_QUIESCING ||
             new_state == State::QUIESCING ||
             new_state == State::QUIESCED);
-      if (new_state != State::GRACEFUL_QUIESCING) {
+      if (new_state == State::QUIESCING) {
         CHECK(entries_.empty());
-      }
-      if (new_state == State::GRACEFUL_QUIESCING || new_state == 
State::QUIESCING) {
         CHECK_GT(active_threads_, 0);
       }
+      if (new_state == State::QUIESCED || new_state == State::IDLE) {
+        CHECK(entries_.empty());
+        CHECK_EQ(active_threads_, 0);
+      }
+      if (new_state == State::GRACEFUL_QUIESCING) {
+        CHECK(active_threads_ > 0 || !entries_.empty());
+      }
       break;
     case State::GRACEFUL_QUIESCING:
       CHECK(new_state == State::QUIESCING ||
             new_state == State::QUIESCED);
+      CHECK(entries_.empty());
+      if (new_state == State::QUIESCING) {
+        CHECK_GT(active_threads_, 0);
+      }
+      if (new_state == State::QUIESCED) {
+        CHECK_EQ(active_threads_, 0);
+      }
       break;
     case State::QUIESCING:
       CHECK(new_state == State::QUIESCED);
+      CHECK(entries_.empty());
       CHECK_EQ(active_threads_, 0);
       break;
     case State::QUIESCED:
@@ -498,9 +509,8 @@ void ThreadPool::Shutdown() {
         // (i.e. there are no active threads), the tasks will have been removed
         // above and we can quiesce immediately. Otherwise, we need to wait for
         // the threads to finish.
-        t->Transition(t->active_threads_ > 0 ?
-            ThreadPoolToken::State::QUIESCING :
-            ThreadPoolToken::State::QUIESCED);
+        t->Transition(t->active_threads_ > 0 ? 
ThreadPoolToken::State::QUIESCING
+                                             : 
ThreadPoolToken::State::QUIESCED);
         break;
       default:
         break;
@@ -849,8 +859,12 @@ void ThreadPool::DispatchThread() {
 
     // Possible states:
     // 1. The token was shut down while we ran its task. Transition to 
QUIESCED.
-    // 2. The token has no more queued tasks. Transition back to IDLE.
-    // 3. The token has more tasks. Requeue it and transition back to RUNNABLE.
+    // 2. The token has no more queued tasks. Transition to IDLE.
+    // 3. The token has more tasks. Requeue it and keep the state RUNNING
+    //    or GRACEFUL_QUIESCING.
+    // 4. The token was gracefully quiesced while its task was running, and
+    //    (a) the token's queue has more elements: transition to 
GRACEFUL_QUIESCING
+    //    (b) the token's queue is empty: transition to QUIESCED
     const ThreadPoolToken::State state = token->state();
     DCHECK(state == ThreadPoolToken::State::RUNNING ||
            state == ThreadPoolToken::State::GRACEFUL_QUIESCING ||
diff --git a/src/kudu/util/threadpool.h b/src/kudu/util/threadpool.h
index b855e0e3b..136f4805f 100644
--- a/src/kudu/util/threadpool.h
+++ b/src/kudu/util/threadpool.h
@@ -714,16 +714,19 @@ class ThreadPoolToken {
   //    worker thread finishes executing a task belonging
   //    to a shut down token or pool
   enum class State {
-    // Token has no queued tasks.
+    // Token has no queued tasks, and there aren't any active worker threads
+    // running the token's tasks.
     IDLE,
 
-    // A worker thread is running one of the token's previously queued tasks.
+    // A worker thread is running one of the token's previously queued tasks,
+    // or there aren't yet any active worker threads running the token's tasks
+    // at this particular moment, but the token's queue isn't empty.
     RUNNING,
 
-    // No new tasks may be submitted to the token. A worker thread is still
-    // running one of the token's previously queued tasks, and will continue
-    // running all the rest of the scheduled tasks if any are still present
-    // in the queue unless the token is shut down in the process of doing so.
+    // No new tasks may be submitted to the token, but otherwise the same
+    // set of conditions apply in RUNNING state. Worker threads will continue
+    // running the scheduled tasks if any are still present in the queue
+    // unless the token is shut down in the process.
     GRACEFUL_QUIESCING,
 
     // No new tasks may be submitted to the token. A worker thread is still

Reply via email to