blob: 7388d52d79e3fe65c193a1749b9b4b7faa2fa285 [file] [log] [blame]
Sharvil Nanavati19084c62014-06-23 16:30:46 -07001/******************************************************************************
2 *
3 * Copyright (C) 2014 Google, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 ******************************************************************************/
18
19#define LOG_TAG "bt_osi_reactor"
20
Marie Janssen49a86702015-07-08 11:48:57 -070021#include "osi/include/reactor.h"
22
Sharvil Nanavati19084c62014-06-23 16:30:46 -070023#include <assert.h>
24#include <errno.h>
Sharvil Nanavatifbf89082014-08-13 00:40:49 -070025#include <pthread.h>
Sharvil Nanavati19084c62014-06-23 16:30:46 -070026#include <stdlib.h>
Etan Cohen3e59b5b2015-03-31 17:15:53 -070027#include <string.h>
Sharvil Nanavatifbf89082014-08-13 00:40:49 -070028#include <sys/epoll.h>
Sharvil Nanavati19084c62014-06-23 16:30:46 -070029#include <sys/eventfd.h>
Arman Uguraybb6836b2015-05-21 13:52:25 -070030#include <unistd.h>
Sharvil Nanavati19084c62014-06-23 16:30:46 -070031
Sharvil Nanavati0f9b91e2015-03-12 15:42:50 -070032#include "osi/include/allocator.h"
33#include "osi/include/list.h"
Sharvil Nanavati44802762014-12-23 23:08:58 -080034#include "osi/include/log.h"
Sharvil Nanavati19084c62014-06-23 16:30:46 -070035
36#if !defined(EFD_SEMAPHORE)
37# define EFD_SEMAPHORE (1 << 0)
38#endif
39
40struct reactor_t {
Sharvil Nanavatifbf89082014-08-13 00:40:49 -070041 int epoll_fd;
Sharvil Nanavati19084c62014-06-23 16:30:46 -070042 int event_fd;
Sharvil Nanavatifbf89082014-08-13 00:40:49 -070043 pthread_mutex_t list_lock; // protects invalidation_list.
44 list_t *invalidation_list; // reactor objects that have been unregistered.
45 pthread_t run_thread; // the pthread on which reactor_run is executing.
46 bool is_running; // indicates whether |run_thread| is valid.
47 bool object_removed;
Sharvil Nanavati19084c62014-06-23 16:30:46 -070048};
49
Sharvil Nanavatifbf89082014-08-13 00:40:49 -070050struct reactor_object_t {
51 int fd; // the file descriptor to monitor for events.
52 void *context; // a context that's passed back to the *_ready functions.
53 reactor_t *reactor; // the reactor instance this object is registered with.
54 pthread_mutex_t lock; // protects the lifetime of this object and all variables.
Sharvil Nanavati19084c62014-06-23 16:30:46 -070055
Sharvil Nanavatifbf89082014-08-13 00:40:49 -070056 void (*read_ready)(void *context); // function to call when the file descriptor becomes readable.
57 void (*write_ready)(void *context); // function to call when the file descriptor becomes writeable.
58};
59
60static reactor_status_t run_reactor(reactor_t *reactor, int iterations);
61
62static const size_t MAX_EVENTS = 64;
63static const eventfd_t EVENT_REACTOR_STOP = 1;
Sharvil Nanavati13959e02014-07-25 15:12:36 -070064
Sharvil Nanavati19084c62014-06-23 16:30:46 -070065reactor_t *reactor_new(void) {
Zach Johnson384f8a92014-08-25 23:22:24 -070066 reactor_t *ret = (reactor_t *)osi_calloc(sizeof(reactor_t));
Sharvil Nanavati19084c62014-06-23 16:30:46 -070067
Sharvil Nanavatifbf89082014-08-13 00:40:49 -070068 ret->epoll_fd = INVALID_FD;
69 ret->event_fd = INVALID_FD;
70
71 ret->epoll_fd = epoll_create(MAX_EVENTS);
72 if (ret->epoll_fd == INVALID_FD) {
Marie Janssendb554582015-06-26 14:53:46 -070073 LOG_ERROR(LOG_TAG, "%s unable to create epoll instance: %s", __func__, strerror(errno));
Sharvil Nanavatifbf89082014-08-13 00:40:49 -070074 goto error;
75 }
76
Sharvil Nanavati13959e02014-07-25 15:12:36 -070077 ret->event_fd = eventfd(0, 0);
Sharvil Nanavati2cb29982014-08-01 17:00:12 -070078 if (ret->event_fd == INVALID_FD) {
Marie Janssendb554582015-06-26 14:53:46 -070079 LOG_ERROR(LOG_TAG, "%s unable to create eventfd: %s", __func__, strerror(errno));
Sharvil Nanavati19084c62014-06-23 16:30:46 -070080 goto error;
81 }
82
Sharvil Nanavatifbf89082014-08-13 00:40:49 -070083 pthread_mutex_init(&ret->list_lock, NULL);
84 ret->invalidation_list = list_new(NULL);
85 if (!ret->invalidation_list) {
Marie Janssendb554582015-06-26 14:53:46 -070086 LOG_ERROR(LOG_TAG, "%s unable to allocate object invalidation list.", __func__);
Sharvil Nanavati19084c62014-06-23 16:30:46 -070087 goto error;
Sharvil Nanavatifbf89082014-08-13 00:40:49 -070088 }
89
Mark Salyzyna9962ce2015-04-16 11:13:39 -070090 struct epoll_event event;
91 memset(&event, 0, sizeof(event));
Sharvil Nanavatifbf89082014-08-13 00:40:49 -070092 event.events = EPOLLIN;
93 event.data.ptr = NULL;
94 if (epoll_ctl(ret->epoll_fd, EPOLL_CTL_ADD, ret->event_fd, &event) == -1) {
Marie Janssendb554582015-06-26 14:53:46 -070095 LOG_ERROR(LOG_TAG, "%s unable to register eventfd with epoll set: %s", __func__, strerror(errno));
Sharvil Nanavatifbf89082014-08-13 00:40:49 -070096 goto error;
97 }
Sharvil Nanavati19084c62014-06-23 16:30:46 -070098
99 return ret;
100
101error:;
Zach Johnsonec4059a2014-08-04 20:49:49 -0700102 reactor_free(ret);
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700103 return NULL;
104}
105
106void reactor_free(reactor_t *reactor) {
107 if (!reactor)
108 return;
109
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700110 list_free(reactor->invalidation_list);
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700111 close(reactor->event_fd);
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700112 close(reactor->epoll_fd);
Zach Johnson384f8a92014-08-25 23:22:24 -0700113 osi_free(reactor);
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700114}
115
116reactor_status_t reactor_start(reactor_t *reactor) {
117 assert(reactor != NULL);
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700118 return run_reactor(reactor, 0);
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700119}
120
121reactor_status_t reactor_run_once(reactor_t *reactor) {
122 assert(reactor != NULL);
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700123 return run_reactor(reactor, 1);
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700124}
125
126void reactor_stop(reactor_t *reactor) {
127 assert(reactor != NULL);
128
Sharvil Nanavati13959e02014-07-25 15:12:36 -0700129 eventfd_write(reactor->event_fd, EVENT_REACTOR_STOP);
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700130}
131
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700132reactor_object_t *reactor_register(reactor_t *reactor,
133 int fd, void *context,
134 void (*read_ready)(void *context),
135 void (*write_ready)(void *context)) {
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700136 assert(reactor != NULL);
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700137 assert(fd != INVALID_FD);
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700138
Pavlin Radoslavov717a4a92016-02-06 08:36:06 -0800139 reactor_object_t *object =
140 (reactor_object_t *)osi_calloc(sizeof(reactor_object_t));
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700141
142 object->reactor = reactor;
143 object->fd = fd;
144 object->context = context;
145 object->read_ready = read_ready;
146 object->write_ready = write_ready;
147 pthread_mutex_init(&object->lock, NULL);
148
Mark Salyzyna9962ce2015-04-16 11:13:39 -0700149 struct epoll_event event;
150 memset(&event, 0, sizeof(event));
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700151 if (read_ready)
152 event.events |= (EPOLLIN | EPOLLRDHUP);
153 if (write_ready)
154 event.events |= EPOLLOUT;
155 event.data.ptr = object;
156
157 if (epoll_ctl(reactor->epoll_fd, EPOLL_CTL_ADD, fd, &event) == -1) {
Marie Janssendb554582015-06-26 14:53:46 -0700158 LOG_ERROR(LOG_TAG, "%s unable to register fd %d to epoll set: %s", __func__, fd, strerror(errno));
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700159 pthread_mutex_destroy(&object->lock);
Zach Johnson384f8a92014-08-25 23:22:24 -0700160 osi_free(object);
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700161 return NULL;
162 }
163
164 return object;
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700165}
166
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700167bool reactor_change_registration(reactor_object_t *object,
168 void (*read_ready)(void *context),
169 void (*write_ready)(void *context)) {
170 assert(object != NULL);
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700171
Mark Salyzyna9962ce2015-04-16 11:13:39 -0700172 struct epoll_event event;
173 memset(&event, 0, sizeof(event));
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700174 if (read_ready)
175 event.events |= (EPOLLIN | EPOLLRDHUP);
176 if (write_ready)
177 event.events |= EPOLLOUT;
178 event.data.ptr = object;
179
180 if (epoll_ctl(object->reactor->epoll_fd, EPOLL_CTL_MOD, object->fd, &event) == -1) {
Marie Janssendb554582015-06-26 14:53:46 -0700181 LOG_ERROR(LOG_TAG, "%s unable to modify interest set for fd %d: %s", __func__, object->fd, strerror(errno));
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700182 return false;
183 }
184
185 pthread_mutex_lock(&object->lock);
186 object->read_ready = read_ready;
187 object->write_ready = write_ready;
188 pthread_mutex_unlock(&object->lock);
189
190 return true;
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700191}
192
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700193void reactor_unregister(reactor_object_t *obj) {
194 assert(obj != NULL);
195
196 reactor_t *reactor = obj->reactor;
197
198 if (epoll_ctl(reactor->epoll_fd, EPOLL_CTL_DEL, obj->fd, NULL) == -1)
Marie Janssendb554582015-06-26 14:53:46 -0700199 LOG_ERROR(LOG_TAG, "%s unable to unregister fd %d from epoll set: %s", __func__, obj->fd, strerror(errno));
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700200
201 if (reactor->is_running && pthread_equal(pthread_self(), reactor->run_thread)) {
202 reactor->object_removed = true;
203 return;
204 }
205
206 pthread_mutex_lock(&reactor->list_lock);
207 list_append(reactor->invalidation_list, obj);
208 pthread_mutex_unlock(&reactor->list_lock);
209
210 // Taking the object lock here makes sure a callback for |obj| isn't
211 // currently executing. The reactor thread must then either be before
212 // the callbacks or after. If after, we know that the object won't be
213 // referenced because it has been taken out of the epoll set. If before,
214 // it won't be referenced because the reactor thread will check the
215 // invalidation_list and find it in there. So by taking this lock, we
216 // are waiting until the reactor thread drops all references to |obj|.
217 // One the wait completes, we can unlock and destroy |obj| safely.
218 pthread_mutex_lock(&obj->lock);
219 pthread_mutex_unlock(&obj->lock);
220 pthread_mutex_destroy(&obj->lock);
Zach Johnson384f8a92014-08-25 23:22:24 -0700221 osi_free(obj);
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700222}
223
224// Runs the reactor loop for a maximum of |iterations|.
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700225// 0 |iterations| means loop forever.
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700226// |reactor| may not be NULL.
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700227static reactor_status_t run_reactor(reactor_t *reactor, int iterations) {
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700228 assert(reactor != NULL);
229
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700230 reactor->run_thread = pthread_self();
231 reactor->is_running = true;
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700232
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700233 struct epoll_event events[MAX_EVENTS];
234 for (int i = 0; iterations == 0 || i < iterations; ++i) {
235 pthread_mutex_lock(&reactor->list_lock);
236 list_clear(reactor->invalidation_list);
237 pthread_mutex_unlock(&reactor->list_lock);
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700238
239 int ret;
Pavlin Radoslavovd6121a32016-05-12 11:36:44 -0700240 OSI_NO_INTR(ret = epoll_wait(reactor->epoll_fd, events, MAX_EVENTS, -1));
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700241 if (ret == -1) {
Marie Janssendb554582015-06-26 14:53:46 -0700242 LOG_ERROR(LOG_TAG, "%s error in epoll_wait: %s", __func__, strerror(errno));
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700243 reactor->is_running = false;
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700244 return REACTOR_STATUS_ERROR;
245 }
246
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700247 for (int j = 0; j < ret; ++j) {
248 // The event file descriptor is the only one that registers with
249 // a NULL data pointer. We use the NULL to identify it and break
250 // out of the reactor loop.
251 if (events[j].data.ptr == NULL) {
252 eventfd_t value;
253 eventfd_read(reactor->event_fd, &value);
254 reactor->is_running = false;
255 return REACTOR_STATUS_STOP;
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700256 }
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700257
258 reactor_object_t *object = (reactor_object_t *)events[j].data.ptr;
259
260 pthread_mutex_lock(&reactor->list_lock);
261 if (list_contains(reactor->invalidation_list, object)) {
262 pthread_mutex_unlock(&reactor->list_lock);
263 continue;
264 }
265
266 // Downgrade the list lock to an object lock.
267 pthread_mutex_lock(&object->lock);
268 pthread_mutex_unlock(&reactor->list_lock);
269
270 reactor->object_removed = false;
271 if (events[j].events & (EPOLLIN | EPOLLHUP | EPOLLRDHUP | EPOLLERR) && object->read_ready)
272 object->read_ready(object->context);
273 if (!reactor->object_removed && events[j].events & EPOLLOUT && object->write_ready)
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700274 object->write_ready(object->context);
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700275 pthread_mutex_unlock(&object->lock);
276
277 if (reactor->object_removed) {
278 pthread_mutex_destroy(&object->lock);
Zach Johnson384f8a92014-08-25 23:22:24 -0700279 osi_free(object);
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700280 }
281 }
282 }
Zach Johnsonec4059a2014-08-04 20:49:49 -0700283
Sharvil Nanavatifbf89082014-08-13 00:40:49 -0700284 reactor->is_running = false;
Sharvil Nanavati19084c62014-06-23 16:30:46 -0700285 return REACTOR_STATUS_DONE;
286}