Skip to content

Commit

Permalink
Add experimental fast timers
Browse files Browse the repository at this point in the history
  • Loading branch information
uNetworkingAB committed Dec 29, 2023
1 parent edd6435 commit 49b2cba
Show file tree
Hide file tree
Showing 2 changed files with 225 additions and 0 deletions.
194 changes: 194 additions & 0 deletions src/FastTimers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>

/* Free timers is a stack of available timer offsets */
unsigned int freeTimersHead = 0;
unsigned int freeTimers[1000000];

/* Every timer is a bunch of components */
unsigned int componentMultiplierMs[5] = {10, 50, 100, 500, 1000};

struct Timer {
/* Offsets in timers array */
unsigned int next, prev;

/* The components remaining for this untriggered run */
unsigned int components[5];

/* We need to track the original Ms in case of repeat timers */
unsigned int nextOriginalMs;

/* Overshoot Ms is used to track accumulated overshoot and to remove smallest 10ms when overshot more than 10ms */
unsigned int overshootMs;

/* What list the timer is in */
unsigned int componentOffset;
};

/* Timers are strides over an integer array */
struct Timer timers[1000000];

/* Timer to registered callback */
void (*timerCallbacks[1000000])();

/* Per component timer doubly linked list (head) */
unsigned int timerListHead[5] = {UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX};

void cb() {
printf("A timer triggered!\n");
}

void init() {
freeTimersHead = 0;
for (int i = 0; i < 1000000; i++) {
freeTimers[i] = i;
}
for (int i = 0; i < 5; i++) {
timerListHead[i] = UINT_MAX;
}
}

unsigned int allocateTimer() {
freeTimersHead++;
//printf("Allocating timer: %d\n", freeTimers[freeTimersHead - 1]);
return freeTimers[freeTimersHead - 1];
}

void freeTimer(unsigned int timer) {
//printf("Freeing timer: %d\n", timer);
freeTimersHead--;
freeTimers[freeTimersHead] = timer;
}

unsigned int divideComponents(unsigned int components[5], unsigned int *biggestSetComponent, unsigned int ms) {
/* Start on fifth component */
int biggestComponent = 4;

while (ms > 0 && biggestComponent >= 0) {
/* Can we divide by this component? */
if (ms >= componentMultiplierMs[biggestComponent]) {
components[biggestComponent] = ms / componentMultiplierMs[biggestComponent];
ms -= components[biggestComponent] * componentMultiplierMs[biggestComponent];

if (*biggestSetComponent < biggestComponent) {
*biggestSetComponent = biggestComponent;
}
}
biggestComponent--;
}

unsigned int overshoot = 0;
if (ms) {
components[0]++;
overshoot = 10 - ms;
}

/* Return the overshoot */
return overshoot;
}

void addTimerToList(unsigned int timer, unsigned int componentOffset) {
unsigned int head = timerListHead[componentOffset];
timers[timer].next = head;
timers[timer].prev = UINT_MAX;
if (head != UINT_MAX) {
timers[head].prev = timer;
}
timerListHead[componentOffset] = timer;

/* Track what list the timer is in */
timers[timer].componentOffset = componentOffset;
}

/* Returns the next timer in the list or UINT_MAX */
unsigned int removeTimerFromList(unsigned int timer, unsigned int componentOffset) {
unsigned int prev = timers[timer].prev;
unsigned int next = timers[timer].next;

if (prev != UINT_MAX) {
timers[prev].next = next;
} else {
timerListHead[componentOffset] = next;
}

if (next != UINT_MAX) {
timers[next].prev = prev;
}

return next;
}

unsigned int moveTimerToList(unsigned int timer, unsigned int newComponentOffset) {
unsigned int currentComponentOffset = timers[timer].componentOffset;
unsigned int nextTimer = removeTimerFromList(timer, currentComponentOffset);
addTimerToList(timer, newComponentOffset);
return nextTimer;
}

