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