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