Skip to content

Commit

Permalink
native implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
lloyd committed Nov 16, 2012
1 parent f690e55 commit 1245fb6
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 62 deletions.
12 changes: 12 additions & 0 deletions binding.gyp
@@ -0,0 +1,12 @@
{
'targets': [
{
'target_name': 'toobusy',
'include_dirs': [
],
'sources': [
'toobusy.cc',
]
}
]
}
14 changes: 10 additions & 4 deletions examples/http.js
Expand Up @@ -7,9 +7,15 @@ http.createServer(function (req, res) {
return res.end("I'm a bit busy right now, come back later\n");
}
// we're not too busy! let's process a request!
var i = 0;
while (i < 1e6) i++;
process.nextTick(function() {
var i = 0;
while (i < 1e6) i++;
process.nextTick(function() {
var j = 0;
while (j < 1e6) j++;

res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('I counted to ' + i + '\n');
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('I counted to ' + i + '\n');
});
});
}).listen(3000, '127.0.0.1', 1024);
61 changes: 3 additions & 58 deletions index.js
@@ -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;
63 changes: 63 additions & 0 deletions toobusy.cc
@@ -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);
};

1 comment on commit 1245fb6

@ConradIrwin
Copy link

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

Please sign in to comment.