/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright 2011-2020 Couchbase, 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. */ /** * This file contains IO operations that use libevent * * @author Trond Norbye * @todo add more documentation */ #define LCB_IOPS_V12_NO_DEPRECATE #include "config.h" #include #include "libevent_io_opts.h" #include #include #include #include struct libevent_cookie { struct event_base *base; int allocated; }; #ifndef HAVE_LIBEVENT2 /* libevent 1.x compatibility layer */ #ifndef evutil_socket_t #define evutil_socket_t int #endif typedef void (*event_callback_fn)(evutil_socket_t, short, void *); static int event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, short events, event_callback_fn callback, void *arg) { ev->ev_callback = callback; ev->ev_arg = arg; ev->ev_fd = fd; ev->ev_events = events; ev->ev_res = 0; ev->ev_flags = EVLIST_INIT; ev->ev_ncalls = 0; ev->ev_pncalls = NULL; event_base_set(base, ev); return 0; } static struct event *event_new(struct event_base *base, evutil_socket_t fd, short events, event_callback_fn cb, void *arg) { struct event *ev; ev = malloc(sizeof(struct event)); if (ev == NULL) { return NULL; } if (event_assign(ev, base, fd, events, cb, arg) < 0) { free(ev); return NULL; } return ev; } static void event_free(struct event *ev) { /* make sure that this event won't be coming back to haunt us. */ free(ev); } static short event_get_events(const struct event *ev) { return ev->ev_events; } static event_callback_fn event_get_callback(const struct event *ev) { return ev->ev_callback; } #endif static void *lcb_io_create_event(struct lcb_io_opt_st *iops) { return event_new(((struct libevent_cookie *)iops->v.v2.cookie)->base, INVALID_SOCKET, 0, NULL, NULL); } static int lcb_io_update_event(struct lcb_io_opt_st *iops, lcb_socket_t sock, void *event, short flags, void *cb_data, void (*handler)(lcb_socket_t sock, short which, void *cb_data)) { flags |= EV_PERSIST; if (flags == event_get_events(event) && handler == event_get_callback(event)) { /* no change! */ return 0; } if (event_pending(event, EV_READ | EV_WRITE, 0)) { event_del(event); } event_assign(event, ((struct libevent_cookie *)iops->v.v2.cookie)->base, sock, flags, handler, cb_data); return event_add(event, NULL); } static void lcb_io_delete_timer(struct lcb_io_opt_st *iops, void *event) { (void)iops; if (event_pending(event, EV_TIMEOUT, 0) != 0 && event_del(event) == -1) { iops->v.v2.error = EINVAL; } event_assign(event, ((struct libevent_cookie *)iops->v.v2.cookie)->base, -1, 0, NULL, NULL); } static int lcb_io_update_timer(struct lcb_io_opt_st *iops, void *timer, lcb_uint32_t usec, void *cb_data, void (*handler)(lcb_socket_t sock, short which, void *cb_data)) { short flags = EV_TIMEOUT | EV_PERSIST; struct timeval tmo; if (flags == event_get_events(timer) && handler == event_get_callback(timer)) { /* no change! */ return 0; } if (event_pending(timer, EV_TIMEOUT, 0)) { event_del(timer); } event_assign(timer, ((struct libevent_cookie *)iops->v.v2.cookie)->base, -1, flags, handler, cb_data); tmo.tv_sec = usec / 1000000; tmo.tv_usec = usec % 1000000; return event_add(timer, &tmo); } static void lcb_io_destroy_event(struct lcb_io_opt_st *iops, void *event) { (void)iops; if (event_pending(event, EV_READ | EV_WRITE | EV_TIMEOUT, 0)) { event_del(event); } event_free(event); } static void lcb_io_delete_event(struct lcb_io_opt_st *iops, lcb_socket_t sock, void *event) { (void)iops; (void)sock; if (event_del(event) == -1) { iops->v.v2.error = EINVAL; } event_assign(event, ((struct libevent_cookie *)iops->v.v2.cookie)->base, -1, 0, NULL, NULL); } static void lcb_io_stop_event_loop(struct lcb_io_opt_st *iops) { event_base_loopbreak(((struct libevent_cookie *)iops->v.v2.cookie)->base); } static void lcb_io_run_event_loop(struct lcb_io_opt_st *iops) { event_base_loop(((struct libevent_cookie *)iops->v.v2.cookie)->base, 0); } static void lcb_io_tick_event_loop(struct lcb_io_opt_st *iops) { event_base_loop(((struct libevent_cookie *)iops->v.v2.cookie)->base, EVLOOP_NONBLOCK); } static void lcb_destroy_io_opts(struct lcb_io_opt_st *iops) { if (((struct libevent_cookie *)iops->v.v2.cookie)->allocated) { event_base_free(((struct libevent_cookie *)iops->v.v2.cookie)->base); } free(iops->v.v2.cookie); free(iops); } static void procs2_lnt_callback(int version, lcb_loop_procs *loop_procs, lcb_timer_procs *timer_procs, lcb_bsd_procs *bsd_procs, lcb_ev_procs *ev_procs, lcb_completion_procs *completion_procs, lcb_iomodel_t *iomodel) { ev_procs->create = lcb_io_create_event; ev_procs->destroy = lcb_io_destroy_event; ev_procs->watch = lcb_io_update_event; ev_procs->cancel = lcb_io_delete_event; timer_procs->create = lcb_io_create_event; timer_procs->destroy = lcb_io_destroy_event; timer_procs->schedule = lcb_io_update_timer; timer_procs->cancel = lcb_io_delete_timer; loop_procs->start = lcb_io_run_event_loop; loop_procs->stop = lcb_io_stop_event_loop; loop_procs->tick = lcb_io_tick_event_loop; *iomodel = LCB_IOMODEL_EVENT; wire_lcb_bsd_impl2(bsd_procs, version); (void)completion_procs; } LIBCOUCHBASE_API lcb_STATUS lcb_create_libevent_io_opts(int version, lcb_io_opt_t *io, void *arg) { struct event_base *base = arg; struct lcb_io_opt_st *ret; struct libevent_cookie *cookie; if (version != 0) { return LCB_ERR_PLUGIN_VERSION_MISMATCH; } ret = calloc(1, sizeof(*ret)); cookie = calloc(1, sizeof(*cookie)); if (ret == NULL || cookie == NULL) { free(ret); free(cookie); return LCB_ERR_NO_MEMORY; } /* setup io iops! */ ret->version = 3; ret->dlhandle = NULL; ret->destructor = lcb_destroy_io_opts; /* consider that struct isn't allocated by the library, * `need_cleanup' flag might be set in lcb_create() */ ret->v.v3.need_cleanup = 0; if (base == NULL) { if ((cookie->base = event_base_new()) == NULL) { free(ret); free(cookie); return LCB_ERR_NO_MEMORY; } cookie->allocated = 1; } else { cookie->base = base; cookie->allocated = 0; } ret->v.v3.cookie = cookie; ret->v.v3.get_procs = procs2_lnt_callback; /* For back-compat */ wire_lcb_bsd_impl(ret); *io = ret; return LCB_SUCCESS; }