RequestContext support
[folly.git] / folly / experimental / fibers / TimeoutController.cpp
1 /*
2  * Copyright 2015 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 #include "TimeoutController.h"
17 #include <folly/Memory.h>
18
19 namespace folly { namespace fibers {
20
21 TimeoutController::TimeoutController(LoopController& loopController) :
22     nextTimeout_(TimePoint::max()),
23     loopController_(loopController) {}
24
25 intptr_t TimeoutController::registerTimeout(std::function<void()> f,
26                                             Duration duration) {
27   auto& list = [&]() -> TimeoutHandleList& {
28     for (auto& bucket : timeoutHandleBuckets_) {
29       if (bucket.first == duration) {
30         return *bucket.second;
31       }
32     }
33
34     timeoutHandleBuckets_.emplace_back(duration,
35                                        folly::make_unique<TimeoutHandleList>());
36     return *timeoutHandleBuckets_.back().second;
37   }();
38
39   auto timeout = Clock::now() + duration;
40   list.emplace(std::move(f), timeout, list);
41
42   if (timeout < nextTimeout_) {
43     nextTimeout_ = timeout;
44     scheduleRun();
45   }
46
47   return reinterpret_cast<intptr_t>(&list.back());
48 }
49
50 void TimeoutController::runTimeouts(TimePoint time) {
51   auto now = Clock::now();
52   // Make sure we don't skip some events if function was run before actual time.
53   if (time < now) {
54     time = now;
55   }
56   if (nextTimeout_ > time) {
57     return;
58   }
59
60   nextTimeout_ = TimePoint::max();
61
62   for (auto& bucket : timeoutHandleBuckets_) {
63     auto& list = *bucket.second;
64
65     while (!list.empty()) {
66       if (!list.front().canceled) {
67         if (list.front().timeout > time) {
68           nextTimeout_ = std::min(nextTimeout_, list.front().timeout);
69           break;
70         }
71
72         list.front().func();
73       }
74       list.pop();
75     }
76   }
77
78   if (nextTimeout_ != TimePoint::max()) {
79     scheduleRun();
80   }
81 }
82
83 void TimeoutController::scheduleRun() {
84   auto time = nextTimeout_;
85   std::weak_ptr<TimeoutController> timeoutControllerWeak = shared_from_this();
86
87   loopController_.timedSchedule([timeoutControllerWeak, time]() {
88       if (auto timeoutController = timeoutControllerWeak.lock()) {
89         timeoutController->runTimeouts(time);
90       }
91     }, time);
92 }
93
94 void TimeoutController::cancel(intptr_t p) {
95   auto handle = reinterpret_cast<TimeoutHandle*>(p);
96   handle->canceled = true;
97
98   auto& list = handle->list;
99
100   while (!list.empty() && list.front().canceled) {
101     list.pop();
102   }
103 }
104
105 }}