blob: d3d7767e47dd1d248be293e4fa850245973d139e [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2014 Google, 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.
*
******************************************************************************/
#define LOG_TAG "bt_osi_reactor"
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/eventfd.h>
#include <sys/select.h>
#include <utils/Log.h>
#include "list.h"
#include "reactor.h"
#if !defined(EFD_SEMAPHORE)
# define EFD_SEMAPHORE (1 << 0)
#endif
struct reactor_t {
int event_fd;
list_t *objects;
};
static reactor_status_t run_reactor(reactor_t *reactor, int iterations, struct timeval *tv);
reactor_t *reactor_new(void) {
reactor_t *ret = (reactor_t *)calloc(1, sizeof(reactor_t));
if (!ret)
return NULL;
ret->event_fd = eventfd(0, EFD_SEMAPHORE);
if (ret->event_fd == -1) {
ALOGE("%s unable to create eventfd: %s", __func__, strerror(errno));
goto error;
}
ret->objects = list_new(NULL);
if (!ret->objects)
goto error;
return ret;
error:;
list_free(ret->objects);
close(ret->event_fd);
free(ret);
return NULL;
}
void reactor_free(reactor_t *reactor) {
if (!reactor)
return;
list_free(reactor->objects);
close(reactor->event_fd);
free(reactor);
}
reactor_status_t reactor_start(reactor_t *reactor) {
assert(reactor != NULL);
return run_reactor(reactor, 0, NULL);
}
reactor_status_t reactor_run_once(reactor_t *reactor) {
assert(reactor != NULL);
return run_reactor(reactor, 1, NULL);
}
reactor_status_t reactor_run_once_timeout(reactor_t *reactor, timeout_t timeout_ms) {
assert(reactor != NULL);
struct timeval tv;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
return run_reactor(reactor, 1, &tv);
}
void reactor_stop(reactor_t *reactor) {
assert(reactor != NULL);
eventfd_write(reactor->event_fd, 1);
}
void reactor_register(reactor_t *reactor, reactor_object_t *obj) {
assert(reactor != NULL);
assert(obj != NULL);
list_append(reactor->objects, obj);
}
void reactor_unregister(reactor_t *reactor, reactor_object_t *obj) {
assert(reactor != NULL);
assert(obj != NULL);
list_remove(reactor->objects, obj);
}
// Runs the reactor loop for a maximum of |iterations| with the given timeout, |tv|.
// 0 |iterations| means loop forever.
// NULL |tv| means no timeout (block until an event occurs).
// |reactor| may not be NULL.
static reactor_status_t run_reactor(reactor_t *reactor, int iterations, struct timeval *tv) {
assert(reactor != NULL);
for (int i = 0; iterations == 0 || i < iterations; ++i) {
fd_set read_set;
fd_set write_set;
FD_ZERO(&read_set);
FD_ZERO(&write_set);
FD_SET(reactor->event_fd, &read_set);
int max_fd = reactor->event_fd;
for (const list_node_t *iter = list_begin(reactor->objects); iter != list_end(reactor->objects); iter = list_next(iter)) {
reactor_object_t *object = (reactor_object_t *)list_node(iter);
int fd = object->fd;
reactor_interest_t interest = object->interest;
if (interest & REACTOR_INTEREST_READ)
FD_SET(fd, &read_set);
if (interest & REACTOR_INTEREST_WRITE)
FD_SET(fd, &write_set);
if (fd > max_fd)
max_fd = fd;
}
int ret;
do {
ret = select(max_fd + 1, &read_set, &write_set, NULL, tv);
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
ALOGE("%s error in select: %s", __func__, strerror(errno));
return REACTOR_STATUS_ERROR;
}
if (ret == 0)
return REACTOR_STATUS_TIMEOUT;
if (FD_ISSET(reactor->event_fd, &read_set)) {
eventfd_t value;
eventfd_read(reactor->event_fd, &value);
return REACTOR_STATUS_STOP;
}
for (const list_node_t *iter = list_begin(reactor->objects); ret > 0 && iter != list_end(reactor->objects); iter = list_next(iter)) {
reactor_object_t *object = (reactor_object_t *)list_node(iter);
int fd = object->fd;
if (FD_ISSET(fd, &read_set)) {
object->read_ready(object->context);
--ret;
}
if (FD_ISSET(fd, &write_set)) {
object->write_ready(object->context);
--ret;
}
}
}
return REACTOR_STATUS_DONE;
}