Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Follow-up on #1875. Originally I intended to implement a fully thread-safe mmap
resize
, meaning the tread would wait if there were other threads using mmap at the time. At the same time I wanted to keep the requirement that in normal use (noresize
in progress), regular threads do no locking, waiting, or other expensive operations (Interlocked.CompareExchange
deemed not expensive). It actually seemed to be possible, with the following schema:resize
, grabs the writer lock of aReaderWriterLockSlim
Exclusive
in the state. This prompts incoming regular threads to fall back on the reader lock and wait on the writer lock be released.In one word — complicated. So I agree with @slozier, that in such cases it should be the responsibility of the user code to ensure that mmap is not in use on other threads when resize is called. However, if not satisfied, rather than letting user threads run rampant across an unprotected mmap, it is better to simply raise an exception at the controlled entry point, just as it is now done when the mmap is already closed. I chose
EAGAIN
since the resource will be available again for access after a while. In this way the problem is punted back to the user, who can decide how to handle it: from not doing anything and letting the exception propagate, to catching a well defined exception (BlockingIOError
) and retrying in a loop. Because the mmap code does not need to wait (and simply throws an exception), the mechanism becomes much simpler. This is what is implemented in this PR.I also added support for preventing
resize
while there are existing buffer exports, since Buffer Protocol is coming in #1866. The only thing thatGetBuffer
/Release
need to do is to increase/decrease the ref count withMmapLocker
and then atomically set bitStateBits.Exporting
on the first export and reset it on the last. This will handleresize
, which on entry will throwBufferException
while there are existing exports.This will also handle
close
, although no exception will ever be thrown. It is not needed;close
will be accepted and quickly return, while the whole mmap will auto-dispose when the last buffer is released. I know that it is not the same as what CPython does (which completely preventsclose
while there are exports), but I don't think we should be heeding CPython in this case:WaitForPendingFinalizer
is expensive and disruptive, and on Mono will not work anyway.resize
case, I do think they have a point withclose
. Ifclose
can refuse to close, people will simply stop closing mmap atogether, relying of GC, which is not the best idea.