- Fixes an assertion triggered by accessing a file field in releaseRef
- Fixed a potential leak of file system watcher handles when calling releaseRef from inside the supplied callback - this was the reason why the fixed assertion error wasn't triggered by the test code
Removes overlapped I/O events when a handle gets closed prematurely (before all events have been processed) to avoid potential range violation errors as a consequence.
Unfortunately the built-in assert GC allocates an exception, which means that if called directly or indirectly form a finalizer, the assertion will not become visible and instead an InvalidMemoryOperationError is thrown.
This implements a custom nogc_assert() function that directly prints the assertion message and uses abort() to end the process.
Calling WinSock functions from inside of a completion routine results in undefined behavior, because the completion routine may be triggered within another WinSock function that enters an alertable wait state. For this reason, none of the callbacks that are triggered by overlapped I/O may be invoked directly from a completion routine.
To solve this, a ConsumableQueue is filled with all completion events that occur and is processed after each MsgWaitForMultipleObjectsEx call.
Under certain (rare) circumstances, the receive buffer contains data from a file instead of the expected FILE_NOTIFY_INFORMATION structure. No signs of handle or buffer reuse could be found, so the cause for this remains unclear. Until the cause can be narrowed down, this keeps the issue visible while avoiding to crash the application.
Explicitly gets the read/write slot from the handle map from within the callback instead of dereferencing the pointer assigned when starting the operation. This makes sure that no dangling reference is accessed. Instead, if the slot got reused before the callback is invoked (which is a bug), an assertion will be triggered if the slot now has a different handle type.
The underlying file handle and the associated slot could previously be destroyed before the I/O callback had been finally called. Now the callback itself is responsible to destroying the handle.
FileChange now has the full path of a file split into the base path (as specified when creating the watcher), the sub directory, and the file name. This allows to work with less dynamic memory allocations internally.
I/O errors were being reported as IOStatus.ok instead of IOStatus.error, and the I/O operation finalization protocol had a potential race-condition when storing the number of bytes processed.
Periodic timers were first re-enqueued and then removed, effectively causing them to stay at their old position, cutting off the tail of the timer queue.
- Removes the accept() loop, as that doesn't measurably improve performance in practice. This also gives each connection the possibility to perform initial I/O before more concurrency is generated by accepting the next connection.
- Uses SOCK_NONBLOCK in conjunction with socket() and accept4() to save one additional syscall per connection.
The limit of 20 accepted connections per event invocation can otherwise lead to no more connections being accepted when more that 20 connections are available at a time. This is an adaptation of @Boris-Barboris pull request #21 to restrict the level triggered behavior to listening sockets. See also #20.
When the last reference got released while a blocking socket operation was still in progress, the old slot was still accessed from the completion callback, leading to null pointer accesses or other issues.
- Errors were reported as "ok"
- The cancel state logic was flawed and could lose cancel requests
- Operations could be removed from the active sets before they actually finished, causing the callback to never get called
Note that the IOMode.immediate semantics are not compatible with the current 0-udp.d test and will instead call the callback asynchronously. It appears that non-blocking semantics are generally not possible with overlapped sockets.
Generally each FD type should have its own .userData property in the respective driver. This is important for drivers that don't have a unified file descriptor space and need to store user data in a type specific way.