address issue #41, replace process.nextTick(check) because that causes a...#42
address issue #41, replace process.nextTick(check) because that causes a...#42seanjsong wants to merge 1 commit intocreationix:masterfrom
Conversation
…that causes a race condition
|
Hi seansoong, Your fix will not work if a step creates more than one group(), because all such groups will share the same return value array and the group items will write over eachothers' results. It is sufficient to add the following boolean to fix the race condition in group completion: I'm having trouble coming up with a test that reproduces the race, but I have real code that triggers the problem - on node v0.8.12 but not in v0.6.15 (presumably the exact timing of the fs callbacks differs between versions.) The race only seems to show up in non-trivial examples, perhaps when you have more than one group() or parallel() in the same step, but basically what happens is: if all the callbacks created by a group are called back before process.nextTick runs, check() will find pending === 0 both times, and it calls localCallback twice. Since group() is built on parallel() this will decrement the step's pending count an extra time and cause the next step to be run too early with some arguments missing (the missing arguments are the other groups or parallels that have not completed yet.) I created a branch and added lots of debugging checks for accidental misuse of step callbacks, which is how I found this problem. |
|
Here is a test case that reproduces the issue on node v0.8.12: The expectation fails to be met because one of the groups yields undefined instead of the expected result [3]. With defensive checks added to the code in my fork, and the group_done fix reverted, I see this output (I also commented out the throw on my checks so it would run to completion): It seems that Step assumes the process.nextTick callback will be run before any callbacks that are scheduled "later" in program order, but node doesn't make this promise; indeed this issue is topical ;) |
|
Hi raffecat, You are right. I didn't use multiple group within one step myself so I overlooked this possibility. I believe this flag thing works. The reason it didn't come to my mind, I suppose, is the fear of introducing yet another flag that is hard to explain to others. Anyway, I was persuaded by the author to try the upgrade version of step. I recommend you review and test it :) |
Hi there,
When I reported issue #41 this morning, I promised to provide a fix. Here it is.
In order to check synchronously I have to move the whole bunch of local vars from
next.groupto outer scope. Hence the readability is slightly compromised.Anyway, this fix passed my counter-example and all of the original tests. And I tried not to change the semantics of this lib.