Skip to content

Commit 49b2cba

Browse files
committed
Add experimental fast timers
1 parent edd6435 commit 49b2cba

File tree

2 files changed

+225
-0
lines changed

2 files changed

+225
-0
lines changed

src/FastTimers.h

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <unistd.h>
4+
#include <limits.h>
5+
6+
/* Free timers is a stack of available timer offsets */
7+
unsigned int freeTimersHead = 0;
8+
unsigned int freeTimers[1000000];
9+
10+
/* Every timer is a bunch of components */
11+
unsigned int componentMultiplierMs[5] = {10, 50, 100, 500, 1000};
12+
13+
struct Timer {
14+
/* Offsets in timers array */
15+
unsigned int next, prev;
16+
17+
/* The components remaining for this untriggered run */
18+
unsigned int components[5];
19+
20+
/* We need to track the original Ms in case of repeat timers */
21+
unsigned int nextOriginalMs;
22+
23+
/* Overshoot Ms is used to track accumulated overshoot and to remove smallest 10ms when overshot more than 10ms */
24+
unsigned int overshootMs;
25+
26+
/* What list the timer is in */
27+
unsigned int componentOffset;
28+
};
29+
30+
/* Timers are strides over an integer array */
31+
struct Timer timers[1000000];
32+
33+
/* Timer to registered callback */
34+
void (*timerCallbacks[1000000])();
35+
36+
/* Per component timer doubly linked list (head) */
37+
unsigned int timerListHead[5] = {UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX};
38+
39+
void cb() {
40+
printf("A timer triggered!\n");
41+
}
42+
43+
void init() {
44+
freeTimersHead = 0;
45+
for (int i = 0; i < 1000000; i++) {
46+
freeTimers[i] = i;
47+
}
48+
for (int i = 0; i < 5; i++) {
49+
timerListHead[i] = UINT_MAX;
50+
}
51+
}
52+
53+
unsigned int allocateTimer() {
54+
freeTimersHead++;
55+
//printf("Allocating timer: %d\n", freeTimers[freeTimersHead - 1]);
56+
return freeTimers[freeTimersHead - 1];
57+
}
58+
59+
void freeTimer(unsigned int timer) {
60+
//printf("Freeing timer: %d\n", timer);
61+
freeTimersHead--;
62+
freeTimers[freeTimersHead] = timer;
63+
}
64+
65+
unsigned int divideComponents(unsigned int components[5], unsigned int *biggestSetComponent, unsigned int ms) {
66+
/* Start on fifth component */
67+
int biggestComponent = 4;
68+
69+
while (ms > 0 && biggestComponent >= 0) {
70+
/* Can we divide by this component? */
71+
if (ms >= componentMultiplierMs[biggestComponent]) {
72+
components[biggestComponent] = ms / componentMultiplierMs[biggestComponent];
73+
ms -= components[biggestComponent] * componentMultiplierMs[biggestComponent];
74+
75+
if (*biggestSetComponent < biggestComponent) {
76+
*biggestSetComponent = biggestComponent;
77+
}
78+
}
79+
biggestComponent--;
80+
}
81+
82+
unsigned int overshoot = 0;
83+
if (ms) {
84+
components[0]++;
85+
overshoot = 10 - ms;
86+
}
87+
88+
/* Return the overshoot */
89+
return overshoot;
90+
}
91+
92+
void addTimerToList(unsigned int timer, unsigned int componentOffset) {
93+
unsigned int head = timerListHead[componentOffset];
94+
timers[timer].next = head;
95+
timers[timer].prev = UINT_MAX;
96+
if (head != UINT_MAX) {
97+
timers[head].prev = timer;
98+
}
99+
timerListHead[componentOffset] = timer;
100+
101+
/* Track what list the timer is in */
102+
timers[timer].componentOffset = componentOffset;
103+
}
104+
105+
/* Returns the next timer in the list or UINT_MAX */
106+
unsigned int removeTimerFromList(unsigned int timer, unsigned int componentOffset) {
107+
unsigned int prev = timers[timer].prev;
108+
unsigned int next = timers[timer].next;
109+
110+
if (prev != UINT_MAX) {
111+
timers[prev].next = next;
112+
} else {
113+
timerListHead[componentOffset] = next;
114+
}
115+
116+
if (next != UINT_MAX) {
117+
timers[next].prev = prev;
118+
}
119+
120+
return next;
121+
}
122+
123+
unsigned int moveTimerToList(unsigned int timer, unsigned int newComponentOffset) {
124+
unsigned int currentComponentOffset = timers[timer].componentOffset;
125+
unsigned int nextTimer = removeTimerFromList(timer, currentComponentOffset);
126+
addTimerToList(timer, newComponentOffset);
127+
return nextTimer;
128+
}
129+
130+
/* Trigger a tick of the given component offset, to be called from system timers */
131+
void tick(unsigned int componentOffset) {
132+
//printf("Tick for %d\n", componentOffset);
133+
134+
/* Iterate this list, decrementing the timer components */
135+
unsigned int timerIterator = timerListHead[componentOffset];
136+
137+
while (timerIterator != UINT_MAX) {
138+
//printf("Iterating timer %d\n", timerIterator);
139+
140+
/* This timer needs to move to a higher precision list, or trigger */
141+
if (!--timers[timerIterator].components[componentOffset]) {
142+
143+
/* Seek to next non-null component or the 0th component */
144+
unsigned int nextComponentOffsetForTimer = componentOffset;
145+
while (nextComponentOffsetForTimer && timers[timerIterator].components[nextComponentOffsetForTimer] == 0) {
146+
nextComponentOffsetForTimer--;
147+
}
148+
149+
/* Here we either have a new list to join or we trigger the timer here and now */
150+
if (timers[timerIterator].components[nextComponentOffsetForTimer]) {
151+
/* This should return the next timerIterator */
152+
timerIterator = moveTimerToList(timerIterator, nextComponentOffsetForTimer);
153+
} else {
154+
/* This callback must not modify the list (but we can handle that later!) */
155+
timerCallbacks[timerIterator]();
156+
157+
/* If the timer itself is removed in the above callback, below removal will fail */
158+
159+
/* And remove the timer (this should return the next timerIterator) */
160+
timerIterator = removeTimerFromList(timerIterator, componentOffset);
161+
}
162+
} else {
163+
timerIterator = timers[timerIterator].next;
164+
}
165+
}
166+
}
167+
168+
unsigned int setTimeout_(void (*cb)(), unsigned int ms) {
169+
/* Allocate free timer */
170+
unsigned int timer = allocateTimer();
171+
172+
/* Divide given ms in components */
173+
unsigned int biggestSetComponent = 0;
174+
timers[timer].overshootMs = divideComponents(timers[timer].components, &biggestSetComponent, ms);
175+
176+
//printf("Overshoot by %u ms\n", timers[timer].overshootMs);
177+
//printf("Biggest set component is %u\n", biggestSetComponent);
178+
179+
/* Add the timer to the list of the highest component */
180+
addTimerToList(timer, biggestSetComponent);
181+
182+
/* Set the callback */
183+
timerCallbacks[timer] = cb;
184+
185+
return timer;
186+
}
187+
188+
void clearTimeout_(unsigned int timer) {
189+
/* Unlink the timer from its list */
190+
removeTimerFromList(timer, timers[timer].componentOffset);
191+
192+
/* Put the timer into free timer circle buffer */
193+
freeTimer(timer);
194+
}

