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