1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
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/EventBaseBackendBase.h>
18 
19 #include <folly/io/async/EventBase.h>
20 
21 #if defined(__linux__) && !FOLLY_MOBILE
22 #define FOLLY_USE_EPOLLET
23 
24 #include <sys/epoll.h>
25 
26 struct event_base {
27   void* evsel;
28   void* evbase;
29 };
30 
31 struct epollop {
32   void* fds;
33   int nfds;
34   void* events;
35   int nevents;
36   int epfd;
37 };
38 #endif
39 
40 namespace folly {
eb_ev_base(EventBase * evb)41 void EventBaseEvent::eb_ev_base(EventBase* evb) {
42   evb_ = evb;
43   event_.ev_base = evb ? evb->getLibeventBase() : nullptr;
44 }
45 
eb_event_base_set(EventBase * evb)46 int EventBaseEvent::eb_event_base_set(EventBase* evb) {
47   evb_ = evb;
48   auto* base = evb_ ? (evb_->getLibeventBase()) : nullptr;
49   if (base) {
50     return ::event_base_set(base, &event_);
51   }
52 
53   return 0;
54 }
55 
eb_event_add(const struct timeval * timeout)56 int EventBaseEvent::eb_event_add(const struct timeval* timeout) {
57   auto* backend = evb_ ? (evb_->getBackend()) : nullptr;
58   if (backend) {
59     return backend->eb_event_add(*this, timeout);
60   }
61 
62   return -1;
63 }
64 
eb_event_del()65 int EventBaseEvent::eb_event_del() {
66   auto* backend = evb_ ? (evb_->getBackend()) : nullptr;
67   if (backend) {
68     return backend->eb_event_del(*this);
69   }
70 
71   return -1;
72 }
73 
eb_event_active(int res)74 bool EventBaseEvent::eb_event_active(int res) {
75   auto* backend = evb_ ? (evb_->getBackend()) : nullptr;
76   if (backend) {
77     return backend->eb_event_active(*this, res);
78   }
79 
80   return false;
81 }
82 
setEdgeTriggered()83 bool EventBaseEvent::setEdgeTriggered() {
84 #ifdef FOLLY_USE_EPOLLET
85   // Until v2 libevent doesn't expose API to set edge-triggered flag for events.
86   // If epoll backend is used by libevent, we can enable it though epoll_ctl
87   // directly.
88   // Note that this code depends on internal event_base and epollop layout, so
89   // we have to validate libevent version.
90   static const bool supportedVersion =
91       !strcmp(event_get_version(), "1.4.14b-stable");
92   if (!supportedVersion) {
93     return false;
94   }
95   auto* base = evb_ ? (evb_->getLibeventBase()) : nullptr;
96   if (!base || strcmp(event_base_get_method(base), "epoll")) {
97     return false;
98   }
99 
100   auto epfd = static_cast<epollop*>(base->evbase)->epfd;
101   epoll_event epev = {0, {0}};
102   epev.data.fd = eb_ev_fd();
103   epev.events = EPOLLET;
104   if (eb_ev_events() & EV_READ) {
105     epev.events |= EPOLLIN;
106   }
107   if (eb_ev_events() & EV_WRITE) {
108     epev.events |= EPOLLOUT;
109   }
110   if (::epoll_ctl(epfd, EPOLL_CTL_MOD, eb_ev_fd(), &epev) == -1) {
111     LOG(DFATAL) << "epoll_ctl failed: " << errno;
112     return false;
113   }
114   return true;
115 #else
116   return false;
117 #endif
118 }
119 } // namespace folly
120