/*
* 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);
}