-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Add non-blocking io mode setter for POSIX #19120
Closed
Closed
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
b0e964a
Add non-blocking io mode setter for POSIX
quantimnot 8d1fc85
Fix case where ioctl is used
quantimnot 4f34697
Fix faulty ioctl use and prevent runnableExamples on windows
quantimnot fe6c3fd
Fix more bugs of mine
quantimnot 80f3d76
Simplified compilation logic and corrected runnableExamples
quantimnot 0daa59c
sigh. Fixed indent mistake in last commit
quantimnot 4586bda
Changed setNonBlocking param to nonBlobking=true and changed test to …
quantimnot a9ca9b3
Removed uneeded sleep in test
quantimnot 2990984
Fix compilation on FreeRTOS
quantimnot 1869ae9
Try to see if freertos has fcntl or not
quantimnot a80e22f
Use getOsFileHandle instead of getFileHandle; doc corrections, links
quantimnot 0526f73
Add timeout to test; fix typo in test doc
quantimnot 5c31d20
Disable test for FreeRTOS; correct comment typo
quantimnot 3ca182b
Revert some changes; allow some code dup to fix FreeRTOS build
quantimnot c1849a9
Increased the test timeout
quantimnot 70f0209
Remove the nonexistent freertos discard statement from test
quantimnot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
discard """ | ||
disabled: "win" | ||
targets: "c" | ||
matrix: "-d:threadsafe --threads:on" | ||
timeout: 60 | ||
output: "started\nstopped\nquit\n" | ||
""" | ||
# PURPOSE | ||
# Test setting and unsetting non-blocking IO mode on stdin, stdout, and stderr. | ||
# Test the exception handing behavior. | ||
# DESIGN | ||
# Create a parent process. | ||
# Parent tries setting an invalid file handle to non-blocking and then catches the error. | ||
# Parent creates child process. | ||
# Parent waits on child's non-blocking stdout and stderr. | ||
# Child waits on non-blocking stdin. | ||
# Parent sends `start` command to child on stdin. | ||
# Child responds by writing `started` to its stdout and then waits on blocking stdin. | ||
# Parent reads `started` on child's stdout and responds with `stop` on stdin and then closes stdin. | ||
# Child catches IO exception from closed stdin and responds with `stopped` on stdout and quits. | ||
# Parent gets signaled of quit, prints `quit` and quits. | ||
when not (compileOption("threads") and defined(threadsafe)): | ||
{.error: "-d:threadsafe --threads:on needed".} | ||
|
||
import std/[selectors, osproc, streams, os, posix] | ||
|
||
type | ||
Handler = proc() {.closure.} | ||
ErrorHandler = proc(code: OSErrorCode) {.closure.} | ||
Handlers = tuple[process, stdout, stderr: tuple[handle: int, onEvent: Handler, onError: ErrorHandler]] | ||
Monitor = enum | ||
StdIn, StdOut, StdErr, Quit | ||
|
||
const blockIndefinitely = -1 | ||
|
||
proc drain(f: File): string = | ||
while not f.endOfFile: | ||
result &= f.readChar | ||
|
||
proc drain(f: Stream): string = | ||
while not f.atEnd: | ||
result &= f.readChar | ||
|
||
proc monitor(arg: Handlers) {.thread.} = | ||
var watcher = newSelector[Monitor]() | ||
let processSignal = watcher.registerProcess(arg.process.handle, Quit) | ||
watcher.registerHandle(arg.stdout.handle, {Event.Read}, StdOut) | ||
watcher.registerHandle(arg.stderr.handle, {Event.Read}, StdErr) | ||
{.gcsafe.}: | ||
block running: | ||
while true: | ||
let events = watcher.select(blockIndefinitely) | ||
for ready in events.items: | ||
var kind: Monitor = watcher.getData(ready.fd) | ||
case kind: | ||
of StdIn: discard | ||
of StdOut: | ||
if Event.Read in ready.events: | ||
arg.stdout.onEvent() | ||
if Event.Error in ready.events: | ||
if ready.errorCode.int == ECONNRESET: | ||
watcher.unregister(ready.fd) | ||
else: | ||
arg.stderr.onError(ready.errorCode) | ||
break running | ||
of StdErr: | ||
if Event.Read in ready.events: | ||
arg.stderr.onEvent() | ||
if Event.Error in ready.events: | ||
if ready.errorCode.int == ECONNRESET: | ||
watcher.unregister(ready.fd) | ||
else: | ||
arg.stderr.onError(ready.errorCode) | ||
break running | ||
of Quit: | ||
arg.process.onEvent() | ||
break running | ||
watcher.unregister(processSignal) | ||
watcher.close | ||
|
||
proc parent = | ||
try: | ||
# test that exception is thrown | ||
setNonBlocking(-1) | ||
doAssert(false, "setNonBlocking should raise exception for invalid input") | ||
except: | ||
discard | ||
var child = startProcess( | ||
getAppFilename(), | ||
args = ["child"], | ||
options = {} | ||
) | ||
var thread: Thread[Handlers] | ||
setNonBlocking(child.outputHandle) | ||
setNonBlocking(child.errorHandle) | ||
proc onEvent() = | ||
let output = child.outputStream.drain | ||
stdout.write output | ||
if output == "started\n": | ||
child.inputStream.write "stop" | ||
child.inputStream.close | ||
proc onError(code: OSErrorCode) {.closure.} = | ||
doAssert(false, "error " & $code) | ||
proc onQuit() {.closure.} = | ||
echo "quit" | ||
createThread(thread, monitor, ( | ||
(child.processID.int, onQuit, onError), | ||
(child.outputHandle.int, onEvent, onError), | ||
(child.errorHandle.int, onEvent, onError))) | ||
child.inputStream.write "start" | ||
child.inputStream.flush | ||
doAssert(child.waitForExit == 0) | ||
joinThread(thread) | ||
|
||
proc child = | ||
var watcher = newSelector[Monitor]() | ||
watcher.registerHandle(stdin.getOsFileHandle.int, {Event.Read}, StdIn) | ||
setNonBlocking(stdin) | ||
block running: | ||
while true: | ||
let events = watcher.select(blockIndefinitely) | ||
for ready in events.items: | ||
var kind: Monitor = watcher.getData(ready.fd) | ||
case kind: | ||
of StdIn: | ||
if stdin.drain == "start": # this would normally block | ||
echo "started" # piped to parent | ||
break running | ||
else: discard | ||
watcher.close | ||
setNonBlocking(stdin, false) | ||
try: | ||
# this line ensures the above setNonBlocking call is distinguisable from | ||
# a no-op; "stopped" would never be sent | ||
if not stdin.endOfFile: | ||
echo stdin.readAll | ||
except: # this will raise when stdin is closed by the parent | ||
doAssert(osLastError().int == EAGAIN) | ||
echo "stopped" # piped to parent | ||
|
||
proc main = | ||
if paramCount() > 0: | ||
child() | ||
else: | ||
parent() | ||
|
||
main() |
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Define for Windows too, but with a
{.error: ....}
(less confusing than getting a "Undeclared identifier" error