From 3126abfe11596dfd5aa587af020cc3a6ed16837c Mon Sep 17 00:00:00 2001 From: Alexander Fritze Date: Mon, 9 Jul 2018 16:13:10 -0500 Subject: [PATCH] event::Emitter: fix reentrant retraction edgecase --- modules/event.sjs | 13 ++++++++++++- test/unit/event-tests.sjs | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/modules/event.sjs b/modules/event.sjs index f6def80d..d8fc2344 100644 --- a/modules/event.sjs +++ b/modules/event.sjs @@ -100,7 +100,18 @@ function Emitter() { listeners.push(resume); } retract { - __js listeners.splice(listeners.indexOf(resume), 1); + __js { + // the listeners array gets exchanged from 'emit', + // so if this is a concurrent retract, we must take care not to try + // to accidentally call splice(-1,1) on listeners when our listener isn't found. + // i.e., we must not do: + // listeners.splice(listeners.indexOf(resume), 1); + // see also event-tests.sjs:concurrent retract edgecase + var i = listeners.indexOf(resume); + if (i >= 0) + listeners.splice(i, 1); + } // __js + } receiver(val); } diff --git a/test/unit/event-tests.sjs b/test/unit/event-tests.sjs index a4ef5d32..38076f6f 100644 --- a/test/unit/event-tests.sjs +++ b/test/unit/event-tests.sjs @@ -441,3 +441,42 @@ context('wait()') {|| rv .. assert.eq(2); } } + +test('concurrent retract edgecase') {|| + var rv = ''; + + var E = event.Emitter(); + var R; + + waitfor { + E .. event.wait(); + waitfor { + E .. event.wait(); // this listener used to be erroneously removed by the listener + // retraction triggered by R(), causing the test to hang forever + rv += 'C'; + } + and { + R(); // this call will abort the event listener in the clause below + rv += 'B'; + } + + rv += 'D'; + } + and { + waitfor() { + R = resume; + // R() will be called before we receive the event, causing the wait() call to be + // aborted + E .. event.wait(); + } + rv += 'A'; + } + and { + hold(0); + E.emit(); + hold(0); + E.emit(); + rv += 'E'; + } + rv .. assert.eq('ABCDE'); +}