/* * Copyright (c) 2010-2011, Pieter Noordhuis * * SPDX-FileCopyrightText: 2024 Hiredict Contributors * SPDX-FileCopyrightText: 2024 Pieter Noordhuis * * SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: LGPL-3.0-or-later * */ #ifndef __HIREDICT_LIBEVENT_H__ #define __HIREDICT_LIBEVENT_H__ #include #include "../hiredict.h" #include "../async.h" #define REDICT_LIBEVENT_DELETED 0x01 #define REDICT_LIBEVENT_ENTERED 0x02 typedef struct redictLibeventEvents { redictAsyncContext *context; struct event *ev; struct event_base *base; struct timeval tv; short flags; short state; } redictLibeventEvents; static void redictLibeventDestroy(redictLibeventEvents *e) { hi_free(e); } static void redictLibeventHandler(evutil_socket_t fd, short event, void *arg) { ((void)fd); redictLibeventEvents *e = (redictLibeventEvents*)arg; e->state |= REDICT_LIBEVENT_ENTERED; #define CHECK_DELETED() if (e->state & REDICT_LIBEVENT_DELETED) {\ redictLibeventDestroy(e);\ return; \ } if ((event & EV_TIMEOUT) && (e->state & REDICT_LIBEVENT_DELETED) == 0) { redictAsyncHandleTimeout(e->context); CHECK_DELETED(); } if ((event & EV_READ) && e->context && (e->state & REDICT_LIBEVENT_DELETED) == 0) { redictAsyncHandleRead(e->context); CHECK_DELETED(); } if ((event & EV_WRITE) && e->context && (e->state & REDICT_LIBEVENT_DELETED) == 0) { redictAsyncHandleWrite(e->context); CHECK_DELETED(); } e->state &= ~REDICT_LIBEVENT_ENTERED; #undef CHECK_DELETED } static void redictLibeventUpdate(void *privdata, short flag, int isRemove) { redictLibeventEvents *e = (redictLibeventEvents *)privdata; const struct timeval *tv = e->tv.tv_sec || e->tv.tv_usec ? &e->tv : NULL; if (isRemove) { if ((e->flags & flag) == 0) { return; } else { e->flags &= ~flag; } } else { if (e->flags & flag) { return; } else { e->flags |= flag; } } event_del(e->ev); event_assign(e->ev, e->base, e->context->c.fd, e->flags | EV_PERSIST, redictLibeventHandler, privdata); event_add(e->ev, tv); } static void redictLibeventAddRead(void *privdata) { redictLibeventUpdate(privdata, EV_READ, 0); } static void redictLibeventDelRead(void *privdata) { redictLibeventUpdate(privdata, EV_READ, 1); } static void redictLibeventAddWrite(void *privdata) { redictLibeventUpdate(privdata, EV_WRITE, 0); } static void redictLibeventDelWrite(void *privdata) { redictLibeventUpdate(privdata, EV_WRITE, 1); } static void redictLibeventCleanup(void *privdata) { redictLibeventEvents *e = (redictLibeventEvents*)privdata; if (!e) { return; } event_del(e->ev); event_free(e->ev); e->ev = NULL; if (e->state & REDICT_LIBEVENT_ENTERED) { e->state |= REDICT_LIBEVENT_DELETED; } else { redictLibeventDestroy(e); } } static void redictLibeventSetTimeout(void *privdata, struct timeval tv) { redictLibeventEvents *e = (redictLibeventEvents *)privdata; short flags = e->flags; e->flags = 0; e->tv = tv; redictLibeventUpdate(e, flags, 0); } static int redictLibeventAttach(redictAsyncContext *ac, struct event_base *base) { redictContext *c = &(ac->c); redictLibeventEvents *e; /* Nothing should be attached when something is already attached */ if (ac->ev.data != NULL) return REDICT_ERR; /* Create container for context and r/w events */ e = (redictLibeventEvents*)hi_calloc(1, sizeof(*e)); if (e == NULL) return REDICT_ERR; e->context = ac; /* Register functions to start/stop listening for events */ ac->ev.addRead = redictLibeventAddRead; ac->ev.delRead = redictLibeventDelRead; ac->ev.addWrite = redictLibeventAddWrite; ac->ev.delWrite = redictLibeventDelWrite; ac->ev.cleanup = redictLibeventCleanup; ac->ev.scheduleTimer = redictLibeventSetTimeout; ac->ev.data = e; /* Initialize and install read/write events */ e->ev = event_new(base, c->fd, EV_READ | EV_WRITE, redictLibeventHandler, e); e->base = base; return REDICT_OK; } #endif