/* Trigger a tick of the given component offset, to be called from system timers */
void tick(unsigned int componentOffset) {
//printf("Tick for %d\n", componentOffset);

/* Iterate this list, decrementing the timer components */
unsigned int timerIterator = timerListHead[componentOffset];

while (timerIterator != UINT_MAX) {
//printf("Iterating timer %d\n", timerIterator);

/* This timer needs to move to a higher precision list, or trigger */
if (!--timers[timerIterator].components[componentOffset]) {

/* Seek to next non-null component or the 0th component */
unsigned int nextComponentOffsetForTimer = componentOffset;
while (nextComponentOffsetForTimer && timers[timerIterator].components[nextComponentOffsetForTimer] == 0) {
nextComponentOffsetForTimer--;
}

/* Here we either have a new list to join or we trigger the timer here and now */
if (timers[timerIterator].components[nextComponentOffsetForTimer]) {
/* This should return the next timerIterator */
timerIterator = moveTimerToList(timerIterator, nextComponentOffsetForTimer);
} else {
/* This callback must not modify the list (but we can handle that later!) */
timerCallbacks[timerIterator]();

/* If the timer itself is removed in the above callback, below removal will fail */

/* And remove the timer (this should return the next timerIterator) */
timerIterator = removeTimerFromList(timerIterator, componentOffset);
}
} else {
timerIterator = timers[timerIterator].next;
}
}
}

unsigned int setTimeout_(void (*cb)(), unsigned int ms) {
/* Allocate free timer */
unsigned int timer = allocateTimer();

/* Divide given ms in components */
unsigned int biggestSetComponent = 0;
timers[timer].overshootMs = divideComponents(timers[timer].components, &biggestSetComponent, ms);

//printf("Overshoot by %u ms\n", timers[timer].overshootMs);
//printf("Biggest set component is %u\n", biggestSetComponent);

/* Add the timer to the list of the highest component */
addTimerToList(timer, biggestSetComponent);

/* Set the callback */
timerCallbacks[timer] = cb;

return timer;
}

void clearTimeout_(unsigned int timer) {
/* Unlink the timer from its list */
removeTimerFromList(timer, timers[timer].componentOffset);

/* Put the timer into free timer circle buffer */
freeTimer(timer);
}
31 changes: 31 additions & 0 deletions src/addon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,34 @@ void uWS_getParts(const FunctionCallbackInfo<Value> &args) {
/* We'll return undefined on error */
}

/* Faster setTimeout, clearTimeout */

#include "FastTimers.h"

UniquePersistent<Function> timerCallbacksJS[1000];

void uWS_setTimeout(const FunctionCallbackInfo<Value> &args) {

/* Function, integer */

unsigned int timer = setTimeout_(nullptr, 1000);

timerCallbacksJS[timer].Reset(args.GetIsolate(), Local<Function>::Cast(args[0]));

args.GetReturnValue().Set(Integer::New(args.GetIsolate(), timer));
}

void uWS_clearTimeout(const FunctionCallbackInfo<Value> &args) {

/* Integer */

uint32_t timer = Local<Integer>::Cast(args[0])->Value();

clearTimeout_(timer);

timerCallbacksJS[timer].Reset();
}

/* Pass various undocumented configs */
void uWS_cfg(const FunctionCallbackInfo<Value> &args) {
NativeString key(args.GetIsolate(), args[0]);
Expand Down Expand Up @@ -372,6 +400,9 @@ PerContextData *Main(Local<Object> exports) {
exports->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "deleteStringCollection", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_deleteStringCollection)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()).ToChecked();
exports->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "deleteIntegerCollection", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_deleteIntegerCollection)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()).ToChecked();

exports->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "setTimeout", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_setTimeout)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()).ToChecked();
exports->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "clearTimeout", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_clearTimeout)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()).ToChecked();

exports->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "_cfg", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_cfg)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()).ToChecked();
exports->Set(isolate->GetCurrentContext(), String::NewFromUtf8(isolate, "getParts", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_getParts)->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()).ToChecked();

Expand Down

0 comments on commit 49b2cba

Please sign in to comment.