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

[C#] Support concurrent device dispose and async commit #423

Merged
merged 3 commits into from
Mar 9, 2021

Conversation

badrishc
Copy link
Contributor

@badrishc badrishc commented Mar 8, 2021

Fix #417

@@ -445,7 +449,15 @@ protected SafeFileHandle GetOrAddHandle(int _segmentId)
{
return h;
}
return logHandles.GetOrAdd(_segmentId, segmentId => CreateHandle(segmentId));
if (_disposed) return null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do the callers of GetOrAddHandle need null-value handling?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct, the call sites of WriteFile and ReadFile catch the exception when reading or writing to a null handle, and issue the IO callback on the current thread itself:

            catch
            {
                Interlocked.Decrement(ref numPending);
                callback(uint.MaxValue, 0, context);
                results.Enqueue(result);
            }

di.Delete(recursive: true);
break;
}
catch { }
Copy link
Contributor

Choose a reason for hiding this comment

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

interesting, we still expect an exception?
Did I get it wrong that after the current fix there'll be no exceptions in DirectoryInfo.Delete() ?

Copy link
Contributor Author

@badrishc badrishc Mar 8, 2021

Choose a reason for hiding this comment

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

This sequence, though rare, could happen:

  1. Async IO thread calls Commit, which first checks _disposed to be false
  2. Dispose sets _disposed to be true and disposes the file handle
  3. Commit sees the handle is null and creates a new handle (or creates a handle for different commit file)
  4. Main thread tries to delete directory, fails
  5. Commit thread re-checks _dispose and deletes the handle it just created (for handle reclamation correctness).

To avoid this case, we retry the directory deletion (it did not happen in the tests though). Note that I removed the WaitForCommit and the check for IsFaulted state in this testcase -- if you had that then this loop would not be needed.

Also, unrelated, we did seem to notice earlier (though not confirmed) that the OS releases the handle a bit later after the dispose is called, i.e. an immediate directory deletion does not always work. This is unclear though.

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.

[C#] Cannot delete fasterlog from disk if the log was disposed with a commit request in flight
2 participants