2 * Copyright 2014-present Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 #include "ThreadWheelTimekeeper.h"
18 #include <folly/Singleton.h>
19 #include <folly/futures/Future.h>
25 Singleton<ThreadWheelTimekeeper> timekeeperSingleton_;
27 // Our Callback object for HHWheelTimer
28 struct WTCallback : public std::enable_shared_from_this<WTCallback>,
29 public folly::HHWheelTimer::Callback {
30 struct PrivateConstructorTag {};
33 WTCallback(PrivateConstructorTag, EventBase* base) : base_(base) {}
35 // Only allow creation by this factory, to ensure heap allocation.
36 static std::shared_ptr<WTCallback> create(EventBase* base) {
37 // optimization opportunity: memory pool
38 auto cob = std::make_shared<WTCallback>(PrivateConstructorTag{}, base);
39 // Capture shared_ptr of cob in lambda so that Core inside Promise will
40 // hold a ref count to it. The ref count will be released when Core goes
41 // away which happens when both Promise and Future go away
42 cob->promise_.setInterruptHandler(
43 [cob](const folly::exception_wrapper&) { cob->interruptHandler(); });
47 Future<Unit> getFuture() {
48 return promise_.getFuture();
51 void releasePromise() {
52 // Don't need promise anymore. Break the circular reference as promise_
53 // is holding a ref count to us via Core. Core won't go away until both
54 // Promise and Future go away.
55 promise_ = Promise<Unit>::makeEmpty();
60 Promise<Unit> promise_;
62 void timeoutExpired() noexcept override {
64 // Don't need Promise anymore, break the circular reference
68 void interruptHandler() {
69 // Capture shared_ptr of self in lambda, if we don't do this, object
70 // may go away before the lambda is executed from event base thread.
71 // This is not racing with timeoutExpired anymore because this is called
72 // through Future, which means Core is still alive and keeping a ref count
73 // on us, so what timeouExpired is doing won't make the object go away
74 base_->runInEventBaseThread([me = shared_from_this()] {
76 // Don't need Promise anymore, break the circular reference
84 ThreadWheelTimekeeper::ThreadWheelTimekeeper()
85 : thread_([this] { eventBase_.loopForever(); }),
87 HHWheelTimer::newTimer(&eventBase_, std::chrono::milliseconds(1))) {
88 eventBase_.waitUntilRunning();
89 eventBase_.runInEventBaseThread([this]{
91 eventBase_.setName("FutureTimekeepr");
95 ThreadWheelTimekeeper::~ThreadWheelTimekeeper() {
96 eventBase_.runInEventBaseThreadAndWait([this]{
97 wheelTimer_->cancelAll();
98 eventBase_.terminateLoopSoon();
103 Future<Unit> ThreadWheelTimekeeper::after(Duration dur) {
104 auto cob = WTCallback::create(&eventBase_);
105 auto f = cob->getFuture();
107 // Even shared_ptr of cob is captured in lambda this is still somewhat *racy*
108 // because it will be released once timeout is scheduled. So technically there
109 // is no gurantee that EventBase thread can safely call timeout callback.
110 // However due to fact that we are having circular reference here:
111 // WTCallback->Promise->Core->WTCallbak, so three of them won't go away until
112 // we break the circular reference. The break happens either in
113 // WTCallback::timeoutExpired or WTCallback::interruptHandler. Former means
114 // timeout callback is being safely executed. Latter captures shared_ptr of
115 // WTCallback again in another lambda for canceling timeout. The moment
116 // canceling timeout is executed in EventBase thread, the actual timeout
117 // callback has either been executed, or will never be executed. So we are
120 if (!eventBase_.runInEventBaseThread([this, cob, dur]{
121 wheelTimer_->scheduleTimeout(cob.get(), dur);
123 // Release promise to break the circular reference. Because if
124 // scheduleTimeout fails, there is nothing to *promise*. Internally
125 // Core would automatically set an exception result when Promise is
126 // destructed before fulfilling.
127 // This is either called from EventBase thread, or here.
128 // They are somewhat racy but given the rare chance this could fail,
129 // I don't see it is introducing any problem yet.
130 cob->releasePromise();
137 std::shared_ptr<Timekeeper> getTimekeeperSingleton() {
138 return timekeeperSingleton_.try_get();
141 } // namespace detail