Unexpected Re-entrant behavior
Macil opened this issue · comments
Several days of debugging an issue have finally led me to discovering this:
var Bacon = require('baconjs');
var criticalSectionActive = false;
var someBus = new Bacon.Bus();
someBus.log();
Bacon.later(0, [1,2])
.flatMap(Bacon.fromArray)
.onValue(function(x) {
if (criticalSectionActive) {
console.error("Re-entrance!", x);
}
criticalSectionActive = true;
someBus.end();
criticalSectionActive = false;
console.log('got x', x);
});
Output:
<end>
Re-entrance! 2
got x 2
got x 1
Expected output:
<end>
got x 1
got x 2
Using Bacon 0.7.53
Somehow changing the event graph (someBus.end()
) confuses event delivery:
var Bacon = require("./dist/Bacon.js");
var criticalSectionActive = false;
var someBus = new Bacon.Bus();
someBus.log();
Bacon.later(0, [1,2])
.flatMap(Bacon.fromArray)
.onValue(function(x) {
if (criticalSectionActive) {
console.error("Re-entrance!", x);
}
console.log("point a", x);
criticalSectionActive = true;
console.log("point b", x);
someBus.end();
console.log("point c", x);
criticalSectionActive = false;
console.log("point d", x);
console.log('got x', x);
});
point a 1
point b 1
<end>
Re-entrance! 2
point a 2
point b 2
point c 2
point d 2
got x 2
point c 1
point d 1
got x 1
i.e. first we process first value, and in between we process second one.
I'm not sure if this can be fixed without breaking something else. Currently event propagation, side-effects (onValue
and co) and stream-network changes can be interleaved, which causes edge cases like that.
Workaround for now is to un-interleave side-effecting manually (if synchronous delivery isn't important):
var Bacon = require("./dist/Bacon.js");
var criticalSectionActive = false;
var someBus = new Bacon.Bus();
someBus.log();
Bacon.later(0, [1,2])
.flatMap(Bacon.fromArray)
.onValue(function(x) {
setTimeout(function () {
if (criticalSectionActive) {
console.error("Re-entrance!", x);
}
console.log("point a", x);
criticalSectionActive = true;
console.log("point b", x);
someBus.end();
console.log("point c", x);
criticalSectionActive = false;
console.log("point d", x);
console.log('got x', x);
}, 0);
});
point a 1
point b 1
<end>
point c 1
point d 1
got x 1
point a 2
point b 2
point c 2
point d 2
got x 2
Well it is certainly unexpected that ending an unrelated Bus affects the processing order of side-effects. I'll see if this can be avoided. The Bus seems to provided us with a lot of edge cases.
Thanks @agentme for taking the time to produce a test case for us. It helps a lot!