diff --git a/chronos/internal/asyncengine.nim b/chronos/internal/asyncengine.nim index 45197a038..01e7097b2 100644 --- a/chronos/internal/asyncengine.nim +++ b/chronos/internal/asyncengine.nim @@ -185,7 +185,7 @@ when defined(nimdoc): ## Perform single asynchronous step, processing timers and completing ## tasks. Blocks until at least one event has completed. ## - ## Exceptions raised during `async` task exection are stored as outcome + ## Exceptions raised during `async` task exception are stored as outcome ## in the corresponding `Future` - `poll` itself does not raise. proc register2*(fd: AsyncFD): Result[void, OSErrorCode] = discard @@ -697,6 +697,19 @@ elif defined(windows): if not(isNil(aftercb)): loop.callbacks.addLast(AsyncCallback(function: aftercb, udata: param)) + proc safeCloseHandle(h: HANDLE): Result[void, string] = + let res = closeHandle(h) + if res == 0: # WINBOOL FALSE + return err("Failed to close handle error code: " & osErrorMsg(osLastError())) + ok() + + proc closeDispatcher*(loop: PDispatcher): Result[void, string] = + ? safeCloseHandle(loop.ioPort) + for i in loop.handles.items: + closeHandle(i) + loop.handles.clear() + ok() + proc unregisterAndCloseFd*(fd: AsyncFD): Result[void, OSErrorCode] = ## Unregister from system queue and close asynchronous socket. ## @@ -936,6 +949,14 @@ elif defined(macosx) or defined(freebsd) or defined(netbsd) or ## You can execute ``aftercb`` before actual socket close operation. closeSocket(fd, aftercb) + proc closeDispatcher*(loop: PDispatcher): Result[void, string] = + ## Close selector associated with current thread's dispatcher. + try: + loop.selector.close() + except IOSelectorsException as e: + return err("Exception in closeDispatcher: " & e.msg) + ok() + when chronosEventEngine in ["epoll", "kqueue"]: type ProcessHandle* = distinct int diff --git a/chronos/internal/asyncfutures.nim b/chronos/internal/asyncfutures.nim index 51622f14d..6ec1aa98b 100644 --- a/chronos/internal/asyncfutures.nim +++ b/chronos/internal/asyncfutures.nim @@ -641,6 +641,17 @@ proc pollFor[F: Future | InternalRaisesFuture](fut: F): F {.raises: [].} = fut +proc shutdown*(): Result[void, string] {.raises: [].} = + ## Performs the shutdown and cleanup of all dispatcher resources. + ## Notice that this should be called only when sure that no new async tasks will be scheduled. + ## + ## This routine shall be called only after `pollFor` has completed. Upon + ## invocation, all streams are assumed to have been closed. + + let disp = getThreadDispatcher() + ? disp.closeDispatcher() + ok() + proc waitFor*[T: not void](fut: Future[T]): lent T {.raises: [CatchableError].} = ## Blocks the current thread of execution until `fut` has finished, returning ## its value. diff --git a/chronos/ioselects/ioselectors_poll.nim b/chronos/ioselects/ioselectors_poll.nim index 51f21bbfd..1f6a3eb9b 100644 --- a/chronos/ioselects/ioselectors_poll.nim +++ b/chronos/ioselects/ioselectors_poll.nim @@ -56,7 +56,7 @@ proc new*(t: typedesc[Selector], T: typedesc): SelectResult[Selector[T]] = proc close2*[T](s: Selector[T]): SelectResult[void] = s.fds.clear() - s.pollfds.clear() + s.pollfds.setLen(0) proc new*(t: typedesc[SelectEvent]): SelectResult[SelectEvent] = let flags = {DescriptorFlag.NonBlock, DescriptorFlag.CloseOnExec}