/* * Copyright (c) 2009-2011, Salvatore Sanfilippo * * SPDX-FileCopyrightText: 2024 Hiredict Contributors * SPDX-FileCopyrightText: 2024 Salvatore Sanfilippo * * SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: LGPL-3.0-or-later * */ #ifndef __HIREDICT_LIBUV_H__ #define __HIREDICT_LIBUV_H__ #include #include #include "../hiredict.h" #include "../async.h" #include typedef struct redictLibuvEvents { redictAsyncContext* context; uv_poll_t handle; uv_timer_t timer; int events; } redictLibuvEvents; static void redictLibuvPoll(uv_poll_t* handle, int status, int events) { redictLibuvEvents* p = (redictLibuvEvents*)handle->data; int ev = (status ? p->events : events); if (p->context != NULL && (ev & UV_READABLE)) { redictAsyncHandleRead(p->context); } if (p->context != NULL && (ev & UV_WRITABLE)) { redictAsyncHandleWrite(p->context); } } static void redictLibuvAddRead(void *privdata) { redictLibuvEvents* p = (redictLibuvEvents*)privdata; if (p->events & UV_READABLE) { return; } p->events |= UV_READABLE; uv_poll_start(&p->handle, p->events, redictLibuvPoll); } static void redictLibuvDelRead(void *privdata) { redictLibuvEvents* p = (redictLibuvEvents*)privdata; p->events &= ~UV_READABLE; if (p->events) { uv_poll_start(&p->handle, p->events, redictLibuvPoll); } else { uv_poll_stop(&p->handle); } } static void redictLibuvAddWrite(void *privdata) { redictLibuvEvents* p = (redictLibuvEvents*)privdata; if (p->events & UV_WRITABLE) { return; } p->events |= UV_WRITABLE; uv_poll_start(&p->handle, p->events, redictLibuvPoll); } static void redictLibuvDelWrite(void *privdata) { redictLibuvEvents* p = (redictLibuvEvents*)privdata; p->events &= ~UV_WRITABLE; if (p->events) { uv_poll_start(&p->handle, p->events, redictLibuvPoll); } else { uv_poll_stop(&p->handle); } } static void on_timer_close(uv_handle_t *handle) { redictLibuvEvents* p = (redictLibuvEvents*)handle->data; p->timer.data = NULL; if (!p->handle.data) { // both timer and handle are closed hi_free(p); } // else, wait for `on_handle_close` } static void on_handle_close(uv_handle_t *handle) { redictLibuvEvents* p = (redictLibuvEvents*)handle->data; p->handle.data = NULL; if (!p->timer.data) { // timer never started, or timer already destroyed hi_free(p); } // else, wait for `on_timer_close` } // libuv removed `status` parameter since v0.11.23 // see: https://github.com/libuv/libuv/blob/v0.11.23/include/uv.h #if (UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR < 11) || \ (UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR == 11 && UV_VERSION_PATCH < 23) static void redictLibuvTimeout(uv_timer_t *timer, int status) { (void)status; // unused #else static void redictLibuvTimeout(uv_timer_t *timer) { #endif redictLibuvEvents *e = (redictLibuvEvents*)timer->data; redictAsyncHandleTimeout(e->context); } static void redictLibuvSetTimeout(void *privdata, struct timeval tv) { redictLibuvEvents* p = (redictLibuvEvents*)privdata; uint64_t millsec = tv.tv_sec * 1000 + tv.tv_usec / 1000.0; if (!p->timer.data) { // timer is uninitialized if (uv_timer_init(p->handle.loop, &p->timer) != 0) { return; } p->timer.data = p; } // updates the timeout if the timer has already started // or start the timer uv_timer_start(&p->timer, redictLibuvTimeout, millsec, 0); } static void redictLibuvCleanup(void *privdata) { redictLibuvEvents* p = (redictLibuvEvents*)privdata; p->context = NULL; // indicate that context might no longer exist if (p->timer.data) { uv_close((uv_handle_t*)&p->timer, on_timer_close); } uv_close((uv_handle_t*)&p->handle, on_handle_close); } static int redictLibuvAttach(redictAsyncContext* ac, uv_loop_t* loop) { redictContext *c = &(ac->c); if (ac->ev.data != NULL) { return REDICT_ERR; } ac->ev.addRead = redictLibuvAddRead; ac->ev.delRead = redictLibuvDelRead; ac->ev.addWrite = redictLibuvAddWrite; ac->ev.delWrite = redictLibuvDelWrite; ac->ev.cleanup = redictLibuvCleanup; ac->ev.scheduleTimer = redictLibuvSetTimeout; redictLibuvEvents* p = (redictLibuvEvents*)hi_malloc(sizeof(*p)); if (p == NULL) return REDICT_ERR; memset(p, 0, sizeof(*p)); if (uv_poll_init_socket(loop, &p->handle, c->fd) != 0) { hi_free(p); return REDICT_ERR; } ac->ev.data = p; p->handle.data = p; p->context = ac; return REDICT_OK; } #endif