mirror of https://github.com/adamdruppe/arsd.git
timeout functions
This commit is contained in:
parent
dea59df01c
commit
3f616fc5b2
123
eventloop.d
123
eventloop.d
|
@ -12,6 +12,15 @@ template typehash(T...) {
|
||||||
enum typehash = hashOf(tmp.mangleof.ptr, tmp.mangleof.length);
|
enum typehash = hashOf(tmp.mangleof.ptr, tmp.mangleof.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct TimerInfo {
|
||||||
|
WrappedListener handler;
|
||||||
|
int timeoutRemaining; // in milliseconds
|
||||||
|
int originalTimeout;
|
||||||
|
int countRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimerInfo*[] timers;
|
||||||
|
|
||||||
private WrappedListener[][hash_t] listeners;
|
private WrappedListener[][hash_t] listeners;
|
||||||
private WrappedListener[] idleHandlers;
|
private WrappedListener[] idleHandlers;
|
||||||
|
|
||||||
|
@ -38,6 +47,64 @@ public void removeOnIdle(T)(T t) if(isCallable!T && ParameterTypeTuple!(T).lengt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An opaque type to reference an active timer
|
||||||
|
struct TimerHandle {
|
||||||
|
private TimerInfo* ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a timer, one-shot by default. Count tells how many times the timer will fire. Set to zero for a continuously firing timer
|
||||||
|
public TimerHandle setTimeout(T)(T t, int msecsWait, int count = 1) if(isCallable!T && ParameterTypeTuple!(T).length == 0) {
|
||||||
|
auto ti = new TimerInfo;
|
||||||
|
ti.handler = wrap(t);
|
||||||
|
ti.timeoutRemaining = msecsWait;
|
||||||
|
ti.originalTimeout = msecsWait;
|
||||||
|
ti.countRemaining = count;
|
||||||
|
|
||||||
|
// FIXME: this could prolly be faster by taking advantage of the fact that the timers are sorted
|
||||||
|
bool inserted = false;
|
||||||
|
foreach(idx, timer; timers) {
|
||||||
|
if(timer.timeoutRemaining > msecsWait) {
|
||||||
|
import std.array;
|
||||||
|
insertInPlace(timers, idx, ti);
|
||||||
|
inserted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!inserted)
|
||||||
|
timers ~= ti;
|
||||||
|
|
||||||
|
return TimerHandle(ti);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a continuously firing interval. It will call the function as close to the interval as it can, but it won't let triggers stack up.
|
||||||
|
public TimerHandle setInterval(T)(T t, int msecsInterval) if(isCallable!T && ParameterTypeTuple!(T).length == 0) {
|
||||||
|
return setTimeout(t, msecsInterval, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears a timer
|
||||||
|
public void clearTimeout(TimerHandle handle) {
|
||||||
|
size_t foundIndex = size_t.max;
|
||||||
|
// FIXME: this could prolly be faster by taking advantage of the fact that the timers are sorted
|
||||||
|
foreach(idx, timer; timers) {
|
||||||
|
if(timer is handle.ptr) {
|
||||||
|
foundIndex = idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(foundIndex == size_t.max)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(auto i = foundIndex; i < timers.length - 1; i++)
|
||||||
|
timers[i] = timers[i + 1];
|
||||||
|
timers.length = timers.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearInterval(TimerHandle handle) {
|
||||||
|
clearTimeout(handle);
|
||||||
|
}
|
||||||
|
|
||||||
/// Sends an exit event to the loop. The loop will break when it sees this event, ignoring any events after that point.
|
/// Sends an exit event to the loop. The loop will break when it sees this event, ignoring any events after that point.
|
||||||
public void exit() {
|
public void exit() {
|
||||||
ubyte[backingSize] bufferBacking = 0; // a null message means exit...
|
ubyte[backingSize] bufferBacking = 0; // a null message means exit...
|
||||||
|
@ -396,8 +463,16 @@ version(linux) {
|
||||||
|
|
||||||
epoll_event[16] events;
|
epoll_event[16] events;
|
||||||
|
|
||||||
|
timeval tv;
|
||||||
|
|
||||||
outer_loop: for(;;) {
|
outer_loop: for(;;) {
|
||||||
auto nfds = epoll_wait(epoll, events.ptr, events.length, -1 /* wait forever, otherwise in milliseconds */);
|
int lowestWait = -1; /* wait forever. this is in milliseconds */
|
||||||
|
if(timers.length) {
|
||||||
|
gettimeofday(&tv, null);
|
||||||
|
lowestWait = timers[0].timeoutRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto nfds = epoll_wait(epoll, events.ptr, events.length, lowestWait);
|
||||||
moreEvents:
|
moreEvents:
|
||||||
if(nfds == -1)
|
if(nfds == -1)
|
||||||
throw new Exception("epoll_wait");
|
throw new Exception("epoll_wait");
|
||||||
|
@ -418,9 +493,53 @@ version(linux) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// are any timers ready to fire?
|
||||||
|
if(timers.length) {
|
||||||
|
long prev = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||||
|
gettimeofday(&tv, null);
|
||||||
|
long diff = tv.tv_sec * 1000 + tv.tv_usec / 1000 - prev;
|
||||||
|
|
||||||
|
bool resetDone = false;
|
||||||
|
for(size_t idx = 0; idx < timers.length; idx++) {
|
||||||
|
auto timer = timers[idx];
|
||||||
|
timer.timeoutRemaining -= diff;
|
||||||
|
if(timer.timeoutRemaining <= 0) {
|
||||||
|
if(timer.countRemaining) {
|
||||||
|
timer.countRemaining--;
|
||||||
|
if(timer.countRemaining != 0)
|
||||||
|
goto reset;
|
||||||
|
// otherwise we should remove it
|
||||||
|
for(size_t i2 = idx; i2 < timers.length - 1; i2++) {
|
||||||
|
timers[i2] = timers[i2 + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
timers.length = timers.length - 1;
|
||||||
|
idx--; // cuz we removed it, this keeps the outer loop going
|
||||||
|
} else {
|
||||||
|
reset:
|
||||||
|
timer.timeoutRemaining += timer.originalTimeout;
|
||||||
|
// this is meant to throttle - if we missed a frame, oh well, just skip it instead of trying to throttle
|
||||||
|
// FIXME: maybe the throttling should be configurable
|
||||||
|
if(timer.timeoutRemaining <= 0)
|
||||||
|
timer.timeoutRemaining = timer.originalTimeout;
|
||||||
|
resetDone = true;
|
||||||
|
}
|
||||||
|
timer.handler.call(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(resetDone) {
|
||||||
|
// it could be out of order now, so we'll resort
|
||||||
|
import std.algorithm;
|
||||||
|
import std.range;
|
||||||
|
timers = sort!("a.timeoutRemaining < b.timeoutRemaining")(timers).array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nfds = epoll_wait(epoll, events.ptr, events.length, 0 /* no wait */);
|
nfds = epoll_wait(epoll, events.ptr, events.length, 0 /* no wait */);
|
||||||
if(nfds != 0)
|
if(nfds != 0)
|
||||||
goto moreEvents;
|
goto moreEvents;
|
||||||
|
|
||||||
// no immediate events means we're idle for now, run those functions
|
// no immediate events means we're idle for now, run those functions
|
||||||
foreach(idleHandler; idleHandlers)
|
foreach(idleHandler; idleHandlers)
|
||||||
idleHandler.call(null);
|
idleHandler.call(null);
|
||||||
|
@ -602,6 +721,8 @@ version(linux) {
|
||||||
int epoll_create1(int flags);
|
int epoll_create1(int flags);
|
||||||
int epoll_ctl(int epfd, int op, int fd, epoll_event* event);
|
int epoll_ctl(int epfd, int op, int fd, epoll_event* event);
|
||||||
int epoll_wait(int epfd, epoll_event* events, int maxevents, int timeout);
|
int epoll_wait(int epfd, epoll_event* events, int maxevents, int timeout);
|
||||||
|
|
||||||
|
import core.sys.posix.sys.time;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* **** */
|
/* **** */
|
||||||
|
|
Loading…
Reference in New Issue