Move runAfterDelay/tryRunAfterDelay into TimeoutManager
[folly.git] / folly / io / async / TimeoutManager.cpp
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements. See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership. The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License. You may obtain a copy of the License at
11  *
12  *   http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied. See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21 #include <folly/io/async/TimeoutManager.h>
22
23 #include <boost/intrusive/list.hpp>
24
25 #include <folly/Exception.h>
26 #include <folly/Memory.h>
27 #include <folly/io/async/AsyncTimeout.h>
28
29 #include <glog/logging.h>
30
31 namespace folly {
32
33 struct TimeoutManager::CobTimeouts {
34   // small object used as a callback arg with enough info to execute the
35   // appropriate client-provided Cob
36   class CobTimeout : public AsyncTimeout {
37    public:
38     CobTimeout(TimeoutManager* timeoutManager, Func cob, InternalEnum internal)
39         : AsyncTimeout(timeoutManager, internal), cob_(std::move(cob)) {}
40
41     void timeoutExpired() noexcept override {
42       // For now, we just swallow any exceptions that the callback threw.
43       try {
44         cob_();
45       } catch (const std::exception& ex) {
46         LOG(ERROR) << "TimeoutManager::runAfterDelay() callback threw "
47                    << typeid(ex).name() << " exception: " << ex.what();
48       } catch (...) {
49         LOG(ERROR) << "TimeoutManager::runAfterDelay() callback threw "
50                    << "non-exception type";
51       }
52
53       // The CobTimeout object was allocated on the heap by runAfterDelay(),
54       // so delete it now that the it has fired.
55       delete this;
56     }
57
58    private:
59     Func cob_;
60
61    public:
62     using ListHook = boost::intrusive::list_member_hook<
63         boost::intrusive::link_mode<boost::intrusive::auto_unlink>>;
64     ListHook hook;
65     using List = boost::intrusive::list<
66         CobTimeout,
67         boost::intrusive::member_hook<CobTimeout, ListHook, &CobTimeout::hook>,
68         boost::intrusive::constant_time_size<false>>;
69   };
70
71   CobTimeout::List list;
72 };
73
74 TimeoutManager::TimeoutManager()
75     : cobTimeouts_(folly::make_unique<CobTimeouts>()) {}
76
77 void TimeoutManager::runAfterDelay(
78     Func cob,
79     uint32_t milliseconds,
80     InternalEnum internal) {
81   if (!tryRunAfterDelay(std::move(cob), milliseconds, internal)) {
82     folly::throwSystemError(
83         "error in TimeoutManager::runAfterDelay(), failed to schedule timeout");
84   }
85 }
86
87 bool TimeoutManager::tryRunAfterDelay(
88     Func cob,
89     uint32_t milliseconds,
90     InternalEnum internal) {
91   if (!cobTimeouts_) {
92     return false;
93   }
94
95   auto timeout = folly::make_unique<CobTimeouts::CobTimeout>(
96       this, std::move(cob), internal);
97   if (!timeout->scheduleTimeout(milliseconds)) {
98     return false;
99   }
100   cobTimeouts_->list.push_back(*timeout.release());
101   return true;
102 }
103
104 void TimeoutManager::clearCobTimeouts() {
105   if (!cobTimeouts_) {
106     return;
107   }
108
109   // Delete any unfired callback objects, so that we don't leak memory
110   // Note that we don't fire them.
111   while (!cobTimeouts_->list.empty()) {
112     auto* timeout = &cobTimeouts_->list.front();
113     delete timeout;
114   }
115 }
116
117 TimeoutManager::~TimeoutManager() {
118   clearCobTimeouts();
119 }
120 }