Merge branch 'devel' of https://github.com/nim-lang/Nim into devel
This commit is contained in:
commit
e11d582fa1
14 changed files with 278 additions and 99 deletions
|
|
@ -568,7 +568,7 @@ when defined(windows) or defined(nimdoc):
|
|||
if flags.isDisconnectionError(errcode):
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||
retFuture.fail(newOSError(errcode))
|
||||
)
|
||||
|
||||
let ret = WSASend(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived,
|
||||
|
|
@ -1134,11 +1134,6 @@ else:
|
|||
var data = newAsyncData()
|
||||
p.selector.registerHandle(fd.SocketHandle, {}, data)
|
||||
|
||||
proc closeSocket*(sock: AsyncFD) =
|
||||
let disp = getGlobalDispatcher()
|
||||
disp.selector.unregister(sock.SocketHandle)
|
||||
sock.SocketHandle.close()
|
||||
|
||||
proc unregister*(fd: AsyncFD) =
|
||||
getGlobalDispatcher().selector.unregister(fd.SocketHandle)
|
||||
|
||||
|
|
@ -1174,7 +1169,9 @@ else:
|
|||
let p = getGlobalDispatcher()
|
||||
not p.selector.isEmpty() or p.timers.len != 0 or p.callbacks.len != 0
|
||||
|
||||
template processBasicCallbacks(ident, rwlist: untyped) =
|
||||
proc processBasicCallbacks(
|
||||
fd: AsyncFD, event: Event
|
||||
): tuple[readCbListCount, writeCbListCount: int] =
|
||||
# Process pending descriptor and AsyncEvent callbacks.
|
||||
#
|
||||
# Invoke every callback stored in `rwlist`, until one
|
||||
|
|
@ -1187,32 +1184,46 @@ else:
|
|||
# or it can be possible to fall into endless cycle.
|
||||
var curList: seq[Callback]
|
||||
|
||||
withData(p.selector, ident, adata) do:
|
||||
shallowCopy(curList, adata.rwlist)
|
||||
adata.rwlist = newSeqOfCap[Callback](InitCallbackListSize)
|
||||
let selector = getGlobalDispatcher().selector
|
||||
withData(selector, fd.int, fdData):
|
||||
case event
|
||||
of Event.Read:
|
||||
shallowCopy(curList, fdData.readList)
|
||||
fdData.readList = newSeqOfCap[Callback](InitCallbackListSize)
|
||||
of Event.Write:
|
||||
shallowCopy(curList, fdData.writeList)
|
||||
fdData.writeList = newSeqOfCap[Callback](InitCallbackListSize)
|
||||
else:
|
||||
assert false, "Cannot process callbacks for " & $event
|
||||
|
||||
let newLength = max(len(curList), InitCallbackListSize)
|
||||
var newList = newSeqOfCap[Callback](newLength)
|
||||
|
||||
for cb in curList:
|
||||
if len(newList) > 0:
|
||||
# A callback has already returned with EAGAIN, don't call any others
|
||||
# until next `poll`.
|
||||
if not cb(fd):
|
||||
# Callback wants to be called again.
|
||||
newList.add(cb)
|
||||
else:
|
||||
if not cb(fd.AsyncFD):
|
||||
# Callback wants to be called again.
|
||||
newList.add(cb)
|
||||
# This callback has returned with EAGAIN, so we don't need to
|
||||
# call any other callbacks as they are all waiting for the same event
|
||||
# on the same fd.
|
||||
break
|
||||
|
||||
withData(p.selector, ident, adata) do:
|
||||
# descriptor still present in queue.
|
||||
adata.rwlist = newList & adata.rwlist
|
||||
rLength = len(adata.readList)
|
||||
wLength = len(adata.writeList)
|
||||
withData(selector, fd.int, fdData) do:
|
||||
# Descriptor is still present in the queue.
|
||||
case event
|
||||
of Event.Read:
|
||||
fdData.readList = newList & fdData.readList
|
||||
of Event.Write:
|
||||
fdData.writeList = newList & fdData.writeList
|
||||
else:
|
||||
assert false, "Cannot process callbacks for " & $event
|
||||
|
||||
result.readCbListCount = len(fdData.readList)
|
||||
result.writeCbListCount = len(fdData.writeList)
|
||||
do:
|
||||
# descriptor was unregistered in callback via `unregister()`.
|
||||
rLength = -1
|
||||
wLength = -1
|
||||
# Descriptor was unregistered in callback via `unregister()`.
|
||||
result.readCbListCount = -1
|
||||
result.writeCbListCount = -1
|
||||
|
||||
template processCustomCallbacks(ident: untyped) =
|
||||
# Process pending custom event callbacks. Custom events are
|
||||
|
|
@ -1221,7 +1232,7 @@ else:
|
|||
# so there is no need to iterate over list.
|
||||
var curList: seq[Callback]
|
||||
|
||||
withData(p.selector, ident, adata) do:
|
||||
withData(p.selector, ident.int, adata) do:
|
||||
shallowCopy(curList, adata.readList)
|
||||
adata.readList = newSeqOfCap[Callback](InitCallbackListSize)
|
||||
|
||||
|
|
@ -1232,16 +1243,33 @@ else:
|
|||
if not cb(fd.AsyncFD):
|
||||
newList.add(cb)
|
||||
|
||||
withData(p.selector, ident, adata) do:
|
||||
withData(p.selector, ident.int, adata) do:
|
||||
# descriptor still present in queue.
|
||||
adata.readList = newList & adata.readList
|
||||
if len(adata.readList) == 0:
|
||||
# if no callbacks registered with descriptor, unregister it.
|
||||
p.selector.unregister(fd)
|
||||
p.selector.unregister(fd.int)
|
||||
do:
|
||||
# descriptor was unregistered in callback via `unregister()`.
|
||||
discard
|
||||
|
||||
proc closeSocket*(sock: AsyncFD) =
|
||||
let selector = getGlobalDispatcher().selector
|
||||
if sock.SocketHandle notin selector:
|
||||
raise newException(ValueError, "File descriptor not registered.")
|
||||
|
||||
let data = selector.getData(sock.SocketHandle)
|
||||
sock.unregister()
|
||||
sock.SocketHandle.close()
|
||||
# We need to unblock the read and write callbacks which could still be
|
||||
# waiting for the socket to become readable and/or writeable.
|
||||
for cb in data.readList & data.writeList:
|
||||
if not cb(sock):
|
||||
raise newException(
|
||||
ValueError, "Expecting async operations to stop when fd has closed."
|
||||
)
|
||||
|
||||
|
||||
proc runOnce(timeout = 500): bool =
|
||||
let p = getGlobalDispatcher()
|
||||
when ioselSupportedPlatform:
|
||||
|
|
@ -1257,41 +1285,42 @@ else:
|
|||
let nextTimer = processTimers(p, result)
|
||||
var count = p.selector.selectInto(adjustTimeout(timeout, nextTimer), keys)
|
||||
for i in 0..<count:
|
||||
var custom = false
|
||||
let fd = keys[i].fd
|
||||
let fd = keys[i].fd.AsyncFD
|
||||
let events = keys[i].events
|
||||
var rLength = 0 # len(data.readList) after callback
|
||||
var wLength = 0 # len(data.writeList) after callback
|
||||
var (readCbListCount, writeCbListCount) = (0, 0)
|
||||
|
||||
if Event.Read in events or events == {Event.Error}:
|
||||
processBasicCallbacks(fd, readList)
|
||||
(readCbListCount, writeCbListCount) =
|
||||
processBasicCallbacks(fd, Event.Read)
|
||||
result = true
|
||||
|
||||
if Event.Write in events or events == {Event.Error}:
|
||||
processBasicCallbacks(fd, writeList)
|
||||
(readCbListCount, writeCbListCount) =
|
||||
processBasicCallbacks(fd, Event.Write)
|
||||
result = true
|
||||
|
||||
var isCustomEvent = false
|
||||
if Event.User in events:
|
||||
processBasicCallbacks(fd, readList)
|
||||
custom = true
|
||||
if rLength == 0:
|
||||
p.selector.unregister(fd)
|
||||
(readCbListCount, writeCbListCount) =
|
||||
processBasicCallbacks(fd, Event.Read)
|
||||
isCustomEvent = true
|
||||
if readCbListCount == 0:
|
||||
p.selector.unregister(fd.int)
|
||||
result = true
|
||||
|
||||
when ioselSupportedPlatform:
|
||||
if (customSet * events) != {}:
|
||||
custom = true
|
||||
isCustomEvent = true
|
||||
processCustomCallbacks(fd)
|
||||
result = true
|
||||
|
||||
# because state `data` can be modified in callback we need to update
|
||||
# descriptor events with currently registered callbacks.
|
||||
if not custom:
|
||||
if not isCustomEvent and (readCbListCount != -1 and writeCbListCount != -1):
|
||||
var newEvents: set[Event] = {}
|
||||
if rLength != -1 and wLength != -1:
|
||||
if rLength > 0: incl(newEvents, Event.Read)
|
||||
if wLength > 0: incl(newEvents, Event.Write)
|
||||
p.selector.updateHandle(SocketHandle(fd), newEvents)
|
||||
if readCbListCount > 0: incl(newEvents, Event.Read)
|
||||
if writeCbListCount > 0: incl(newEvents, Event.Write)
|
||||
p.selector.updateHandle(SocketHandle(fd), newEvents)
|
||||
|
||||
# Timer processing.
|
||||
discard processTimers(p, result)
|
||||
|
|
@ -1370,7 +1399,7 @@ else:
|
|||
if flags.isDisconnectionError(lastError):
|
||||
retFuture.complete()
|
||||
else:
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
retFuture.fail(newOSError(lastError))
|
||||
else:
|
||||
result = false # We still want this callback to be called.
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -393,11 +393,13 @@ proc asyncCheck*[T](future: Future[T]) =
|
|||
## This should be used instead of ``discard`` to discard void futures,
|
||||
## or use ``waitFor`` if you need to wait for the future's completion.
|
||||
assert(not future.isNil, "Future is nil")
|
||||
future.callback =
|
||||
proc () =
|
||||
if future.failed:
|
||||
injectStacktrace(future)
|
||||
raise future.error
|
||||
# TODO: We can likely look at the stack trace here and inject the location
|
||||
# where the `asyncCheck` was called to give a better error stack message.
|
||||
proc asyncCheckCallback() =
|
||||
if future.failed:
|
||||
injectStacktrace(future)
|
||||
raise future.error
|
||||
future.callback = asyncCheckCallback
|
||||
|
||||
proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
|
||||
## Returns a future which will complete once both ``fut1`` and ``fut2``
|
||||
|
|
|
|||
|
|
@ -57,8 +57,10 @@ proc osErrorMsg*(errorCode: OSErrorCode): string =
|
|||
if errorCode != OSErrorCode(0'i32):
|
||||
result = $c_strerror(errorCode.int32)
|
||||
|
||||
proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
|
||||
## Raises an `OSError exception <system.html#OSError>`_.
|
||||
proc newOSError*(
|
||||
errorCode: OSErrorCode, additionalInfo = ""
|
||||
): owned(ref OSError) {.noinline.} =
|
||||
## Creates a new `OSError exception <system.html#OSError>`_.
|
||||
##
|
||||
## The ``errorCode`` will determine the
|
||||
## message, `osErrorMsg proc <#osErrorMsg,OSErrorCode>`_ will be used
|
||||
|
|
@ -82,7 +84,14 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
|
|||
e.msg.addQuoted additionalInfo
|
||||
if e.msg == "":
|
||||
e.msg = "unknown OS error"
|
||||
raise e
|
||||
return e
|
||||
|
||||
proc raiseOSError*(errorCode: OSErrorCode, additionalInfo = "") {.noinline.} =
|
||||
## Raises an `OSError exception <system.html#OSError>`_.
|
||||
##
|
||||
## Read the description of the `newOSError proc <#newOSError,OSErrorCode,string>`_ to learn
|
||||
## how the exception object is created.
|
||||
raise newOSError(errorCode, additionalInfo)
|
||||
|
||||
{.push stackTrace:off.}
|
||||
proc osLastError*(): OSErrorCode {.sideEffect.} =
|
||||
|
|
|
|||
|
|
@ -3776,8 +3776,11 @@ proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} =
|
|||
when defined(nimscript) or defined(js) or (hostOS == "standalone"):
|
||||
echo errormsg
|
||||
else:
|
||||
cstderr.rawWrite(errormsg)
|
||||
cstderr.rawWrite("\n")
|
||||
when nimvm:
|
||||
echo errormsg
|
||||
else:
|
||||
cstderr.rawWrite(errormsg)
|
||||
cstderr.rawWrite("\n")
|
||||
quit(errorcode)
|
||||
|
||||
{.pop.} # checks
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue