}
void HHWheelTimer::destroy() {
- assert(count_ == 0);
- cancelAll();
+ if (getDestructorGuardCount() == count_) {
+ // Every callback holds a DestructorGuard. In this simple case,
+ // All timeouts should already be gone.
+ assert(count_ == 0);
+
+ // Clean them up in opt builds and move on
+ cancelAll();
+ }
+ // else, we are only marking pending destruction, but one or more
+ // HHWheelTimer::SharedPtr's (and possibly their timeouts) are still holding
+ // this HHWheelTimer. We cannot assert that all timeouts have been cancelled,
+ // and will just have to wait for them all to complete on their own.
DelayedDestruction::destroy();
}
* A HHWheelTimer should only be destroyed when there are no more
* callbacks pending in the set. (If it helps you may use cancelAll() to
* cancel all pending timeouts explicitly before calling this.)
+ *
+ * However, it is OK to invoke this function (or via UniquePtr dtor) while
+ * there are outstanding DestructorGuard's or HHWheelTimer::SharedPtr's.
*/
virtual void destroy();
EXPECT_EQ(1, t.cancelAll());
EXPECT_EQ(1, tt.canceledTimestamps.size());
}
+
+TEST_F(HHWheelTimerTest, SharedPtr) {
+ HHWheelTimer::UniquePtr t(new HHWheelTimer(&eventBase, milliseconds(1)));
+
+ TestTimeout t1;
+ TestTimeout t2;
+ TestTimeout t3;
+
+ ASSERT_EQ(t->count(), 0);
+
+ t->scheduleTimeout(&t1, milliseconds(5));
+ t->scheduleTimeout(&t2, milliseconds(5));
+
+ HHWheelTimer::SharedPtr s(t);
+
+ s->scheduleTimeout(&t3, milliseconds(10));
+
+ ASSERT_EQ(t->count(), 3);
+
+ // Kill the UniquePtr, but the SharedPtr keeps it alive
+ t.reset();
+
+ TimePoint start;
+ eventBase.loop();
+ TimePoint end;
+
+ ASSERT_EQ(t1.timestamps.size(), 1);
+ ASSERT_EQ(t2.timestamps.size(), 1);
+ ASSERT_EQ(t3.timestamps.size(), 1);
+
+ ASSERT_EQ(s->count(), 0);
+
+ T_CHECK_TIMEOUT(start, t1.timestamps[0], milliseconds(5));
+ T_CHECK_TIMEOUT(start, t2.timestamps[0], milliseconds(5));
+ T_CHECK_TIMEOUT(start, t3.timestamps[0], milliseconds(10));
+ T_CHECK_TIMEOUT(start, end, milliseconds(10));
+}