Future<Unit> ThreadWheelTimekeeper::after(Duration dur) { auto cob = WTCallback::create(&eventBase_); auto f = cob->getFuture(); // // Even shared_ptr of cob is captured in lambda this is still somewhat *racy* // because it will be released once timeout is scheduled. So technically there // is no gurantee that EventBase thread can safely call timeout callback. // However due to fact that we are having circular reference here: // WTCallback->Promise->Core->WTCallbak, so three of them won't go away until // we break the circular reference. The break happens either in // WTCallback::timeoutExpired or WTCallback::interruptHandler. Former means // timeout callback is being safely executed. Latter captures shared_ptr of // WTCallback again in another lambda for canceling timeout. The moment // canceling timeout is executed in EventBase thread, the actual timeout // callback has either been executed, or will never be executed. So we are // fine here. // if (!eventBase_.runInEventBaseThread([this, cob, dur]{ wheelTimer_->scheduleTimeout(cob.get(), dur); })) { // Release promise to break the circular reference. Because if // scheduleTimeout fails, there is nothing to *promise*. Internally // Core would automatically set an exception result when Promise is // destructed before fulfilling. // This is either called from EventBase thread, or here. // They are somewhat racy but given the rare chance this could fail, // I don't see it is introducing any problem yet. auto promise = cob->stealPromise(); if (!promise.isFulfilled()) { promise.setException(NoTimekeeper{}); } } return f; }
bool MockCheckedExpectedCall::canMatchActualCalls() { return !isFulfilled(); }