src/addon.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,34 @@ void uWS_getParts(const FunctionCallbackInfo<Value> &args) {
109109
/* We'll return undefined on error */
110110
}
111111

112+
/* Faster setTimeout, clearTimeout */
113+
114+
#include "FastTimers.h"
115+
116+
UniquePersistent<Function> timerCallbacksJS[1000];
117+
118+
void uWS_setTimeout(const FunctionCallbackInfo<Value> &args) {
119+
120+
/* Function, integer */
121+
122+
unsigned int timer = setTimeout_(nullptr, 1000);
123+
124+
timerCallbacksJS[timer].Reset(args.GetIsolate(), Local<Function>::Cast(args[0]));
125+
126+
args.GetReturnValue().Set(Integer::New(args.GetIsolate(), timer));
127+
}
128+
129+
void uWS_clearTimeout(const FunctionCallbackInfo<Value> &args) {
130+
131+
/* Integer */
132+
133+
uint32_t timer = Local<Integer>::Cast(args[0])->Value();
134+
135+
clearTimeout_(timer);
136+
137+
timerCallbacksJS[timer].Reset();
138+
}
139+
112140
/* Pass various undocumented configs */
113141
void uWS_cfg(const FunctionCallbackInfo<Value> &args) {
114142
NativeString key(args.GetIsolate(), args[0]);
@@ -372,6 +400,9 @@ PerContextData *Main(Local<Object> exports) {
372400
exports->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "deleteStringCollection", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_deleteStringCollection)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()).ToChecked();
373401
exports->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "deleteIntegerCollection", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_deleteIntegerCollection)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()).ToChecked();
374402

403+
exports->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "setTimeout", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_setTimeout)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()).ToChecked();
404+
exports->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "clearTimeout", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_clearTimeout)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()).ToChecked();
405+
375406
exports->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "_cfg", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_cfg)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()).ToChecked();
376407
exports->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "getParts", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_getParts)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()).ToChecked();
377408

0 commit comments

Comments
 (0)