Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
88 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
'targets': [ | ||
{ | ||
'target_name': 'toobusy', | ||
'include_dirs': [ | ||
], | ||
'sources': [ | ||
'toobusy.cc', | ||
] | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,58 +1,3 @@ | ||
const events = require('events'); | ||
|
||
// constants | ||
const eventsEmitted = [ 'busy', 'normal' ]; | ||
const POLL_PERIOD_MS = 500; | ||
var HIGH_WATER_MARK_MS = 100; | ||
|
||
// state | ||
var polling = false; | ||
var timer = null; | ||
|
||
// api | ||
module.exports.stop = function() { | ||
stopPolling(); | ||
}; | ||
|
||
// implementation | ||
function startPolling() { | ||
if (polling) return; | ||
polling = true; | ||
checkState(); | ||
} | ||
|
||
function stopPolling() { | ||
if (!polling) return; | ||
clearTimeout(timer); | ||
timer = null; | ||
polling = false; | ||
} | ||
|
||
var lastMark = null; | ||
var lagging = false; | ||
function checkState() { | ||
if (!polling) return; | ||
if (lastMark) { | ||
var lag = new Date() - lastMark - POLL_PERIOD_MS; | ||
|
||
// have we transitioned across the high water mark? | ||
if (!lagging && lag > HIGH_WATER_MARK_MS) { | ||
lagging = lag; | ||
} else if (lagging && lag < HIGH_WATER_MARK_MS) { | ||
lagging = null; | ||
} | ||
} | ||
lastMark = new Date(); | ||
timer = setTimeout(checkState, POLL_PERIOD_MS); | ||
} | ||
|
||
module.exports = function() { | ||
startPolling(); | ||
if (lagging) return lagging; | ||
var curLag = new Date() - lastMark - POLL_PERIOD_MS; | ||
if (lastMark && curLag > HIGH_WATER_MARK_MS) | ||
return curLag; | ||
return null; | ||
}; | ||
|
||
module.exports.shutdown = stopPolling; | ||
var bindings = require('bindings')('toobusy.node') | ||
module.exports = bindings.toobusy; | ||
module.exports.shutdown = bindings.shutdown; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
#include <v8.h> | ||
#include <node.h> | ||
#include <uv.h> | ||
#include <stdlib.h> | ||
#include <sys/time.h> | ||
|
||
using namespace v8; | ||
|
||
static const unsigned int POLL_PERIOD_MS = 500; | ||
static const unsigned int HIGH_WATER_MARK_MS = 50; | ||
static const unsigned int AVG_DECAY_FACTOR = 3; | ||
|
||
//static uv_idle_t s_idler; | ||
static uv_timer_t s_timer; | ||
static uint64_t s_currentLag; | ||
static uint64_t s_lastMark; | ||
static uint64_t s_avgCalls; | ||
static uint64_t s_calls; | ||
|
||
Handle<Value> TooBusy(const Arguments& args) { | ||
bool block = false; | ||
if (s_currentLag > HIGH_WATER_MARK_MS) { | ||
// probabilistically block 2x as many requests as we would need | ||
// to in order to catch up. | ||
double pctToBlock = ((s_currentLag - HIGH_WATER_MARK_MS) / | ||
(double) HIGH_WATER_MARK_MS) * 100.0; | ||
double r = (rand() / (double) RAND_MAX) * 100.0; | ||
if (r < pctToBlock) block = true; | ||
} | ||
s_calls++; | ||
return Boolean::New(block); | ||
} | ||
|
||
Handle<Value> ShutDown(const Arguments& args) { | ||
uv_timer_stop(&s_timer); | ||
return Undefined(); | ||
} | ||
|
||
static void every_second(uv_timer_t* handle, int status) | ||
{ | ||
uint64_t now = uv_hrtime(); | ||
|
||
s_avgCalls = (s_calls + (s_avgCalls * (AVG_DECAY_FACTOR-1))) / | ||
AVG_DECAY_FACTOR; | ||
s_calls = 0; | ||
|
||
if (s_lastMark > 0) { | ||
uint64_t lag = ((now - s_lastMark) / 1000000); | ||
lag = (lag < POLL_PERIOD_MS) ? 0 : lag - POLL_PERIOD_MS; | ||
s_currentLag = (lag + (s_currentLag * (AVG_DECAY_FACTOR-1))) / | ||
AVG_DECAY_FACTOR; | ||
} | ||
s_lastMark = now; | ||
}; | ||
|
||
extern "C" void init(Handle<Object> target) { | ||
HandleScope scope; | ||
|
||
target->Set(String::New("toobusy"), FunctionTemplate::New(TooBusy)->GetFunction()); | ||
target->Set(String::New("shutdown"), FunctionTemplate::New(ShutDown)->GetFunction()); | ||
uv_timer_init(uv_default_loop(), &s_timer); | ||
uv_timer_start(&s_timer, every_second, POLL_PERIOD_MS, POLL_PERIOD_MS); | ||
}; |
1245fb6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lloyd I'm curious as to why you made this native? I'm considering making something similar for ruby's eventmachine (a similar event loop), and wonder if there's a gotcha that's likely to bite me too.
Thanks for giving me the idea in the first place :).
Conrad