From 2c7cbbc1b08a1c5bb8ccee3fcb6dc0145d6f1a70 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 12 Mar 2010 13:39:54 -0800 Subject: [PATCH 1/2] Optimize event emitter for single listener --- src/node.js | 56 +++++++++++++++++++++++++++++++++------------- src/node_events.cc | 35 ++++++++++++++++++++++------- 2 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/node.js b/src/node.js index aea3d1bfe3..7b952fd53e 100644 --- a/src/node.js +++ b/src/node.js @@ -170,37 +170,63 @@ var eventsModule = createInternalModule('events', function (exports) { // process.EventEmitter is defined in src/events.cc // process.EventEmitter.prototype.emit() is also defined there. process.EventEmitter.prototype.addListener = function (type, listener) { - if (listener instanceof Function) { - if (!this._events) this._events = {}; - if (!this._events.hasOwnProperty(type)) this._events[type] = []; - // To avoid recursion in the case that type == "newListeners"! Before - // adding it to the listeners, first emit "newListeners". - this.emit("newListener", type, listener); + if (!(listener instanceof Function)) { + throw new Error('addListener only takes instances of Function'); + } + + if (!this._events) this._events = {}; + + // To avoid recursion in the case that type == "newListeners"! Before + // adding it to the listeners, first emit "newListeners". + this.emit("newListener", type, listener); + + if (!this._events[type]) { + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + } else if (this._events[type] instanceof Array) { + // If we've already got an array, just append. this._events[type].push(listener); + } else { + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; } + return this; }; process.EventEmitter.prototype.removeListener = function (type, listener) { - if (listener instanceof Function) { - // does not use listeners(), so no side effect of creating _events[type] - if (!this._events || !this._events.hasOwnProperty(type)) return; - var list = this._events[type]; - if (list.indexOf(listener) < 0) return; - list.splice(list.indexOf(listener), 1); + if (!(listener instanceof Function)) { + throw new Error('removeListener only takes instances of Function'); } + + // does not use listeners(), so no side effect of creating _events[type] + if (!this._events || !this._events[type]) return this; + + var list = this._events[type]; + + if (list instanceof Array) { + var i = list.indexOf(listener); + if (i < 0) return this; + list.splice(i, 1); + } else { + this._events[type] = null; + } + return this; }; process.EventEmitter.prototype.removeAllListeners = function (type) { // does not use listeners(), so no side effect of creating _events[type] - if (!type || !this._events || !this._events.hasOwnProperty(type)) return this; - this._events[type].length = 0; + if (!type || !this._events || !this._events[type]) return this; + this._events[type] = null; }; process.EventEmitter.prototype.listeners = function (type) { if (!this._events) this._events = {}; - if (!this._events.hasOwnProperty(type)) this._events[type] = []; + if (!this._events[type]) this._events[type] = []; + if (!(this._events[type] instanceof Array)) { + this._events[type] = [this._events[type]]; + } return this._events[type]; }; diff --git a/src/node_events.cc b/src/node_events.cc index 27d07a46be..9630aa6d9a 100644 --- a/src/node_events.cc +++ b/src/node_events.cc @@ -50,15 +50,11 @@ static bool ReallyEmit(Handle self, Local events = events_v->ToObject(); Local listeners_v = events->Get(event); - if (!listeners_v->IsArray()) return false; - Local listeners = Local::Cast(listeners_v); + Local listener; - for (unsigned int i = 0; i < listeners->Length(); i++) { - HandleScope scope; - - Local listener_v = listeners->Get(Integer::New(i)); - if (!listener_v->IsFunction()) continue; - Local listener = Local::Cast(listener_v); + if (listeners_v->IsFunction()) { + // Optimized one-listener case + Local listener = Local::Cast(listeners_v); TryCatch try_catch; @@ -68,6 +64,29 @@ static bool ReallyEmit(Handle self, FatalException(try_catch); return false; } + + } else if (listeners_v->IsArray()) { + Local listeners = Local::Cast(listeners_v); + + for (unsigned int i = 0; i < listeners->Length(); i++) { + HandleScope scope; + + Local listener_v = listeners->Get(Integer::New(i)); + if (!listener_v->IsFunction()) continue; + Local listener = Local::Cast(listener_v); + + TryCatch try_catch; + + listener->Call(self, argc, argv); + + if (try_catch.HasCaught()) { + FatalException(try_catch); + return false; + } + } + + } else { + return false; } return true; From d7efb0fdb2b269f3fa16a26c0690f3f41c1dfe65 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 12 Mar 2010 13:45:49 -0800 Subject: [PATCH 2/2] Factor out TryCatch in ReallyEmit --- src/node_events.cc | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/node_events.cc b/src/node_events.cc index 9630aa6d9a..d88229fe8a 100644 --- a/src/node_events.cc +++ b/src/node_events.cc @@ -43,8 +43,8 @@ static bool ReallyEmit(Handle self, Handle event, int argc, Handle argv[]) { - HandleScope scope; - + // HandleScope not needed here because only called from one of the two + // functions below Local events_v = self->Get(events_symbol); if (!events_v->IsObject()) return false; Local events = events_v->ToObject(); @@ -52,12 +52,12 @@ static bool ReallyEmit(Handle self, Local listeners_v = events->Get(event); Local listener; + TryCatch try_catch; + if (listeners_v->IsFunction()) { // Optimized one-listener case Local listener = Local::Cast(listeners_v); - TryCatch try_catch; - listener->Call(self, argc, argv); if (try_catch.HasCaught()) { @@ -68,15 +68,11 @@ static bool ReallyEmit(Handle self, } else if (listeners_v->IsArray()) { Local listeners = Local::Cast(listeners_v); - for (unsigned int i = 0; i < listeners->Length(); i++) { - HandleScope scope; - - Local listener_v = listeners->Get(Integer::New(i)); + for (uint32_t i = 0; i < listeners->Length(); i++) { + Local listener_v = listeners->Get(i); if (!listener_v->IsFunction()) continue; Local listener = Local::Cast(listener_v); - TryCatch try_catch; - listener->Call(self, argc, argv); if (try_catch.HasCaught()) {