/* * This file is part of Deliantra, the Roguelike Realtime MMORPG. * * Copyright (©) 2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team * * Deliantra is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * The authors can be reached via e-mail to */ #include "global.h" #include "evthread.h" #include "cfperl.h" thread evthread; static ev_loop *loop; #define TICK_CYCLES 4 // the tick watcher is activated every TICK_CYCLES ev_async tick_watcher; EV_ATOMIC_T coroapi::cede_pending; // the ticker runs TICK_CYCLES times as fast as the main server tick static ev_periodic cede_watcher; ///////////////////////////////////////////////////////////////////////////// // it is a bit silly to have to use a mutex, but contention // is basically non-existant, favouring good mutex implementations. static SMUTEX (evthread_mutex); static NV next_tick; NV get_next_tick () { SMUTEX_LOCK (evthread_mutex); NV retval = next_tick; SMUTEX_UNLOCK (evthread_mutex); return retval; } static void cede_cb (EV_P_ ev_periodic *w, int revents) { static int cycle = 0; if (++cycle == TICK_CYCLES) { cycle = 0; SMUTEX_LOCK (evthread_mutex); next_tick = w->at + TICK / TICK_CYCLES * (TICK_CYCLES - 1); SMUTEX_UNLOCK (evthread_mutex); ev_async_send (EV_DEFAULT_UC, &tick_watcher); } coroapi::cede_pending = 1; } static void tick_cb (EV_P_ ev_async *w, int revents) { cfperl_tick (); } ///////////////////////////////////////////////////////////////////////////// static ev_async async_watcher; static ev_io aio1_watcher; static ev_idle aio2_watcher; static EV_ATOMIC_T aio_pending; static void aio2_cb (EV_P_ ev_idle *w, int revents) { IV reqs; CALL_BEGIN (0); CALL_CALL ("IO::AIO::poll_cb", G_SCALAR); reqs = POPi; if (count > 0 && reqs < 0) ev_idle_start (EV_A, w); else ev_idle_stop (EV_A, w); CALL_END; } static void async_cb (EV_P_ ev_async *w, int revents) { aio2_cb (EV_A, &aio2_watcher, 0); } static void aio1_cb (EV_P_ ev_io *w, int revents) { char dummy[9]; if (read (w->fd, dummy, sizeof (dummy)) > 0) { ev_async_send (EV_DEFAULT_UC, &async_watcher); coroapi::cede_pending = 1; } } ///////////////////////////////////////////////////////////////////////////// static void * evthread_proc (void *arg) { ev_loop (loop, 0); return 0; } void evthread_start (int aiofd) { I_EV_API ("evthread"); // main loop ev_async_init (&tick_watcher, tick_cb); ev_set_priority (&tick_watcher, 1); ev_async_start (EV_DEFAULT_UC, &tick_watcher); ev_async_init (&async_watcher, async_cb); ev_set_priority (&async_watcher, 1); ev_async_start (EV_DEFAULT_UC, &async_watcher); ev_idle_init (&aio2_watcher, aio2_cb); // secondary loop loop = ev_loop_new (0); ev_periodic_init (&cede_watcher, cede_cb, 0, TICK / TICK_CYCLES, 0); ev_periodic_start (loop, &cede_watcher); ev_io_init (&aio1_watcher, aio1_cb, aiofd, EV_READ); ev_io_start (loop, &aio1_watcher); evthread.start (evthread_proc); }