|
|
@ -93,88 +93,73 @@ static ev_idle eio_poller; |
|
|
|
// true if the heap hasn't be fully compacted, and needs to be run again.
|
|
|
|
// Returning false means that it doesn't have anymore work to do.
|
|
|
|
//
|
|
|
|
// We try to wait for a period of GC_INTERVAL (2 seconds) of idleness, where
|
|
|
|
// idleness means that no libev watchers have been executed. Since
|
|
|
|
// everything in node uses libev watchers, this is a pretty good measure of
|
|
|
|
// idleness. This is done with gc_check, which records the timestamp
|
|
|
|
// last_active on every tick of the event loop, and with gc_timer which
|
|
|
|
// executes every few seconds to measure if
|
|
|
|
// last_active + GC_INTERVAL < ev_now()
|
|
|
|
// If we do find a period of idleness, then we start the gc_idle timer which
|
|
|
|
// will very repaidly call IdleNotification until the heap is fully
|
|
|
|
// compacted.
|
|
|
|
static ev_tstamp last_active; |
|
|
|
static ev_timer gc_timer; |
|
|
|
// A rather convoluted algorithm has been devised to determine when Node is
|
|
|
|
// idle. You'll have to figure it out for yourself.
|
|
|
|
static ev_check gc_check; |
|
|
|
static ev_idle gc_idle; |
|
|
|
#define GC_INTERVAL 1.0 |
|
|
|
static ev_timer gc_timer; |
|
|
|
bool need_gc; |
|
|
|
|
|
|
|
|
|
|
|
static void gc_timer_start () { |
|
|
|
#define FAST_TICK 0.7 |
|
|
|
#define GC_WAIT_TIME 5. |
|
|
|
#define RPM_SAMPLES 100 |
|
|
|
#define TICK_TIME(n) tick_times[(tick_time_head - (n)) % RPM_SAMPLES] |
|
|
|
static ev_tstamp tick_times[RPM_SAMPLES]; |
|
|
|
static int tick_time_head; |
|
|
|
|
|
|
|
static void StartGCTimer () { |
|
|
|
if (!ev_is_active(&gc_timer)) { |
|
|
|
ev_timer_start(EV_DEFAULT_UC_ &gc_timer); |
|
|
|
ev_unref(EV_DEFAULT_UC); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void gc_timer_stop () { |
|
|
|
static void StopGCTimer () { |
|
|
|
if (ev_is_active(&gc_timer)) { |
|
|
|
ev_ref(EV_DEFAULT_UC); |
|
|
|
ev_timer_stop(EV_DEFAULT_UC_ &gc_timer); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void CheckIdleness(EV_P_ ev_timer *watcher, int revents) { |
|
|
|
assert(watcher == &gc_timer); |
|
|
|
assert(revents == EV_TIMER); |
|
|
|
|
|
|
|
//fprintf(stderr, "check idle\n");
|
|
|
|
|
|
|
|
ev_tstamp idle_time = ev_now(EV_DEFAULT_UC) - last_active; |
|
|
|
|
|
|
|
if (idle_time > GC_INTERVAL) { |
|
|
|
if (!V8::IdleNotification()) { |
|
|
|
ev_idle_start(EV_DEFAULT_UC_ &gc_idle); |
|
|
|
} |
|
|
|
gc_timer_stop(); |
|
|
|
ev_timer_stop(&gc_timer); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void NotifyIdleness(EV_P_ ev_idle *watcher, int revents) { |
|
|
|
static void Idle(EV_P_ ev_idle *watcher, int revents) { |
|
|
|
assert(watcher == &gc_idle); |
|
|
|
assert(revents == EV_IDLE); |
|
|
|
|
|
|
|
//fprintf(stderr, "notify idle\n");
|
|
|
|
//fprintf(stderr, "idle\n");
|
|
|
|
|
|
|
|
if (V8::IdleNotification()) { |
|
|
|
ev_idle_stop(EV_A_ watcher); |
|
|
|
gc_timer_stop(); |
|
|
|
StopGCTimer(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void Activity(EV_P_ ev_check *watcher, int revents) { |
|
|
|
// Called directly after every call to select() (or epoll, or whatever)
|
|
|
|
static void Check(EV_P_ ev_check *watcher, int revents) { |
|
|
|
assert(watcher == &gc_check); |
|
|
|
assert(revents == EV_CHECK); |
|
|
|
|
|
|
|
int pending = ev_pending_count(EV_DEFAULT_UC); |
|
|
|
|
|
|
|
// Don't count GC watchers as activity.
|
|
|
|
tick_times[tick_time_head] = ev_now(); |
|
|
|
tick_time_head = (tick_time_head + 1) % RPM_SAMPLES; |
|
|
|
|
|
|
|
if (ev_is_pending(&gc_timer)) pending--; |
|
|
|
if (ev_is_pending(&gc_idle)) pending--; |
|
|
|
if (ev_is_pending(&gc_check)) pending--; |
|
|
|
StartGCTimer(); |
|
|
|
|
|
|
|
assert(pending >= 0); |
|
|
|
for (int i = 0; i < (int)(GC_WAIT_TIME/FAST_TICK); i++) { |
|
|
|
double d = TICK_TIME(i+1) - TICK_TIME(i+2); |
|
|
|
//printf("d = %f\n", d);
|
|
|
|
// If in the last 5 ticks the difference between
|
|
|
|
// ticks was less than 0.7 seconds, then continue.
|
|
|
|
if (d < FAST_TICK) { |
|
|
|
//printf("---\n");
|
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//fprintf(stderr, "activity, pending: %d\n", pending);
|
|
|
|
// Otherwise start the gc!
|
|
|
|
|
|
|
|
if (pending) { |
|
|
|
last_active = ev_now(EV_DEFAULT_UC); |
|
|
|
ev_idle_stop(EV_DEFAULT_UC_ &gc_idle); |
|
|
|
gc_timer_start(); |
|
|
|
} |
|
|
|
//fprintf(stderr, "start idle 2\n");
|
|
|
|
ev_idle_start(EV_A_ &gc_idle); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1374,6 +1359,34 @@ error: |
|
|
|
} |
|
|
|
#endif // __linux__
|
|
|
|
|
|
|
|
|
|
|
|
static void CheckStatus(EV_P_ ev_timer *watcher, int revents) { |
|
|
|
assert(watcher == &gc_timer); |
|
|
|
assert(revents == EV_TIMER); |
|
|
|
|
|
|
|
#if HAVE_GETMEM |
|
|
|
// check memory
|
|
|
|
size_t rss, vsize; |
|
|
|
if (!ev_is_active(&gc_idle) && getmem(&rss, &vsize) == 0) { |
|
|
|
if (rss > 1024*1024*128) { |
|
|
|
// larger than 128 megs, just start the idle watcher
|
|
|
|
ev_idle_start(EV_A_ &gc_idle); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
#endif // HAVE_GETMEM
|
|
|
|
|
|
|
|
double d = ev_now() - TICK_TIME(3); |
|
|
|
|
|
|
|
//printfb("timer d = %f\n", d);
|
|
|
|
|
|
|
|
if (d >= GC_WAIT_TIME - 1.) { |
|
|
|
//fprintf(stderr, "start idle\n");
|
|
|
|
ev_idle_start(EV_A_ &gc_idle); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
v8::Handle<v8::Value> MemoryUsage(const v8::Arguments& args) { |
|
|
|
HandleScope scope; |
|
|
|
assert(args.Length() == 0); |
|
|
@ -2040,13 +2053,12 @@ int main(int argc, char *argv[]) { |
|
|
|
|
|
|
|
ev_idle_init(&node::tick_spinner, node::Spin); |
|
|
|
|
|
|
|
ev_timer_init(&node::gc_timer, node::CheckIdleness, 2*GC_INTERVAL, 2*GC_INTERVAL); |
|
|
|
|
|
|
|
ev_check_init(&node::gc_check, node::Activity); |
|
|
|
ev_check_init(&node::gc_check, node::Check); |
|
|
|
ev_check_start(EV_DEFAULT_UC_ &node::gc_check); |
|
|
|
ev_unref(EV_DEFAULT_UC); |
|
|
|
|
|
|
|
ev_idle_init(&node::gc_idle, node::NotifyIdleness); |
|
|
|
ev_idle_init(&node::gc_idle, node::Idle); |
|
|
|
ev_timer_init(&node::gc_timer, node::CheckStatus, 5., 5.); |
|
|
|
|
|
|
|
|
|
|
|
// Setup the EIO thread pool
|
|
|
|