/* * This file is part of Deliantra, the Roguelike Realtime MMORPG. * * Copyright (©) 2007,2008,2009,2010,2011,2012 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 Affero 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 Affero GNU General Public License * and 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; ///////////////////////////////////////////////////////////////////////////// ev_async tick_watcher; EV_ATOMIC_T coroapi::cede_pending; 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) { SMUTEX_LOCK (evthread_mutex); next_tick = w->at; SMUTEX_UNLOCK (evthread_mutex); coroapi::cede_pending = 1; ev_async_send (EV_DEFAULT_UC, &tick_watcher); } static void tick_cb (EV_P_ ev_async *w, int revents) { cfperl_tick (); } ///////////////////////////////////////////////////////////////////////////// static SMUTEX(cb_mutex); static SCOND(cb_done); static ev_async async_watcher; static ev_io aio_watcher; static void async_cb (EV_P_ ev_async *w, int revents) { CALL_BEGIN (0); CALL_CALL ("IO::AIO::poll_cb", G_SCALAR); IV reqs = POPi; CALL_END; SMUTEX_LOCK (cb_mutex); SCOND_SIGNAL (cb_done); SMUTEX_UNLOCK (cb_mutex); } static void aio_cb (EV_P_ ev_io *w, int revents) { ev_async_send (EV_DEFAULT_UC, &async_watcher); coroapi::cede_pending = 1; // to decrease latency SCOND_WAIT (cb_done, cb_mutex); } ///////////////////////////////////////////////////////////////////////////// static void * evthread_proc (void *arg) { SMUTEX_LOCK (cb_mutex); ev_run (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); // secondary loop, we use select/poll as epoll has very // bad timing characteristics and we only watch a single low fd. loop = ev_loop_new (EVBACKEND_SELECT); ev_periodic_init (&cede_watcher, cede_cb, 0, TICK, 0); ev_periodic_start (loop, &cede_watcher); ev_io_init (&aio_watcher, aio_cb, aiofd, EV_READ); ev_io_start (loop, &aio_watcher); evthread.start (evthread_proc); }