Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix awaitWriteFinish unlink issue #393

Closed
wants to merge 6 commits into from

Conversation

es128
Copy link
Contributor

@es128 es128 commented Nov 7, 2015

Looks like I finally squished this one.

// @paulmillr @alexpusch @nono

@es128 es128 changed the title Fix await write finish unlink issue Fix awaitWriteFinish unlink issue Nov 7, 2015
this._getWatchedDir(sysPath.dirname(path))
.remove(sysPath.basename(path));
if (this._pendingWrites[path]) {
this._pendingWrites[path].cancelWait();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cancelWait calls callback(), and the the end of this branch calls callback(err). This will surly cause some inconsistencies.
With this code the

if (err.code == 'ENOENT') return;

is not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let's try it. 0274e07

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the uncaught ENOENT is happening in Travis... going to add that back, but higher up

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I don't see a reason for cancelWait to call the callback anyway. It doesn't seem like it would make anything happen.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we have a race condition between the file polling and the unlink event. one of them should cancel every thing about the file - the (maybe) ongoing timeout, and release the pending async function. In my original code I've relayed on that the unlink event will cancel the wait, and if the polling will come first, it will notice that the file is missing (ENOENT) and do nothing.

Have you seen a case where the unlink event does not arrive at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue I observed in the original code that was causing tests to fail regularly was that unlink events were being emitted without any prior add event nor an entry in _pendingWrites. This happened because of the slowness of fs.stat calls, causing misalignment between this._watched tracking and this._pendingWrites (the file shows up in the former, but awf suppressed the corresponding add event from being emitted, then never tracked it because it was gone by the time of the stat).

Hmm, hard to explain.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... So maybe the solution is to add the path to _pendingWrites before calling fs.stat? this way, if an unlink event will arrive, it could cancel the wait successfully. I'll try it now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did try that. Didn't solve it on its own. The last change in this PR seems to be working

@es128
Copy link
Contributor Author

es128 commented Nov 7, 2015

For some reason node 0.10 on Windows is very reluctant to cooperate, but tests are now green everywhere else.

@alexpusch any concerns with the latest diff?

.remove(sysPath.basename(path));
if (this._pendingWrites[path]) this._pendingWrites[path].cancelWait();
if (err.code !== 'ENOENT') callback(err);
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the file is deleted (err.code === 'ENOENT') callback will never be called.

Here is my try for this fix. I've extracted the creation of _pendingWrites[path] outside of the polling function, and returned the task of deleting the entry to the unlink event (via cancelWait). everything is green

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The callback doesn't necessarily need to be called to do what it currently does. It only emits either an error or the event, and sometimes we want neither. I agree that it's ugly though, and as a next step I wanted to look into refactoring further to deal with cleaning up this control flow.

Please open your version in a PR so we can see how it fares on appveyor, then we can determine which direction seems better.

@es128 es128 closed this Nov 7, 2015
@es128 es128 deleted the fix-awaitWriteFinish-unlink-issue branch November 7, 2015 21:23
@es128
Copy link
Contributor Author

es128 commented Nov 7, 2015

Resolved the issue with e7c3369

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants