Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 1 | /****************************************************************************** |
| 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 Janssen | 49a8670 | 2015-07-08 11:48:57 -0700 | [diff] [blame] | 21 | #include "osi/include/reactor.h" |
| 22 | |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 23 | #include <assert.h> |
| 24 | #include <errno.h> |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 25 | #include <pthread.h> |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 26 | #include <stdlib.h> |
Etan Cohen | 3e59b5b | 2015-03-31 17:15:53 -0700 | [diff] [blame] | 27 | #include <string.h> |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 28 | #include <sys/epoll.h> |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 29 | #include <sys/eventfd.h> |
Arman Uguray | bb6836b | 2015-05-21 13:52:25 -0700 | [diff] [blame] | 30 | #include <unistd.h> |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 31 | |
Sharvil Nanavati | 0f9b91e | 2015-03-12 15:42:50 -0700 | [diff] [blame] | 32 | #include "osi/include/allocator.h" |
| 33 | #include "osi/include/list.h" |
Sharvil Nanavati | 4480276 | 2014-12-23 23:08:58 -0800 | [diff] [blame] | 34 | #include "osi/include/log.h" |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 35 | |
| 36 | #if !defined(EFD_SEMAPHORE) |
| 37 | # define EFD_SEMAPHORE (1 << 0) |
| 38 | #endif |
| 39 | |
| 40 | struct reactor_t { |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 41 | int epoll_fd; |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 42 | int event_fd; |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 43 | 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 Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 48 | }; |
| 49 | |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 50 | struct 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 Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 55 | |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 56 | 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 | |
| 60 | static reactor_status_t run_reactor(reactor_t *reactor, int iterations); |
| 61 | |
| 62 | static const size_t MAX_EVENTS = 64; |
| 63 | static const eventfd_t EVENT_REACTOR_STOP = 1; |
Sharvil Nanavati | 13959e0 | 2014-07-25 15:12:36 -0700 | [diff] [blame] | 64 | |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 65 | reactor_t *reactor_new(void) { |
Zach Johnson | 384f8a9 | 2014-08-25 23:22:24 -0700 | [diff] [blame] | 66 | reactor_t *ret = (reactor_t *)osi_calloc(sizeof(reactor_t)); |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 67 | |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 68 | 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 Janssen | db55458 | 2015-06-26 14:53:46 -0700 | [diff] [blame] | 73 | LOG_ERROR(LOG_TAG, "%s unable to create epoll instance: %s", __func__, strerror(errno)); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 74 | goto error; |
| 75 | } |
| 76 | |
Sharvil Nanavati | 13959e0 | 2014-07-25 15:12:36 -0700 | [diff] [blame] | 77 | ret->event_fd = eventfd(0, 0); |
Sharvil Nanavati | 2cb2998 | 2014-08-01 17:00:12 -0700 | [diff] [blame] | 78 | if (ret->event_fd == INVALID_FD) { |
Marie Janssen | db55458 | 2015-06-26 14:53:46 -0700 | [diff] [blame] | 79 | LOG_ERROR(LOG_TAG, "%s unable to create eventfd: %s", __func__, strerror(errno)); |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 80 | goto error; |
| 81 | } |
| 82 | |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 83 | pthread_mutex_init(&ret->list_lock, NULL); |
| 84 | ret->invalidation_list = list_new(NULL); |
| 85 | if (!ret->invalidation_list) { |
Marie Janssen | db55458 | 2015-06-26 14:53:46 -0700 | [diff] [blame] | 86 | LOG_ERROR(LOG_TAG, "%s unable to allocate object invalidation list.", __func__); |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 87 | goto error; |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 88 | } |
| 89 | |
Mark Salyzyn | a9962ce | 2015-04-16 11:13:39 -0700 | [diff] [blame] | 90 | struct epoll_event event; |
| 91 | memset(&event, 0, sizeof(event)); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 92 | event.events = EPOLLIN; |
| 93 | event.data.ptr = NULL; |
| 94 | if (epoll_ctl(ret->epoll_fd, EPOLL_CTL_ADD, ret->event_fd, &event) == -1) { |
Marie Janssen | db55458 | 2015-06-26 14:53:46 -0700 | [diff] [blame] | 95 | LOG_ERROR(LOG_TAG, "%s unable to register eventfd with epoll set: %s", __func__, strerror(errno)); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 96 | goto error; |
| 97 | } |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 98 | |
| 99 | return ret; |
| 100 | |
| 101 | error:; |
Zach Johnson | ec4059a | 2014-08-04 20:49:49 -0700 | [diff] [blame] | 102 | reactor_free(ret); |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 103 | return NULL; |
| 104 | } |
| 105 | |
| 106 | void reactor_free(reactor_t *reactor) { |
| 107 | if (!reactor) |
| 108 | return; |
| 109 | |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 110 | list_free(reactor->invalidation_list); |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 111 | close(reactor->event_fd); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 112 | close(reactor->epoll_fd); |
Zach Johnson | 384f8a9 | 2014-08-25 23:22:24 -0700 | [diff] [blame] | 113 | osi_free(reactor); |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 114 | } |
| 115 | |
| 116 | reactor_status_t reactor_start(reactor_t *reactor) { |
| 117 | assert(reactor != NULL); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 118 | return run_reactor(reactor, 0); |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 119 | } |
| 120 | |
| 121 | reactor_status_t reactor_run_once(reactor_t *reactor) { |
| 122 | assert(reactor != NULL); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 123 | return run_reactor(reactor, 1); |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 124 | } |
| 125 | |
| 126 | void reactor_stop(reactor_t *reactor) { |
| 127 | assert(reactor != NULL); |
| 128 | |
Sharvil Nanavati | 13959e0 | 2014-07-25 15:12:36 -0700 | [diff] [blame] | 129 | eventfd_write(reactor->event_fd, EVENT_REACTOR_STOP); |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 130 | } |
| 131 | |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 132 | reactor_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 Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 136 | assert(reactor != NULL); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 137 | assert(fd != INVALID_FD); |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 138 | |
Pavlin Radoslavov | 717a4a9 | 2016-02-06 08:36:06 -0800 | [diff] [blame] | 139 | reactor_object_t *object = |
| 140 | (reactor_object_t *)osi_calloc(sizeof(reactor_object_t)); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 141 | |
| 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 Salyzyn | a9962ce | 2015-04-16 11:13:39 -0700 | [diff] [blame] | 149 | struct epoll_event event; |
| 150 | memset(&event, 0, sizeof(event)); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 151 | 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 Janssen | db55458 | 2015-06-26 14:53:46 -0700 | [diff] [blame] | 158 | LOG_ERROR(LOG_TAG, "%s unable to register fd %d to epoll set: %s", __func__, fd, strerror(errno)); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 159 | pthread_mutex_destroy(&object->lock); |
Zach Johnson | 384f8a9 | 2014-08-25 23:22:24 -0700 | [diff] [blame] | 160 | osi_free(object); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 161 | return NULL; |
| 162 | } |
| 163 | |
| 164 | return object; |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 165 | } |
| 166 | |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 167 | bool reactor_change_registration(reactor_object_t *object, |
| 168 | void (*read_ready)(void *context), |
| 169 | void (*write_ready)(void *context)) { |
| 170 | assert(object != NULL); |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 171 | |
Mark Salyzyn | a9962ce | 2015-04-16 11:13:39 -0700 | [diff] [blame] | 172 | struct epoll_event event; |
| 173 | memset(&event, 0, sizeof(event)); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 174 | 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 Janssen | db55458 | 2015-06-26 14:53:46 -0700 | [diff] [blame] | 181 | LOG_ERROR(LOG_TAG, "%s unable to modify interest set for fd %d: %s", __func__, object->fd, strerror(errno)); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 182 | 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 Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 191 | } |
| 192 | |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 193 | void 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 Janssen | db55458 | 2015-06-26 14:53:46 -0700 | [diff] [blame] | 199 | LOG_ERROR(LOG_TAG, "%s unable to unregister fd %d from epoll set: %s", __func__, obj->fd, strerror(errno)); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 200 | |
| 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 Johnson | 384f8a9 | 2014-08-25 23:22:24 -0700 | [diff] [blame] | 221 | osi_free(obj); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 222 | } |
| 223 | |
| 224 | // Runs the reactor loop for a maximum of |iterations|. |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 225 | // 0 |iterations| means loop forever. |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 226 | // |reactor| may not be NULL. |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 227 | static reactor_status_t run_reactor(reactor_t *reactor, int iterations) { |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 228 | assert(reactor != NULL); |
| 229 | |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 230 | reactor->run_thread = pthread_self(); |
| 231 | reactor->is_running = true; |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 232 | |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 233 | 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 Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 238 | |
| 239 | int ret; |
Pavlin Radoslavov | d6121a3 | 2016-05-12 11:36:44 -0700 | [diff] [blame^] | 240 | OSI_NO_INTR(ret = epoll_wait(reactor->epoll_fd, events, MAX_EVENTS, -1)); |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 241 | if (ret == -1) { |
Marie Janssen | db55458 | 2015-06-26 14:53:46 -0700 | [diff] [blame] | 242 | LOG_ERROR(LOG_TAG, "%s error in epoll_wait: %s", __func__, strerror(errno)); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 243 | reactor->is_running = false; |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 244 | return REACTOR_STATUS_ERROR; |
| 245 | } |
| 246 | |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 247 | 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 Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 256 | } |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 257 | |
| 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 Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 274 | object->write_ready(object->context); |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 275 | pthread_mutex_unlock(&object->lock); |
| 276 | |
| 277 | if (reactor->object_removed) { |
| 278 | pthread_mutex_destroy(&object->lock); |
Zach Johnson | 384f8a9 | 2014-08-25 23:22:24 -0700 | [diff] [blame] | 279 | osi_free(object); |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 280 | } |
| 281 | } |
| 282 | } |
Zach Johnson | ec4059a | 2014-08-04 20:49:49 -0700 | [diff] [blame] | 283 | |
Sharvil Nanavati | fbf8908 | 2014-08-13 00:40:49 -0700 | [diff] [blame] | 284 | reactor->is_running = false; |
Sharvil Nanavati | 19084c6 | 2014-06-23 16:30:46 -0700 | [diff] [blame] | 285 | return REACTOR_STATUS_DONE; |
| 286 | } |