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

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


The following commit(s) were added to refs/heads/master by this push:
     new 72f16f6  Fixed a bug where timers wouldn't expire after 
`process:reinitialize`.
72f16f6 is described below

commit 72f16f68973bf7d2ce5c621539a21fc4eccfa56e
Author: Charles-Francois Natali <cf.nat...@gmail.com>
AuthorDate: Sat Jun 26 19:04:33 2021 +0100

    Fixed a bug where timers wouldn't expire after `process:reinitialize`.
    
    Pending `ticks` are used by `scheduleTick` to decide whether to schedule
    an event loop tick when a new timer is scheduled - since we only need to
    schedule the event loop tick if the new timer is supposed to expire
    earlier than the current earliest timer.
    Unfortunately `Clock::finalize` didn't clear `ticks`, which means that
    the following could happen:
    - schedule a timer T0 for expiration at time t0
    - call `process::reinitalize`, which calls `Clock::finalize` but doesn't
      clear `ticks`
    - schedule a new timer T1 for expiration at time t1 > t0: since
      `scheduleTick` would see that there was already the earlier pending
      tick for T0 in `ticks` with t0 < t1, it wouldn't actually schedule a
      tick of the event loop
    
    Therefore new timers would never fire again.
    
    This caused e.g.
    `DockerContainerizerIPv6Test.ROOT_DOCKER_LaunchIPv6HostNetwork` test to
    hang since it called `process::reinitialized` while having some active
    timers - e.g. the reaper periodic timer.
    
    Also added a test specifically for this bug.
---
 3rdparty/libprocess/src/clock.cpp               | 10 ++++---
 3rdparty/libprocess/src/tests/process_tests.cpp | 37 +++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/3rdparty/libprocess/src/clock.cpp 
b/3rdparty/libprocess/src/clock.cpp
index 519c69e..7307de9 100644
--- a/3rdparty/libprocess/src/clock.cpp
+++ b/3rdparty/libprocess/src/clock.cpp
@@ -218,11 +218,13 @@ void Clock::finalize()
   synchronized (timers_mutex) {
     // NOTE: `currents` is only non-empty when the clock is paused.
 
-    // This, along with the `timers_mutex`, is all that is required to clean
-    // up any pending timers.  Timers are triggered via "ticks".  However,
-    // we do not need to clear `ticks` because a "tick" with an empty `timers`
-    // map will effectively be a no-op.
+    // Clear both timers and ticks.
+    // Note that we need to clear `ticks` as well because the earliest tick in
+    // `ticks` is used by `scheduleTick` to decide whether to schedule an event
+    // loop tick when a new timer is added, so not clearing `ticks` could
+    // cause, after reinitialization, new timers to never fire.
     timers->clear();
+    clock::ticks->clear();
   }
 }
 
diff --git a/3rdparty/libprocess/src/tests/process_tests.cpp 
b/3rdparty/libprocess/src/tests/process_tests.cpp
index eabd70e..d47c19b 100644
--- a/3rdparty/libprocess/src/tests/process_tests.cpp
+++ b/3rdparty/libprocess/src/tests/process_tests.cpp
@@ -114,6 +114,17 @@ using testing::InvokeWithoutArgs;
 using testing::Return;
 using testing::ReturnArg;
 
+
+namespace process {
+
+// Used by TimerAfterReinitialize.
+void reinitialize(
+    const Option<string>& delegate,
+    const Option<string>& readonlyAuthenticationRealm,
+    const Option<string>& readwriteAuthenticationRealm);
+
+}
+
 // TODO(bmahler): Move tests into their own files as appropriate.
 
 class ProcessTest : public TemporaryDirectoryTest {};
@@ -2109,3 +2120,29 @@ TEST_F(ProcessTest, ProcessesEndpointNoHang)
   AWAIT_READY(response);
   EXPECT_EQ(http::Status::OK, response->code);
 }
+
+
+// Test for a bug where timers wouldn't be handled after libprocess was
+// reinitialized.
+TEST_F(ProcessTest, TimerAfterReinitialize)
+{
+  // Schedule a timer, which won't have time to expire before...
+  process::Timer timer_before = Clock::timer(Milliseconds(10), []() {});
+
+  // We reinitialize libprocess.
+  process::reinitialize(
+    None(),
+    process::READWRITE_HTTP_AUTHENTICATION_REALM,
+    process::READONLY_HTTP_AUTHENTICATION_REALM);
+
+  // Now, schedule a new timer which is supposed to fire later than the one
+  // above.
+  Promise<Nothing> promise;
+  Future<Nothing> future = promise.future();
+
+  process::Timer timer_after = Clock::timer(Milliseconds(10),
+                                            [&]() { promise.set(Nothing()); });
+
+  // Wait until it fires.
+  AWAIT_READY(future);
+}

Reply via email to