asyncdispatch, asyncnet: add inheritance control (#14362)

* asyncdispatch, asyncnet: add inheritance control

* asyncnet, asyncdispatch: cleanup
This commit is contained in:
alaviss 2020-05-20 07:42:55 +00:00 • committed by GitHub
commit 4ae341353d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 130 additions and 38 deletions

View file

@ -228,6 +228,16 @@ proc initCallSoonProc =
if asyncfutures.getCallSoonProc().isNil:
asyncfutures.setCallSoonProc(callSoon)
template implementSetInheritable() {.dirty.} =
when declared(setInheritable):
proc setInheritable*(fd: AsyncFD, inheritable: bool): bool =
## Control whether a file handle can be inherited by child processes.
## Returns ``true`` on success.
##
## This procedure is not guaranteed to be available for all platforms.
## Test for availability with `declared()`_.
fd.FileHandle.setInheritable(inheritable)
when defined(windows) or defined(nimdoc):
import winlean, sets, hashes
type
@ -695,7 +705,8 @@ when defined(windows) or defined(nimdoc):
retFuture.complete(bytesReceived)
return retFuture
proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn},
inheritable = defined(nimInheritHandles)):
owned(Future[tuple[address: string, client: AsyncFD]]) =
## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection and the remote address of the client.
@ -704,6 +715,9 @@ when defined(windows) or defined(nimdoc):
## The resulting client socket is automatically registered to the
## dispatcher.
##
## If ``inheritable`` is false (the default), the resulting client socket will
## not be inheritable by child processes.
##
## The ``accept`` call may result in an error if the connecting socket
## disconnects during the duration of the ``accept``. If the ``SafeDisconn``
## flag is specified then this error will not be raised and instead
@ -711,7 +725,7 @@ when defined(windows) or defined(nimdoc):
verifyPresence(socket)
var retFuture = newFuture[tuple[address: string, client: AsyncFD]]("acceptAddr")
var clientSock = createNativeSocket()
var clientSock = createNativeSocket(inheritable = inheritable)
if clientSock == osInvalidSocket: raiseOSError(osLastError())
const lpOutputLen = 1024
@ -788,6 +802,8 @@ when defined(windows) or defined(nimdoc):
return retFuture
implementSetInheritable()
proc closeSocket*(socket: AsyncFD) =
## Closes a socket and ensures that it is unregistered.
socket.SocketHandle.close()
@ -1090,6 +1106,9 @@ else:
import selectors
from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
MSG_NOSIGNAL
when declared(posix.accept4):
from posix import accept4, SOCK_CLOEXEC
const
InitCallbackListSize = 4 # initial size of callbacks sequence,
# associated with file/socket descriptor.
@ -1263,6 +1282,8 @@ else:
# descriptor was unregistered in callback via `unregister()`.
discard
implementSetInheritable()
proc closeSocket*(sock: AsyncFD) =
let selector = getGlobalDispatcher().selector
if sock.SocketHandle notin selector:
@ -1484,7 +1505,8 @@ else:
addRead(socket, cb)
return retFuture
proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn},
inheritable = defined(nimInheritHandles)):
owned(Future[tuple[address: string, client: AsyncFD]]) =
var retFuture = newFuture[tuple[address: string,
client: AsyncFD]]("acceptAddr")
@ -1492,8 +1514,21 @@ else:
result = true
var sockAddress: Sockaddr_storage
var addrLen = sizeof(sockAddress).SockLen
var client = accept(sock.SocketHandle,
cast[ptr SockAddr](addr(sockAddress)), addr(addrLen))
var client =
when declared(accept4):
accept4(sock.SocketHandle, cast[ptr SockAddr](addr(sockAddress)),
addr(addrLen), if inheritable: 0 else: SOCK_CLOEXEC)
else:
accept(sock.SocketHandle, cast[ptr SockAddr](addr(sockAddress)),
addr(addrLen))
when declared(setInheritable) and not declared(accept4):
if client != osInvalidSocket and not setInheritable(client, inheritable):
# Set failure first because close() itself can fail,
# altering osLastError().
retFuture.fail(newOSError(osLastError()))
close client
return false
if client == osInvalidSocket:
let lastError = osLastError()
assert lastError.int32 != EWOULDBLOCK and lastError.int32 != EAGAIN
@ -1578,8 +1613,9 @@ proc poll*(timeout = 500) =
## `epoll`:idx: or `kqueue`:idx: primitive only once.
discard runOnce(timeout)
template createAsyncNativeSocketImpl(domain, sockType, protocol) =
let handle = createNativeSocket(domain, sockType, protocol)
template createAsyncNativeSocketImpl(domain, sockType, protocol: untyped,
inheritable = defined(nimInheritHandles)) =
let handle = createNativeSocket(domain, sockType, protocol, inheritable)
if handle == osInvalidSocket:
return osInvalidSocket.AsyncFD
handle.setBlocking(false)
@ -1589,13 +1625,15 @@ template createAsyncNativeSocketImpl(domain, sockType, protocol) =
register(result)
proc createAsyncNativeSocket*(domain: cint, sockType: cint,
protocol: cint): AsyncFD =
createAsyncNativeSocketImpl(domain, sockType, protocol)
protocol: cint,
inheritable = defined(nimInheritHandles)): AsyncFD =
createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable)
proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): AsyncFD =
createAsyncNativeSocketImpl(domain, sockType, protocol)
sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP,
inheritable = defined(nimInheritHandles)): AsyncFD =
createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable)
proc newAsyncNativeSocket*(domain: cint, sockType: cint,
protocol: cint): AsyncFD {.deprecated: "use createAsyncNativeSocket instead".} =
@ -1824,12 +1862,17 @@ proc withTimeout*[T](fut: Future[T], timeout: int): owned(Future[bool]) =
return retFuture
proc accept*(socket: AsyncFD,
flags = {SocketFlag.SafeDisconn}): owned(Future[AsyncFD]) =
flags = {SocketFlag.SafeDisconn},
inheritable = defined(nimInheritHandles)): owned(Future[AsyncFD]) =
## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection.
##
## If ``inheritable`` is false (the default), the resulting client socket
## will not be inheritable by child processes.
##
## The future will complete when the connection is successfully accepted.
var retFut = newFuture[AsyncFD]("accept")
var fut = acceptAddr(socket, flags)
var fut = acceptAddr(socket, flags, inheritable)
fut.callback =
proc (future: Future[tuple[address: string, client: AsyncFD]]) =
assert future.finished