Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
prompting: add epoll package (#12963)
* mailmap: map new commits from [email protected] to [email protected] Signed-off-by: Oliver Calder <[email protected]> * Initial commit Signed-off-by: Zygmunt Krynicki <[email protected]> Signed-off-by: Oliver Calder <[email protected]> * Correct references to old name Signed-off-by: Oliver Calder <[email protected]> * osutil/epoll: fixed epoll test import Signed-off-by: Oliver Calder <[email protected]> * osutil/epoll: make Close() set epoll fd to -1 Signed-off-by: Oliver Calder <[email protected]> * epoll: renamed `FromSys()` epoll event parameter name to `ev` Signed-off-by: Oliver Calder <[email protected]> * epoll: added unit tests, but `Register()` always fails, as does ioctl syscall to prepare fd to register Signed-off-by: Oliver Calder <[email protected]> * epoll: removed unused `modeSet` type from epoll_test.go Signed-off-by: Oliver Calder <[email protected]> * epoll: replaced `syscall` with vendored `x/sys/unix` Signed-off-by: Oliver Calder <[email protected]> * epoll: simplified `Readiness` type to match `EPOLL{IN,OUT}` flags Signed-off-by: Oliver Calder <[email protected]> * epoll: fix unit tests using socketpair Signed-off-by: Oliver Calder <[email protected]> * epoll: fixed typo in test Signed-off-by: Oliver Calder <[email protected]> * epoll: tie events list to instance with entry for each registered fd, and add unit tests Signed-off-by: Oliver Calder <[email protected]> * epoll: add `WaitTimeout(msec)` with configurable timeout Signed-off-by: Oliver Calder <[email protected]> * epoll: remove comment about using internal/poll instead of epoll Signed-off-by: Oliver Calder <[email protected]> * epoll: improve error message when epoll create syscall fails Signed-off-by: Oliver Calder <[email protected]> * epoll: use time.Duration instead of int in `WaitTimeout()` Signed-off-by: Oliver Calder <[email protected]> * epoll: make epoll package thread safe Replace the instance-specific event buffer with a mutex-guarded registered FD count, allowing an event buffer with a number of slots equal to the current number of registered FDs to be allocated in `Wait()`, and thus allowing events from all FDs to be handled by a single `Wait()` call. Previously, the instance-specific event buffer was shared by separate calls to `Wait()`, with the address of the start of the buffer passed into the EPOLL_WAIT syscall, so multiple concurrent calls to `Wait()` could cause race conditions where the contents of the buffer was overwritten by multiple threads at once. Now, since the event buffer is allocated within the `Wait()` function, this race condition is avoided. Whenever a new FD is registered, the registeredFdCount variable is incremented, and whenever an FD is deregistered, that variable is decremented, in both cases guarded by the mutex for the epoll instance. So what happens if there are n FDs registered, a call to `Wait()` begins, and then while waiting, another FD is registered with the epoll instance? Could this result in a buffer with too few entries for the now (n+1) registered FDs? No, because beyond race conditions where the kernel receives two write syscalls simultaneously and these end up in the same epoll response (extremely unlikely), the only way that multiple events are returned by the same call to `Wait()` is if there were previous write events which had been caught by epoll but hadn't yet been handled with an EPOLL_WAIT syscall. In this case, `Wait()` would immediately return all those previous events, before the new FD is able to be registered and written to. The way around this would be if `Wait()` began, allocated the buffer with size n, and then the thread were interrupted, and the new FD registered and written to before the EPOLL_WAIT syscall occurred. In this case, if all the initial n FDs had pending activity, then the `Wait()` call would return an event buffer of size n, presumably with the events for the original n FDs, and a subsequent call to `Wait()` would return the event on the newly-registered FD. No event is lost in any case. Signed-off-by: Oliver Calder <[email protected]> * epoll: simplified duration in `Wait()` Signed-off-by: Oliver Calder <[email protected]> * epoll: `s/waitMillisecondsThenWriteToFile/waitMillisecondsThenWriteToFd` Signed-off-by: Oliver Calder <[email protected]> * epoll: simplified timeout duration definitions in tests Signed-off-by: Oliver Calder <[email protected]> * epoll: gracefully handle `EINT` errors from `EPOLL_WAIT` syscall Signed-off-by: Oliver Calder <[email protected]> * epoll: clarified handling of EINTR from EPOLL_WAIT syscall Signed-off-by: Oliver Calder <[email protected]> * epoll: replaced mutex with atomic Int32 calls wrapped in helper functions Signed-off-by: Oliver Calder <[email protected]> * epoll: added handling for waiting with no registered FDs It may be the case that one wishes to wait for epoll events without first registering any file descriptors to which to listen. Perhaps those file descriptors will be registered later by another thread, and we want to capture any activity on them as soon as that occurs. In any case, we do not wish to deny the ability to call `Wait()` if there are no registered FDs. However, the `EpollWait` syscall requires a nonzero-length buffer to be passed in (technically a nonzero length value, along with a pointer to the buffer). Thus, in order to allow future registered FDs to be handled by an existing wait call which was initiated when no FDs were registered, it is necessary to ensure that a buffer of at least length 1 is passed into the `EpollWait` syscall. Unit tests were added to check for correct waiting behavior when no FDs are registered, when waiting is initiated before FD is registered, and when the final FD is deregistered after wait has been initiated. The `TestWaitThenRegister` function was rewritten to be more salient. In particular, it now tests behavior when no FDs are registered at the time `Wait()` is called, and then a new FD is registered and activity occurs on it. Signed-off-by: Oliver Calder <[email protected]> * epoll: added tests for {,de}registering bad file descriptors Signed-off-by: Oliver Calder <[email protected]> * epoll: improved the arbitrary nonexistent FD used by {,de}register tests Signed-off-by: Oliver Calder <[email protected]> * epoll: cleaned up increment/decrement order on error, and unneeded `runtime.KeepAlive()` Signed-off-by: Oliver Calder <[email protected]> * epoll: added early return if epoll_wait returned 0 events Signed-off-by: Oliver Calder <[email protected]> * epoll: clean up opened sockets, and ensure Assert not called before reading from channels Signed-off-by: Oliver Calder <[email protected]> * epoll: use proper checker methods when applicable Signed-off-by: Oliver Calder <[email protected]> * epoll: Add comment about minimum buffer size for epoll_wait Signed-off-by: Oliver Calder <[email protected]> * epoll: added unit test for EINTR handling Signed-off-by: Oliver Calder <[email protected]> * epoll: reduced test sleep and timeout durations to make unit tests faster Signed-off-by: Oliver Calder <[email protected]> * epoll: defined `defaultDuration` for use in timing-sensitive unit tests Signed-off-by: Oliver Calder <[email protected]> * epoll: replaced `Errno` magic numbers with defined values Signed-off-by: Oliver Calder <[email protected]> --------- Signed-off-by: Oliver Calder <[email protected]> Signed-off-by: Zygmunt Krynicki <[email protected]> Co-authored-by: Zygmunt Krynicki <[email protected]>
- Loading branch information