twoapple-reboot/src/timer.d

353 lines
8.9 KiB
D
Raw Normal View History

2012-03-14 00:43:29 +00:00
/+
+ timer.d
+
2012-04-19 05:23:08 +00:00
+ Copyright: 2012 Ed McCardell, 2007 Gerald Stocker
2012-03-14 00:43:29 +00:00
+
2012-03-15 06:21:12 +00:00
+ This file is part of twoapple-reboot.
2012-03-14 00:43:29 +00:00
+
2012-03-15 06:21:12 +00:00
+ twoapple-reboot is free software; you can redistribute it and/or modify
2012-03-14 00:43:29 +00:00
+ 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.
+
2012-03-15 06:21:12 +00:00
+ twoapple-reboot is distributed in the hope that it will be useful,
2012-03-14 00:43:29 +00:00
+ 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
2012-03-15 06:21:12 +00:00
+ along with twoapple-reboot; if not, write to the Free Software
2012-03-14 00:43:29 +00:00
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+/
module timer;
2012-04-19 05:23:08 +00:00
final class Timer
2012-03-14 00:43:29 +00:00
{
2012-04-19 05:23:08 +00:00
private:
static struct Counter
2012-03-15 06:45:30 +00:00
{
2012-04-19 05:23:08 +00:00
uint start, curr;
bool active;
size_t next = -1, nextFree;
bool delegate() expiry;
ulong creationTick;
2012-03-14 00:43:29 +00:00
2012-04-19 05:23:08 +00:00
this(uint length, bool delegate() expiry, ulong creationTick)
2012-03-14 00:43:29 +00:00
{
2012-04-19 05:23:08 +00:00
start = curr = length;
this.expiry = expiry;
this.creationTick = creationTick;
active = true;
2012-03-14 00:43:29 +00:00
}
2012-04-19 05:23:08 +00:00
}
2012-03-14 00:43:29 +00:00
2012-04-19 05:23:08 +00:00
ulong _totalTicks;
uint start = uint.max, curr = uint.max, minCurr = uint.max;
uint balance;
size_t head, tail, nextFree;
Counter[] counters;
uint _hertz;
2012-03-14 00:43:29 +00:00
2012-04-19 05:23:08 +00:00
final void setNextFree()
{
for (size_t i = nextFree; i < counters.length; i++)
counters[i].nextFree = i + 1;
2012-03-15 06:45:30 +00:00
}
2012-03-14 00:43:29 +00:00
2012-04-19 05:23:08 +00:00
final void deleteCounter(size_t idx, size_t prev)
2012-03-15 06:45:30 +00:00
{
2012-04-19 05:23:08 +00:00
auto tmp = nextFree;
nextFree = idx;
counters[idx].nextFree = tmp;
2012-03-15 06:45:30 +00:00
2012-04-19 05:23:08 +00:00
auto next = counters[idx].next;
if (idx == head)
2012-03-15 06:45:30 +00:00
{
2012-04-19 05:23:08 +00:00
head = next;
2012-03-15 06:45:30 +00:00
}
2012-04-19 05:23:08 +00:00
else
2012-03-15 06:45:30 +00:00
{
2012-04-19 05:23:08 +00:00
counters[prev].next = next;
2012-03-15 06:45:30 +00:00
}
2012-04-19 05:23:08 +00:00
if (idx == tail)
2012-03-15 06:45:30 +00:00
{
2012-04-19 05:23:08 +00:00
tail = prev;
counters[tail].next = -1;
2012-03-15 06:45:30 +00:00
}
2012-03-14 00:43:29 +00:00
2012-04-19 05:23:08 +00:00
counters[idx].active = false;
2012-03-15 06:45:30 +00:00
}
2012-03-14 00:43:29 +00:00
2012-04-19 05:23:08 +00:00
public:
this(uint primaryLength, uint hertz, bool delegate() primaryStop)
2012-03-15 06:45:30 +00:00
{
2012-04-19 05:23:08 +00:00
counters = new Counter[50];
setNextFree();
_hertz = hertz;
addCounter(primaryLength, primaryStop);
2012-03-15 06:45:30 +00:00
}
2012-03-14 00:43:29 +00:00
2012-04-19 05:23:08 +00:00
final @property uint primaryRemaining()
2012-03-15 06:45:30 +00:00
{
2012-04-19 05:23:08 +00:00
return counters[0].curr;
2012-03-15 06:45:30 +00:00
}
2012-04-19 05:23:08 +00:00
final @property uint primaryLength()
2012-03-15 06:45:30 +00:00
{
2012-04-19 05:23:08 +00:00
return counters[0].start;
2012-03-15 06:45:30 +00:00
}
2012-03-14 00:43:29 +00:00
2012-04-19 05:23:08 +00:00
final @property uint hertz()
2012-03-15 06:45:30 +00:00
{
2012-04-19 05:23:08 +00:00
return _hertz;
2012-03-15 06:45:30 +00:00
}
2012-03-14 00:43:29 +00:00
2012-04-19 05:23:08 +00:00
final @property ulong totalTicks()
2012-03-15 06:45:30 +00:00
{
2012-04-19 05:23:08 +00:00
return _totalTicks;
2012-03-15 06:45:30 +00:00
}
2012-03-14 00:43:29 +00:00
2012-04-19 05:23:08 +00:00
final void tick()
2012-03-14 00:43:29 +00:00
{
2012-04-19 05:23:08 +00:00
_totalTicks++;
curr--;
if (!curr)
2012-03-14 00:43:29 +00:00
{
2012-04-19 05:23:08 +00:00
minCurr = uint.max;
size_t idx = head;
size_t prev = -1;
while (idx != -1)
2012-03-14 00:43:29 +00:00
{
2012-04-19 05:23:08 +00:00
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);
}
2012-04-26 00:02:43 +00:00
if (counters[idx].active) prev = idx;
2012-04-19 05:23:08 +00:00
idx = counters[idx].next;
2012-03-14 00:43:29 +00:00
}
2012-04-19 05:23:08 +00:00
start = curr = minCurr;
balance = 0;
2012-03-14 00:43:29 +00:00
}
}
2012-04-19 05:23:08 +00:00
final size_t addCounter(uint length, bool delegate() expiry)
2012-03-15 06:45:30 +00:00
{
if (nextFree == counters.length)
{
size_t idx = head;
size_t prev = -1;
while (idx != -1)
{
if (!counters[idx].active)
deleteCounter(idx, prev);
else
prev = idx;
idx = counters[idx].next;
}
}
2012-04-19 05:23:08 +00:00
if (nextFree == counters.length)
{
counters.length += 20;
setNextFree();
}
2012-03-15 06:45:30 +00:00
2012-04-19 05:23:08 +00:00
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)
2012-03-15 06:45:30 +00:00
{
2012-04-19 05:23:08 +00:00
counters[idx].curr += start;
2012-03-15 06:45:30 +00:00
}
else
{
2012-04-19 05:23:08 +00:00
if (counters[idx].curr < curr)
{
balance = start - curr;
start = curr = counters[idx].curr;
}
else
{
counters[idx].curr += (start - curr);
}
2012-03-15 06:45:30 +00:00
}
2012-04-19 05:23:08 +00:00
return idx;
}
final void removeCounter(ulong creationTick, size_t idx)
{
assert(counters[idx].creationTick == creationTick);
counters[idx].active = false;
2012-03-15 06:45:30 +00:00
}
2012-04-19 05:23:08 +00:00
final class Cycle
2012-03-15 06:45:30 +00:00
{
2012-04-19 05:23:08 +00:00
ulong startTick;
uint rollOver;
this(uint rollOver)
2012-03-14 00:43:29 +00:00
{
2012-04-19 05:23:08 +00:00
this.rollOver = rollOver;
restart();
2012-03-14 00:43:29 +00:00
}
2012-04-19 05:23:08 +00:00
final void restart()
2012-03-14 00:43:29 +00:00
{
2012-04-19 05:23:08 +00:00
startTick = _totalTicks;
2012-03-14 00:43:29 +00:00
}
2012-04-19 05:23:08 +00:00
final uint val()
2012-03-14 00:43:29 +00:00
{
2012-04-19 05:23:08 +00:00
return (_totalTicks - startTick) % rollOver;
2012-03-14 00:43:29 +00:00
}
2012-04-19 05:23:08 +00:00
}
}
2012-03-14 00:43:29 +00:00
2012-04-19 05:23:08 +00:00
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()
2012-03-14 00:43:29 +00:00
{
2012-04-19 05:23:08 +00:00
auto ticks = t._totalTicks;
assert((c1 == 0 && ticks == 10) || (c1 == 1 && ticks == 30));
c1++;
t.addCounter(10, &exp2);
return false;
2012-03-14 00:43:29 +00:00
}
2012-04-19 05:23:08 +00:00
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;});
2012-03-15 06:45:30 +00:00
}
2012-04-19 05:23:08 +00:00
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);
2012-03-14 00:43:29 +00:00
2012-04-19 05:23:08 +00:00
foreach (i; 0..7000) t.tick();
2012-03-14 00:43:29 +00:00
}