fix flaky ConnectTFOTimeout and ConnectTFOFallbackTimeout tests
[folly.git] / folly / io / async / AsyncTimeout.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/AsyncTimeout.h>
22 #include <folly/io/async/EventBase.h>
23 #include <folly/io/async/EventUtil.h>
24 #include <folly/io/async/Request.h>
25
26 #include <assert.h>
27 #include <glog/logging.h>
28
29 namespace folly {
30
31 AsyncTimeout::AsyncTimeout(TimeoutManager* timeoutManager)
32     : timeoutManager_(timeoutManager) {
33
34   folly_event_set(
35       &event_, -1, EV_TIMEOUT, &AsyncTimeout::libeventCallback, this);
36   event_.ev_base = nullptr;
37   timeoutManager_->attachTimeoutManager(
38       this,
39       TimeoutManager::InternalEnum::NORMAL);
40   RequestContext::saveContext();
41 }
42
43 AsyncTimeout::AsyncTimeout(EventBase* eventBase)
44     : timeoutManager_(eventBase) {
45
46   folly_event_set(
47       &event_, -1, EV_TIMEOUT, &AsyncTimeout::libeventCallback, this);
48   event_.ev_base = nullptr;
49   if (eventBase) {
50     timeoutManager_->attachTimeoutManager(
51       this,
52       TimeoutManager::InternalEnum::NORMAL);
53   }
54   RequestContext::saveContext();
55 }
56
57 AsyncTimeout::AsyncTimeout(TimeoutManager* timeoutManager,
58                              InternalEnum internal)
59     : timeoutManager_(timeoutManager) {
60
61   folly_event_set(
62       &event_, -1, EV_TIMEOUT, &AsyncTimeout::libeventCallback, this);
63   event_.ev_base = nullptr;
64   timeoutManager_->attachTimeoutManager(this, internal);
65   RequestContext::saveContext();
66 }
67
68 AsyncTimeout::AsyncTimeout(EventBase* eventBase, InternalEnum internal)
69     : timeoutManager_(eventBase) {
70
71   folly_event_set(
72       &event_, -1, EV_TIMEOUT, &AsyncTimeout::libeventCallback, this);
73   event_.ev_base = nullptr;
74   timeoutManager_->attachTimeoutManager(this, internal);
75   RequestContext::saveContext();
76 }
77
78 AsyncTimeout::AsyncTimeout(): timeoutManager_(nullptr) {
79   folly_event_set(
80       &event_, -1, EV_TIMEOUT, &AsyncTimeout::libeventCallback, this);
81   event_.ev_base = nullptr;
82   RequestContext::saveContext();
83 }
84
85 AsyncTimeout::~AsyncTimeout() {
86   cancelTimeout();
87 }
88
89 bool AsyncTimeout::scheduleTimeout(TimeoutManager::timeout_type timeout) {
90   assert(timeoutManager_ != nullptr);
91   context_ = RequestContext::saveContext();
92   return timeoutManager_->scheduleTimeout(this, timeout);
93 }
94
95 bool AsyncTimeout::scheduleTimeout(uint32_t milliseconds) {
96   return scheduleTimeout(TimeoutManager::timeout_type(milliseconds));
97 }
98
99 void AsyncTimeout::cancelTimeout() {
100   if (isScheduled()) {
101     timeoutManager_->cancelTimeout(this);
102   }
103 }
104
105 bool AsyncTimeout::isScheduled() const {
106   return EventUtil::isEventRegistered(&event_);
107 }
108
109 void AsyncTimeout::attachTimeoutManager(
110     TimeoutManager* timeoutManager,
111     InternalEnum internal) {
112   // This also implies no timeout is scheduled.
113   assert(timeoutManager_ == nullptr);
114   assert(timeoutManager->isInTimeoutManagerThread());
115   timeoutManager_ = timeoutManager;
116
117   timeoutManager_->attachTimeoutManager(this, internal);
118 }
119
120 void AsyncTimeout::attachEventBase(
121     EventBase* eventBase,
122     InternalEnum internal) {
123   attachTimeoutManager(eventBase, internal);
124 }
125
126 void AsyncTimeout::detachTimeoutManager() {
127   // Only allow the event base to be changed if the timeout is not
128   // currently installed.
129   if (isScheduled()) {
130     // Programmer bug.  Abort the program.
131     LOG(ERROR) << "detachEventBase() called on scheduled timeout; aborting";
132     abort();
133     return;
134   }
135
136   if (timeoutManager_) {
137     timeoutManager_->detachTimeoutManager(this);
138     timeoutManager_ = nullptr;
139   }
140 }
141
142 void AsyncTimeout::detachEventBase() {
143   detachTimeoutManager();
144 }
145
146 void AsyncTimeout::libeventCallback(libevent_fd_t fd, short events, void* arg) {
147   AsyncTimeout* timeout = reinterpret_cast<AsyncTimeout*>(arg);
148   assert(libeventFdToFd(fd) == -1);
149   assert(events == EV_TIMEOUT);
150   // prevent unused variable warnings
151   (void)fd;
152   (void)events;
153
154   // double check that ev_flags gets reset when the timeout is not running
155   assert((event_ref_flags(&timeout->event_) & ~EVLIST_INTERNAL) == EVLIST_INIT);
156
157   // this can't possibly fire if timeout->eventBase_ is nullptr
158   timeout->timeoutManager_->bumpHandlingTime();
159
160   RequestContextScopeGuard rctx(timeout->context_);
161
162   timeout->timeoutExpired();
163 }
164
165 } // folly