From 1245fb65c2a179b62dcf18623b45027f4deeea0a Mon Sep 17 00:00:00 2001 From: Lloyd Hilaiel Date: Fri, 16 Nov 2012 13:41:50 -0700 Subject: [PATCH] native implementation --- binding.gyp | 12 +++++++++ examples/http.js | 14 ++++++++--- index.js | 61 +++------------------------------------------- toobusy.cc | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 62 deletions(-) create mode 100644 binding.gyp create mode 100644 toobusy.cc diff --git a/binding.gyp b/binding.gyp new file mode 100644 index 0000000..83e219d --- /dev/null +++ b/binding.gyp @@ -0,0 +1,12 @@ +{ + 'targets': [ + { + 'target_name': 'toobusy', + 'include_dirs': [ + ], + 'sources': [ + 'toobusy.cc', + ] + } + ] +} diff --git a/examples/http.js b/examples/http.js index 1ed615c..13db541 100644 --- a/examples/http.js +++ b/examples/http.js @@ -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); diff --git a/index.js b/index.js index b1c1fc9..e71d8fa 100644 --- a/index.js +++ b/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; diff --git a/toobusy.cc b/toobusy.cc new file mode 100644 index 0000000..b29399c --- /dev/null +++ b/toobusy.cc @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include + +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 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 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 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); +};