/
EventHandler.cpp
182 lines (155 loc) · 5.94 KB
/
EventHandler.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/*
* Copyright 2014-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/io/async/EventHandler.h>
#include <folly/String.h>
#include <folly/io/async/EventBase.h>
#include <assert.h>
namespace folly {
EventHandler::EventHandler(EventBase* eventBase, NetworkSocket fd) {
folly_event_set(&event_, fd.toFd(), 0, &EventHandler::libeventCallback, this);
if (eventBase != nullptr) {
setEventBase(eventBase);
} else {
// Callers must set the EventBase and fd before using this timeout.
// Set event_->ev_base to nullptr to ensure that this happens.
// (otherwise libevent will initialize it to the "default" event_base)
event_.ev_base = nullptr;
eventBase_ = nullptr;
}
}
EventHandler::~EventHandler() {
unregisterHandler();
}
bool EventHandler::registerImpl(uint16_t events, bool internal) {
assert(event_.ev_base != nullptr);
// We have to unregister the event before we can change the event flags
if (isHandlerRegistered()) {
// If the new events are the same are the same as the already registered
// flags, we don't have to do anything. Just return.
auto flags = event_ref_flags(&event_);
if (events == event_.ev_events &&
static_cast<bool>(flags & EVLIST_INTERNAL) == internal) {
return true;
}
event_del(&event_);
}
// Update the event flags
// Unfortunately, event_set() resets the event_base, so we have to remember
// it before hand, then pass it back into event_base_set() afterwards
struct event_base* evb = event_.ev_base;
event_set(
&event_,
event_.ev_fd,
short(events),
&EventHandler::libeventCallback,
this);
event_base_set(evb, &event_);
// Set EVLIST_INTERNAL if this is an internal event
if (internal) {
event_ref_flags(&event_) |= EVLIST_INTERNAL;
}
// Add the event.
//
// Although libevent allows events to wait on both I/O and a timeout,
// we intentionally don't allow an EventHandler to also use a timeout.
// Callers must maintain a separate AsyncTimeout object if they want a
// timeout.
//
// Otherwise, it is difficult to handle persistent events properly. (The I/O
// event and timeout may both fire together the same time around the event
// loop. Normally we would want to inform the caller of the I/O event first,
// then the timeout. However, it is difficult to do this properly since the
// I/O callback could delete the EventHandler.) Additionally, if a caller
// uses the same struct event for both I/O and timeout, and they just want to
// reschedule the timeout, libevent currently makes an epoll_ctl() call even
// if the I/O event flags haven't changed. Using a separate event struct is
// therefore slightly more efficient in this case (although it does take up
// more space).
if (event_add(&event_, nullptr) < 0) {
LOG(ERROR) << "EventBase: failed to register event handler for fd "
<< event_.ev_fd << ": " << errnoStr(errno);
// Call event_del() to make sure the event is completely uninstalled
event_del(&event_);
return false;
}
return true;
}
void EventHandler::unregisterHandler() {
if (isHandlerRegistered()) {
event_del(&event_);
}
}
void EventHandler::attachEventBase(EventBase* eventBase) {
// attachEventBase() may only be called on detached handlers
assert(event_.ev_base == nullptr);
assert(!isHandlerRegistered());
// This must be invoked from the EventBase's thread
eventBase->dcheckIsInEventBaseThread();
setEventBase(eventBase);
}
void EventHandler::detachEventBase() {
ensureNotRegistered(__func__);
event_.ev_base = nullptr;
}
void EventHandler::changeHandlerFD(NetworkSocket fd) {
ensureNotRegistered(__func__);
// event_set() resets event_base.ev_base, so manually restore it afterwards
struct event_base* evb = event_.ev_base;
folly_event_set(&event_, fd.toFd(), 0, &EventHandler::libeventCallback, this);
event_.ev_base = evb; // don't use event_base_set(), since evb may be nullptr
}
void EventHandler::initHandler(EventBase* eventBase, NetworkSocket fd) {
ensureNotRegistered(__func__);
folly_event_set(&event_, fd.toFd(), 0, &EventHandler::libeventCallback, this);
setEventBase(eventBase);
}
void EventHandler::ensureNotRegistered(const char* fn) {
// Neither the EventBase nor file descriptor may be changed while the
// handler is registered. Treat it as a programmer bug and abort the program
// if this requirement is violated.
if (isHandlerRegistered()) {
LOG(ERROR) << fn << " called on registered handler; aborting";
abort();
}
}
void EventHandler::libeventCallback(libevent_fd_t fd, short events, void* arg) {
EventHandler* handler = reinterpret_cast<EventHandler*>(arg);
assert(fd == handler->event_.ev_fd);
(void)fd; // prevent unused variable warnings
auto observer = handler->eventBase_->getExecutionObserver();
if (observer) {
observer->starting(reinterpret_cast<uintptr_t>(handler));
}
// this can't possibly fire if handler->eventBase_ is nullptr
handler->eventBase_->bumpHandlingTime();
handler->handlerReady(uint16_t(events));
if (observer) {
observer->stopped(reinterpret_cast<uintptr_t>(handler));
}
}
void EventHandler::setEventBase(EventBase* eventBase) {
event_base_set(eventBase->getLibeventBase(), &event_);
eventBase_ = eventBase;
}
bool EventHandler::isPending() const {
if (event_ref_flags(&event_) & EVLIST_ACTIVE) {
if (event_.ev_res & EV_READ) {
return true;
}
}
return false;
}
} // namespace folly