340 lines
8.5 KiB
D
340 lines
8.5 KiB
D
/+
|
|
+ timer.d
|
|
+
|
|
+ Copyright: 2012 Ed McCardell, 2007 Gerald Stocker
|
|
+
|
|
+ This file is part of twoapple-reboot.
|
|
+
|
|
+ twoapple-reboot 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 2 of the License, or
|
|
+ (at your option) any later version.
|
|
+
|
|
+ twoapple-reboot 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 twoapple-reboot; if not, write to the Free Software
|
|
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
+/
|
|
|
|
module timer;
|
|
|
|
final class Timer
|
|
{
|
|
private:
|
|
static struct Counter
|
|
{
|
|
uint start, curr;
|
|
bool active;
|
|
size_t next = -1, nextFree;
|
|
bool delegate() expiry;
|
|
ulong creationTick;
|
|
|
|
this(uint length, bool delegate() expiry, ulong creationTick)
|
|
{
|
|
start = curr = length;
|
|
this.expiry = expiry;
|
|
this.creationTick = creationTick;
|
|
active = true;
|
|
}
|
|
}
|
|
|
|
ulong _totalTicks;
|
|
uint start = uint.max, curr = uint.max, minCurr = uint.max;
|
|
uint balance;
|
|
size_t head, tail, nextFree;
|
|
Counter[] counters;
|
|
uint _hertz;
|
|
|
|
final void setNextFree()
|
|
{
|
|
for (size_t i = nextFree; i < counters.length; i++)
|
|
counters[i].nextFree = i + 1;
|
|
}
|
|
|
|
final void deleteCounter(size_t idx, size_t prev)
|
|
{
|
|
auto tmp = nextFree;
|
|
nextFree = idx;
|
|
counters[idx].nextFree = tmp;
|
|
|
|
auto next = counters[idx].next;
|
|
if (idx == head)
|
|
{
|
|
head = next;
|
|
}
|
|
else
|
|
{
|
|
counters[prev].next = next;
|
|
}
|
|
if (idx == tail)
|
|
{
|
|
tail = prev;
|
|
counters[tail].next = -1;
|
|
}
|
|
|
|
counters[idx].active = false;
|
|
}
|
|
|
|
public:
|
|
this(uint primaryLength, uint hertz, bool delegate() primaryStop)
|
|
{
|
|
counters = new Counter[50];
|
|
setNextFree();
|
|
_hertz = hertz;
|
|
addCounter(primaryLength, primaryStop);
|
|
}
|
|
|
|
final @property uint primaryRemaining()
|
|
{
|
|
return counters[0].curr;
|
|
}
|
|
|
|
final @property uint primaryLength()
|
|
{
|
|
return counters[0].start;
|
|
}
|
|
|
|
final @property uint hertz()
|
|
{
|
|
return _hertz;
|
|
}
|
|
|
|
final @property ulong totalTicks()
|
|
{
|
|
return _totalTicks;
|
|
}
|
|
|
|
final void tick()
|
|
{
|
|
_totalTicks++;
|
|
curr--;
|
|
if (!curr)
|
|
{
|
|
minCurr = uint.max;
|
|
size_t idx = head;
|
|
size_t prev = -1;
|
|
while (idx != -1)
|
|
{
|
|
if (counters[idx].active)
|
|
{
|
|
counters[idx].curr -= start;
|
|
if (counters[idx].curr) counters[idx].curr -= balance;
|
|
if (!counters[idx].curr)
|
|
{
|
|
if (counters[idx].expiry())
|
|
counters[idx].curr = counters[idx].start;
|
|
else
|
|
deleteCounter(idx, prev);
|
|
}
|
|
if (counters[idx].active && counters[idx].curr < minCurr)
|
|
minCurr = counters[idx].curr;
|
|
}
|
|
else
|
|
{
|
|
deleteCounter(idx, prev);
|
|
}
|
|
if (counters[idx].active) prev = idx;
|
|
idx = counters[idx].next;
|
|
}
|
|
start = curr = minCurr;
|
|
balance = 0;
|
|
}
|
|
}
|
|
|
|
final size_t addCounter(uint length, bool delegate() expiry)
|
|
{
|
|
if (nextFree == counters.length)
|
|
{
|
|
counters.length += 20;
|
|
setNextFree();
|
|
}
|
|
|
|
auto idx = nextFree;
|
|
nextFree = counters[nextFree].nextFree;
|
|
counters[idx] = Counter(length, expiry, _totalTicks);
|
|
counters[tail].next = idx;
|
|
tail = idx;
|
|
counters[tail].next = -1;
|
|
|
|
if (curr == 0)
|
|
{
|
|
counters[idx].curr += start;
|
|
}
|
|
else
|
|
{
|
|
if (counters[idx].curr < curr)
|
|
{
|
|
balance = start - curr;
|
|
start = curr = counters[idx].curr;
|
|
}
|
|
else
|
|
{
|
|
counters[idx].curr += (start - curr);
|
|
}
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
final void removeCounter(ulong creationTick, size_t idx)
|
|
{
|
|
assert(counters[idx].creationTick == creationTick);
|
|
counters[idx].active = false;
|
|
}
|
|
|
|
|
|
final class Cycle
|
|
{
|
|
ulong startTick;
|
|
uint rollOver;
|
|
|
|
this(uint rollOver)
|
|
{
|
|
this.rollOver = rollOver;
|
|
restart();
|
|
}
|
|
|
|
final void restart()
|
|
{
|
|
startTick = _totalTicks;
|
|
}
|
|
|
|
final uint val()
|
|
{
|
|
return (_totalTicks - startTick) % rollOver;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
unittest
|
|
{
|
|
bool primary() { return true; }
|
|
|
|
auto t = new Timer(10205, 1020484, &primary);
|
|
int c1 = 0, c2 = 0;
|
|
t.addCounter(10, (){assert(t._totalTicks == 10); c1++; return true;});
|
|
foreach (i; 0..9) t.tick();
|
|
t.addCounter(5, (){assert(t._totalTicks == 14); c2++; return true;});
|
|
foreach (i; 0..5) t.tick();
|
|
assert (c1 == 1 && c2 == 1);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
bool primary() { return true; }
|
|
|
|
auto t = new Timer(10205, 1020484, &primary);
|
|
int c1 = 0, c2 = 0;
|
|
struct Dummy
|
|
{
|
|
bool exp1()
|
|
{
|
|
auto ticks = t._totalTicks;
|
|
assert((c1 == 0 && ticks == 10) || (c1 == 1 && ticks == 30));
|
|
c1++;
|
|
t.addCounter(10, &exp2);
|
|
return false;
|
|
}
|
|
bool exp2()
|
|
{
|
|
auto ticks = t._totalTicks;
|
|
assert((c2 == 0 && ticks == 20) || (c2 == 1 && ticks == 40));
|
|
c2++;
|
|
t.addCounter(10, &exp1);
|
|
return false;
|
|
}
|
|
}
|
|
Dummy d;
|
|
t.addCounter(10, &d.exp1);
|
|
foreach (i; 0..40) t.tick();
|
|
assert(c1 == 2 && c2 == 2);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
bool primary() { return true; }
|
|
|
|
auto t = new Timer(10205, 1020484, &primary);
|
|
int c1 = 0, c2 = 0, c3 = 0;
|
|
|
|
void addExtra()
|
|
{
|
|
t.addCounter(4, (){auto ticks = t._totalTicks;
|
|
assert((c1 == 0 && ticks == 14) ||
|
|
(c1 == 1 && ticks == 24));
|
|
c1++;
|
|
return false;});
|
|
t.addCounter(5, (){auto ticks = t._totalTicks;
|
|
assert((c2 == 0 && ticks == 15) ||
|
|
(c2 == 1 && ticks == 25));
|
|
c2++;
|
|
return false;});
|
|
t.addCounter(6, (){auto ticks = t._totalTicks;
|
|
assert((c3 == 0 && ticks == 16) ||
|
|
(c3 == 1 && ticks == 26));
|
|
c3++;
|
|
return false;});
|
|
}
|
|
t.addCounter(10, (){addExtra(); return true;});
|
|
foreach (i; 0..30) t.tick();
|
|
assert (c1 == 2 && c2 == 2 && c3 == 2);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
bool primary() { return true; }
|
|
|
|
auto t = new Timer(10205, 1020484, &primary);
|
|
auto c1 = t.addCounter(10, (){assert(false); return false;});
|
|
auto c1_tick = t._totalTicks;
|
|
foreach (i; 0..5) t.tick();
|
|
t.removeCounter(c1_tick, c1);
|
|
foreach (i; 0..5) t.tick();
|
|
}
|
|
|
|
unittest
|
|
{
|
|
bool primary() { return true; }
|
|
|
|
auto t = new Timer(10205, 1020484, &primary);
|
|
int c1 = 0, c2 = 0;
|
|
t.addCounter(10, (){c1++; return true;});
|
|
auto idx = t.addCounter(15, (){c2++; return true;});
|
|
auto tick = t._totalTicks;
|
|
foreach (i; 0..20) t.tick();
|
|
assert(c1 == 2 && c2 == 1);
|
|
t.removeCounter(tick, idx);
|
|
foreach (i; 0..20) t.tick();
|
|
assert(c1 == 4 && c2 == 1);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
bool primary() { return true; }
|
|
|
|
auto t = new Timer(10205, 1020484, &primary);
|
|
|
|
bool t1() { assert(!(t.totalTicks % 5000)); return true; }
|
|
bool t2() { assert(!(t.totalTicks % 4500)); return true; }
|
|
bool t3() { assert(!(t.totalTicks % 6500)); return true; }
|
|
bool junk() { assert(!((t.totalTicks - 7000) % 1500)); return true; }
|
|
|
|
t.addCounter(5000, &t1);
|
|
t.addCounter(4500, &t2);
|
|
t.addCounter(6500, &t3);
|
|
|
|
foreach (i; 0..7000) t.tick();
|
|
|
|
t.addCounter(1500, &junk);
|
|
t.addCounter(1500, &junk);
|
|
t.addCounter(1500, &junk);
|
|
t.addCounter(1500, &junk);
|
|
|
|
foreach (i; 0..7000) t.tick();
|
|
}
|