#include <stdio.h>
#include <stdlib.h>
+#include <strings.h>
#include <string.h>
#include <limits.h> /* PATH_MAX */
#include <assert.h>
#include <node_net2.h>
#include <node_events.h>
#include <node_cares.h>
-#include <node_net.h>
#include <node_file.h>
#if 0
// not in use
# include <node_idle_watcher.h>
#endif
-#include <node_http.h>
#include <node_http_parser.h>
#include <node_signal_watcher.h>
#include <node_stat_watcher.h>
// 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();
- }
-}
-
-
-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);
+ tick_times[tick_time_head] = ev_now(EV_DEFAULT_UC);
+ tick_time_head = (tick_time_head + 1) % RPM_SAMPLES;
- // Don't count GC watchers as activity.
+ StartGCTimer();
- if (ev_is_pending(&gc_timer)) pending--;
- if (ev_is_pending(&gc_idle)) pending--;
- if (ev_is_pending(&gc_check)) pending--;
-
- 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);
}
#ifdef EAGAIN
ERRNO_CASE(EAGAIN);
-#else
-# ifdef EWOULDBLOCK
+#endif
+
+#ifdef EWOULDBLOCK
+# if EAGAIN != EWOULDBLOCK
ERRNO_CASE(EWOULDBLOCK);
# endif
#endif
ERRNO_CASE(ENOEXEC);
#endif
-#ifdef ENOLCK
- ERRNO_CASE(ENOLCK);
-#endif
-
#ifdef ENOLINK
ERRNO_CASE(ENOLINK);
#endif
+#ifdef ENOLCK
+# if ENOLINK != ENOLCK
+ ERRNO_CASE(ENOLCK);
+# endif
+#endif
+
#ifdef ENOMEM
ERRNO_CASE(ENOMEM);
#endif
}
}
+const char *signo_string(int signo) {
+#define SIGNO_CASE(e) case e: return #e;
+ switch (signo) {
+
+#ifdef SIGHUP
+ SIGNO_CASE(SIGHUP);
+#endif
+
+#ifdef SIGINT
+ SIGNO_CASE(SIGINT);
+#endif
+
+#ifdef SIGQUIT
+ SIGNO_CASE(SIGQUIT);
+#endif
+
+#ifdef SIGILL
+ SIGNO_CASE(SIGILL);
+#endif
+
+#ifdef SIGTRAP
+ SIGNO_CASE(SIGTRAP);
+#endif
+
+#ifdef SIGABRT
+ SIGNO_CASE(SIGABRT);
+#endif
+
+#ifdef SIGIOT
+# if SIGABRT != SIGIOT
+ SIGNO_CASE(SIGIOT);
+# endif
+#endif
+
+#ifdef SIGBUS
+ SIGNO_CASE(SIGBUS);
+#endif
+
+#ifdef SIGFPE
+ SIGNO_CASE(SIGFPE);
+#endif
+
+#ifdef SIGKILL
+ SIGNO_CASE(SIGKILL);
+#endif
+
+#ifdef SIGUSR1
+ SIGNO_CASE(SIGUSR1);
+#endif
+
+#ifdef SIGSEGV
+ SIGNO_CASE(SIGSEGV);
+#endif
+
+#ifdef SIGUSR2
+ SIGNO_CASE(SIGUSR2);
+#endif
+
+#ifdef SIGPIPE
+ SIGNO_CASE(SIGPIPE);
+#endif
+
+#ifdef SIGALRM
+ SIGNO_CASE(SIGALRM);
+#endif
+
+ SIGNO_CASE(SIGTERM);
+ SIGNO_CASE(SIGCHLD);
+
+#ifdef SIGSTKFLT
+ SIGNO_CASE(SIGSTKFLT);
+#endif
+
+
+#ifdef SIGCONT
+ SIGNO_CASE(SIGCONT);
+#endif
+
+#ifdef SIGSTOP
+ SIGNO_CASE(SIGSTOP);
+#endif
+
+#ifdef SIGTSTP
+ SIGNO_CASE(SIGTSTP);
+#endif
+
+#ifdef SIGTTIN
+ SIGNO_CASE(SIGTTIN);
+#endif
+
+#ifdef SIGTTOU
+ SIGNO_CASE(SIGTTOU);
+#endif
+
+#ifdef SIGURG
+ SIGNO_CASE(SIGURG);
+#endif
+
+#ifdef SIGXCPU
+ SIGNO_CASE(SIGXCPU);
+#endif
+
+#ifdef SIGXFSZ
+ SIGNO_CASE(SIGXFSZ);
+#endif
+
+#ifdef SIGVTALRM
+ SIGNO_CASE(SIGVTALRM);
+#endif
+
+#ifdef SIGPROF
+ SIGNO_CASE(SIGPROF);
+#endif
+
+#ifdef SIGWINCH
+ SIGNO_CASE(SIGWINCH);
+#endif
+
+#ifdef SIGIO
+ SIGNO_CASE(SIGIO);
+#endif
+
+#ifdef SIGPOLL
+# if SIGPOLL != SIGIO
+ SIGNO_CASE(SIGPOLL);
+# endif
+#endif
+
+#ifdef SIGLOST
+ SIGNO_CASE(SIGLOST);
+#endif
+
+#ifdef SIGPWR
+ SIGNO_CASE(SIGPWR);
+#endif
+
+#ifdef SIGSYS
+ SIGNO_CASE(SIGSYS);
+#endif
+
+ default: return "";
+ }
+}
+
Local<Value> ErrnoException(int errorno,
const char *syscall,
stats->Set(rdev_symbol, Integer::New(s->st_rdev));
/* total size, in bytes */
- stats->Set(size_symbol, Integer::New(s->st_size));
+ stats->Set(size_symbol, Number::New(s->st_size));
/* blocksize for filesystem I/O */
stats->Set(blksize_symbol, Integer::New(s->st_blksize));
return *value ? *value : "<str conversion failed>";
}
-static void ReportException(TryCatch &try_catch, bool show_line = false) {
+static void ReportException(TryCatch &try_catch, bool show_line) {
Handle<Message> message = try_catch.Message();
Handle<Value> error = try_catch.Exception();
// Print line of source code.
String::Utf8Value sourceline(message->GetSourceLine());
const char* sourceline_string = ToCString(sourceline);
- fprintf(stderr, "%s\n", sourceline_string);
+
+ // HACK HACK HACK
+ //
+ // FIXME
+ //
+ // Because of how CommonJS modules work, all scripts are wrapped with a
+ // "function (function (exports, __filename, ...) {"
+ // to provide script local variables.
+ //
+ // When reporting errors on the first line of a script, this wrapper
+ // function is leaked to the user. This HACK is to remove it. The length
+ // of the wrapper is 62. That wrapper is defined in lib/module.js
+ //
+ // If that wrapper is ever changed, then this number also has to be
+ // updated. Or - someone could clean this up so that the two peices
+ // don't need to be changed.
+ //
+ // Even better would be to get support into V8 for wrappers that
+ // shouldn't be reported to users.
+ int offset = linenum == 1 ? 62 : 0;
+
+ fprintf(stderr, "%s\n", sourceline_string + offset);
// Print wavy underline (GetUnderline is deprecated).
int start = message->GetStartColumn();
- for (int i = 0; i < start; i++) {
+ for (int i = offset; i < start; i++) {
fprintf(stderr, " ");
}
int end = message->GetEndColumn();
Local<v8::Script> script = v8::Script::Compile(source, filename);
if (script.IsEmpty()) {
- ReportException(try_catch);
+ ReportException(try_catch, true);
exit(1);
}
Local<Value> result = script->Run();
if (result.IsEmpty()) {
- ReportException(try_catch);
+ ReportException(try_catch, true);
exit(1);
}
}
#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(EV_DEFAULT_UC) - 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);
}
Local<Value> result = script->Run();
- if (try_catch.HasCaught()) return try_catch.ReThrow();
+ if (try_catch.HasCaught()) {
+ ReportException(try_catch, false);
+ exit(1);
+ }
return scope.Close(result);
}
// Check if uncaught_exception_counter indicates a recursion
if (uncaught_exception_counter > 0) {
- ReportException(try_catch);
+ ReportException(try_catch, true);
exit(1);
}
uint32_t length = listener_array->Length();
// Report and exit if process has no "uncaughtException" listener
if (length == 0) {
- ReportException(try_catch);
+ ReportException(try_catch, true);
exit(1);
}
binding_cache->Set(module, exports);
}
- } else if (!strcmp(*module_v, "http")) {
- if (binding_cache->Has(module)) {
- exports = binding_cache->Get(module)->ToObject();
- } else {
- // Warning: When calling requireBinding('http') from javascript then
- // be sure that you call requireBinding('tcp') before it.
- assert(binding_cache->Has(String::New("tcp")));
- exports = Object::New();
- HTTPServer::Initialize(exports);
- HTTPConnection::Initialize(exports);
- binding_cache->Set(module, exports);
- }
-
- } else if (!strcmp(*module_v, "tcp")) {
- if (binding_cache->Has(module)) {
- exports = binding_cache->Get(module)->ToObject();
- } else {
- exports = Object::New();
- Server::Initialize(exports);
- Connection::Initialize(exports);
- binding_cache->Set(module, exports);
- }
-
} else if (!strcmp(*module_v, "cares")) {
if (binding_cache->Has(module)) {
exports = binding_cache->Get(module)->ToObject();
exports->Set(String::New("freelist"), String::New(native_freelist));
exports->Set(String::New("fs"), String::New(native_fs));
exports->Set(String::New("http"), String::New(native_http));
- exports->Set(String::New("http_old"), String::New(native_http_old));
exports->Set(String::New("crypto"), String::New(native_crypto));
exports->Set(String::New("ini"), String::New(native_ini));
exports->Set(String::New("mjsunit"), String::New(native_mjsunit));
exports->Set(String::New("repl"), String::New(native_repl));
exports->Set(String::New("sys"), String::New(native_sys));
exports->Set(String::New("tcp"), String::New(native_tcp));
- exports->Set(String::New("tcp_old"), String::New(native_tcp_old));
exports->Set(String::New("uri"), String::New(native_uri));
exports->Set(String::New("url"), String::New(native_url));
exports->Set(String::New("utils"), String::New(native_utils));
// The node.js file returns a function 'f'
-#ifndef NDEBUG
TryCatch try_catch;
-#endif
Local<Value> f_value = ExecuteString(String::New(native_node),
String::New("node.js"));
-#ifndef NDEBUG
if (try_catch.HasCaught()) {
- ReportException(try_catch);
+ ReportException(try_catch, true);
exit(10);
}
-#endif
assert(f_value->IsFunction());
Local<Function> f = Local<Function>::Cast(f_value);
f->Call(global, 1, args);
-#ifndef NDEBUG
if (try_catch.HasCaught()) {
- ReportException(try_catch);
+ ReportException(try_catch, true);
exit(11);
}
-#endif
}
static void PrintHelp();
return 1;
}
- // Ignore the SIGPIPE
- evcom_ignore_sigpipe();
+
+ // Ignore SIGPIPE
+ struct sigaction sa;
+ bzero(&sa, sizeof(sa));
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, NULL);
+
// Initialize the default ev loop.
#ifdef __sun
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