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.