Skip to content
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
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions lib/pure/nativesockets.nim
Original file line number Diff line number Diff line change
Expand Up @@ -725,13 +725,7 @@ proc setBlocking*(s: SocketHandle, blocking: bool) =
if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
raiseOSError(osLastError())
else: # BSD sockets
var x: int = fcntl(s, F_GETFL, 0)
if x == -1:
raiseOSError(osLastError())
else:
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
if fcntl(s, F_SETFL, mode) == -1:
raiseOSError(osLastError())
setNonBlocking(FileHandle(s), not blocking)

proc timeValFromMilliseconds(timeout = 500): Timeval =
if timeout != -1:
Expand Down
56 changes: 56 additions & 0 deletions lib/system/io.nim
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ const SupportIoctlInheritCtl = (defined(linux) or defined(bsd)) and
not defined(nimscript)
when SupportIoctlInheritCtl:
var
FIONBIO {.importc, header: "<sys/ioctl.h>".}: cint
FIOCLEX {.importc, header: "<sys/ioctl.h>".}: cint
FIONCLEX {.importc, header: "<sys/ioctl.h>".}: cint

Expand All @@ -279,7 +280,10 @@ elif defined(posix) and not defined(lwip) and not defined(nimscript):
var
F_GETFD {.importc, header: "<fcntl.h>".}: cint
F_SETFD {.importc, header: "<fcntl.h>".}: cint
F_GETFL {.importc, header: "<fcntl.h>".}: cint
F_SETFL {.importc, header: "<fcntl.h>".}: cint
FD_CLOEXEC {.importc, header: "<fcntl.h>".}: cint
O_NONBLOCK {.importc, header: "<fcntl.h>".}: cint

proc c_fcntl(fd: cint, cmd: cint): cint {.
importc: "fcntl", header: "<fcntl.h>", varargs.}
Expand Down Expand Up @@ -361,6 +365,58 @@ when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(w
result = setHandleInformation(cast[IoHandle](f), HANDLE_FLAG_INHERIT,
inheritable.WinDWORD) != 0

when defined(nimdoc) or (defined(posix) and not defined(nimscript) and not defined(windows)):
proc setNonBlocking*(f: FileHandle, blocking = false) {.raises: [OSError].} =
## Control file handle blocking mode.
##
## Non-blocking IO `read`/`write` calls return immediately with whatever
## result is available, without putting the current thread to sleep. The
## call is expected to be tried again.
##
## Calling `read` on a non-blocking file handle will result in an `IOError`
## of 'Resource temporarily unavailable' whenever there is no data to read.
## The state can be checked beforehand with either
## `endOfFile <#endOfFile,File>`_ or
## `atEnd <streams.html#atEnd,Stream>`_.
##
## This requires the OS file handle, which can be
## retrieved via `getOsFileHandle <#getOsFileHandle,File>`_.
##
## This procedure is available for POSIX platforms. Test for
## availability with `declared() <system.html#declared,untyped>`_.
##
## There are separate APIs on Windows for using console handles,
## pipes and sockets in a non-blocking manner. Some of which aren't
## implemented in stdlib yet.
##
## See `setNonBlocking(File, bool) <#setNonBlocking,File>`_.
when not defined(windows):
runnableExamples:
quantimnot marked this conversation as resolved.
Show resolved Hide resolved
setNonBlocking(getOsFileHandle(stdin))
doAssert(endOfFile(stdin))
when SupportIoctlInheritCtl:
let opt = if blocking: 0 else: 1
if c_ioctl(f, FIONBIO, unsafeAddr(opt)) == -1:
raise newException(OSError, "failed to set file handle mode")
elif defined(posix):
var x: int = c_fcntl(f, F_GETFL, 0)
if x == -1:
raise newException(OSError, "failed to get file handle mode")
else:
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
if c_fcntl(f, F_SETFL, mode) == -1:
raise newException(OSError, "failed to set file handle mode")

proc setNonBlocking*(f: File, blocking = false) {.raises: [OSError].} =
## Control file blocking mode.
##
## See `setNonBlocking(FileHandle, bool) <#setNonBlocking,FileHandle>`_.
when not defined(windows):
runnableExamples:
setNonBlocking(stdin)
doAssert(endOfFile(stdin))
setNonBlocking(getFileHandle(f), blocking)

Copy link
Contributor

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

proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect],
benign.} =
## reads a line of text from the file `f` into `line`. May throw an IO
Expand Down
10 changes: 10 additions & 0 deletions tests/stdlib/tio_nonblocking.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
discard """
disabled: "win"
targets: "c"
"""

proc main =
setNonBlocking(stdin)
doAssert(endOfFile(stdin))

main()