From e5cbe73a82cd57d115f9bb29f127c686835c22b3 Mon Sep 17 00:00:00 2001 From: Carson McDonald Date: Thu, 18 Mar 2010 11:50:15 -0400 Subject: [PATCH] Better EventEmitter modify-in-emit Changed ReallyEmit so that it clones the Array of listeners before processing the emit. Added better tests to make sure that modifying listeners inside event handlers doesn't cause later listeners to be skipped or added. --- src/node.js | 2 +- src/node_events.cc | 3 +-- test/simple/test-event-emitter-modify-in-emit.js | 25 ++++++++++++++++++++---- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/node.js b/src/node.js index 127e1aa..0b52966 100644 --- a/src/node.js +++ b/src/node.js @@ -182,7 +182,7 @@ process.mixin = function() { var eventsModule = createInternalModule('events', function (exports) { exports.EventEmitter = process.EventEmitter; - // process.EventEmitter is defined in src/events.cc + // process.EventEmitter is defined in src/node_events.cc // process.EventEmitter.prototype.emit() is also defined there. process.EventEmitter.prototype.addListener = function (type, listener) { if (!(listener instanceof Function)) { diff --git a/src/node_events.cc b/src/node_events.cc index d88229f..4efe5c1 100644 --- a/src/node_events.cc +++ b/src/node_events.cc @@ -50,7 +50,6 @@ static bool ReallyEmit(Handle self, Local events = events_v->ToObject(); Local listeners_v = events->Get(event); - Local listener; TryCatch try_catch; @@ -66,7 +65,7 @@ static bool ReallyEmit(Handle self, } } else if (listeners_v->IsArray()) { - Local listeners = Local::Cast(listeners_v); + Local listeners = Local::Cast(listeners_v->ToObject()->Clone()); for (uint32_t i = 0; i < listeners->Length(); i++) { Local listener_v = listeners->Get(i); diff --git a/test/simple/test-event-emitter-modify-in-emit.js b/test/simple/test-event-emitter-modify-in-emit.js index 1dd94ba..95de547 100644 --- a/test/simple/test-event-emitter-modify-in-emit.js +++ b/test/simple/test-event-emitter-modify-in-emit.js @@ -8,6 +8,7 @@ var e = new events.EventEmitter(); function callback1() { callbacks_called.push("callback1"); e.addListener("foo", callback2); + e.addListener("foo", callback3); e.removeListener("foo", callback1); } @@ -16,23 +17,39 @@ function callback2() { e.removeListener("foo", callback2); } +function callback3() { + callbacks_called.push("callback3"); + e.removeListener("foo", callback3); +} + e.addListener("foo", callback1); assert.equal(1, e.listeners("foo").length); e.emit("foo"); -assert.equal(1, e.listeners("foo").length); +assert.equal(2, e.listeners("foo").length); assert.deepEqual(["callback1"], callbacks_called); e.emit("foo"); assert.equal(0, e.listeners("foo").length); -assert.deepEqual(["callback1", "callback2"], callbacks_called); +assert.deepEqual(["callback1", "callback2", "callback3"], callbacks_called); e.emit("foo"); assert.equal(0, e.listeners("foo").length); -assert.deepEqual(["callback1", "callback2"], callbacks_called); +assert.deepEqual(["callback1", "callback2", "callback3"], callbacks_called); e.addListener("foo", callback1); e.addListener("foo", callback2); assert.equal(2, e.listeners("foo").length) e.removeAllListeners("foo") -assert.equal(0, e.listeners("foo").length) \ No newline at end of file +assert.equal(0, e.listeners("foo").length) + +// Verify that removing callbacks while in emit allows emits to propagate to +// all listeners +callbacks_called = [ ]; + +e.addListener("foo", callback2); +e.addListener("foo", callback3); +assert.equal(2, e.listeners("foo").length) +e.emit("foo"); +assert.deepEqual(["callback2", "callback3"], callbacks_called); +assert.equal(0, e.listeners("foo").length) -- 2.7.4