use single backtick (#17166)

This commit is contained in:
flywind 2021-02-23 23:05:39 -06:00 • committed by GitHub
commit 47c078e29c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 305 additions and 305 deletions

View file

@ -8,25 +8,25 @@
# #
## This module implements asynchronous IO. This includes a dispatcher, ## This module implements asynchronous IO. This includes a dispatcher,
## a ``Future`` type implementation, and an ``async`` macro which allows ## a `Future` type implementation, and an `async` macro which allows
## asynchronous code to be written in a synchronous style with the ``await`` ## asynchronous code to be written in a synchronous style with the `await`
## keyword. ## keyword.
## ##
## The dispatcher acts as a kind of event loop. You must call ``poll`` on it ## The dispatcher acts as a kind of event loop. You must call `poll` on it
## (or a function which does so for you such as ``waitFor`` or ``runForever``) ## (or a function which does so for you such as `waitFor` or `runForever`)
## in order to poll for any outstanding events. The underlying implementation ## in order to poll for any outstanding events. The underlying implementation
## is based on epoll on Linux, IO Completion Ports on Windows and select on ## is based on epoll on Linux, IO Completion Ports on Windows and select on
## other operating systems. ## other operating systems.
## ##
## The ``poll`` function will not, on its own, return any events. Instead ## The `poll` function will not, on its own, return any events. Instead
## an appropriate ``Future`` object will be completed. A ``Future`` is a ## an appropriate `Future` object will be completed. A `Future` is a
## type which holds a value which is not yet available, but which *may* be ## type which holds a value which is not yet available, but which *may* be
## available in the future. You can check whether a future is finished ## available in the future. You can check whether a future is finished
## by using the ``finished`` function. When a future is finished it means that ## by using the `finished` function. When a future is finished it means that
## either the value that it holds is now available or it holds an error instead. ## either the value that it holds is now available or it holds an error instead.
## The latter situation occurs when the operation to complete a future fails ## The latter situation occurs when the operation to complete a future fails
## with an exception. You can distinguish between the two situations with the ## with an exception. You can distinguish between the two situations with the
## ``failed`` function. ## `failed` function.
## ##
## Future objects can also store a callback procedure which will be called ## Future objects can also store a callback procedure which will be called
## automatically once the future completes. ## automatically once the future completes.
@ -35,9 +35,9 @@
## pattern. In this ## pattern. In this
## pattern you make a request for an action, and once that action is fulfilled ## pattern you make a request for an action, and once that action is fulfilled
## a future is completed with the result of that action. Requests can be ## a future is completed with the result of that action. Requests can be
## made by calling the appropriate functions. For example: calling the ``recv`` ## made by calling the appropriate functions. For example: calling the `recv`
## function will create a request for some data to be read from a socket. The ## function will create a request for some data to be read from a socket. The
## future which the ``recv`` function returns will then complete once the ## future which the `recv` function returns will then complete once the
## requested amount of data is read **or** an exception occurs. ## requested amount of data is read **or** an exception occurs.
## ##
## Code to read some data from a socket may look something like this: ## Code to read some data from a socket may look something like this:
@ -49,18 +49,18 @@
## echo(future.read) ## echo(future.read)
## ) ## )
## ##
## All asynchronous functions returning a ``Future`` will not block. They ## All asynchronous functions returning a `Future` will not block. They
## will not however return immediately. An asynchronous function will have ## will not however return immediately. An asynchronous function will have
## code which will be executed before an asynchronous request is made, in most ## code which will be executed before an asynchronous request is made, in most
## cases this code sets up the request. ## cases this code sets up the request.
## ##
## In the above example, the ``recv`` function will return a brand new ## In the above example, the `recv` function will return a brand new
## ``Future`` instance once the request for data to be read from the socket ## `Future` instance once the request for data to be read from the socket
## is made. This ``Future`` instance will complete once the requested amount ## is made. This `Future` instance will complete once the requested amount
## of data is read, in this case it is 100 bytes. The second line sets a ## of data is read, in this case it is 100 bytes. The second line sets a
## callback on this future which will be called once the future completes. ## callback on this future which will be called once the future completes.
## All the callback does is write the data stored in the future to ``stdout``. ## All the callback does is write the data stored in the future to `stdout`.
## The ``read`` function is used for this and it checks whether the future ## The `read` function is used for this and it checks whether the future
## completes with an error for you (if it did it will simply raise the ## completes with an error for you (if it did it will simply raise the
## error), if there is no error however it returns the value of the future. ## error), if there is no error however it returns the value of the future.
## ##
@ -71,14 +71,14 @@
## this by allowing you to write asynchronous code the same way as you would ## this by allowing you to write asynchronous code the same way as you would
## write synchronous code. ## write synchronous code.
## ##
## An asynchronous procedure is marked using the ``{.async.}`` pragma. ## An asynchronous procedure is marked using the `{.async.}` pragma.
## When marking a procedure with the ``{.async.}`` pragma it must have a ## When marking a procedure with the `{.async.}` pragma it must have a
## ``Future[T]`` return type or no return type at all. If you do not specify ## `Future[T]` return type or no return type at all. If you do not specify
## a return type then ``Future[void]`` is assumed. ## a return type then `Future[void]` is assumed.
## ##
## Inside asynchronous procedures ``await`` can be used to call any ## Inside asynchronous procedures `await` can be used to call any
## procedures which return a ## procedures which return a
## ``Future``; this includes asynchronous procedures. When a procedure is ## `Future`; this includes asynchronous procedures. When a procedure is
## "awaited", the asynchronous procedure it is awaited in will ## "awaited", the asynchronous procedure it is awaited in will
## suspend its execution ## suspend its execution
## until the awaited procedure's Future completes. At which point the ## until the awaited procedure's Future completes. At which point the
@ -86,23 +86,23 @@
## when an asynchronous procedure is suspended other asynchronous procedures ## when an asynchronous procedure is suspended other asynchronous procedures
## will be run by the dispatcher. ## will be run by the dispatcher.
## ##
## The ``await`` call may be used in many contexts. It can be used on the right ## The `await` call may be used in many contexts. It can be used on the right
## hand side of a variable declaration: ``var data = await socket.recv(100)``, ## hand side of a variable declaration: `var data = await socket.recv(100)`,
## in which case the variable will be set to the value of the future ## in which case the variable will be set to the value of the future
## automatically. It can be used to await a ``Future`` object, and it can ## automatically. It can be used to await a `Future` object, and it can
## be used to await a procedure returning a ``Future[void]``: ## be used to await a procedure returning a `Future[void]`:
## ``await socket.send("foobar")``. ## `await socket.send("foobar")`.
## ##
## If an awaited future completes with an error, then ``await`` will re-raise ## If an awaited future completes with an error, then `await` will re-raise
## this error. To avoid this, you can use the ``yield`` keyword instead of ## this error. To avoid this, you can use the `yield` keyword instead of
## ``await``. The following section shows different ways that you can handle ## `await`. The following section shows different ways that you can handle
## exceptions in async procs. ## exceptions in async procs.
## ##
## Handling Exceptions ## Handling Exceptions
## ------------------- ## -------------------
## ##
## The most reliable way to handle exceptions is to use ``yield`` on a future ## The most reliable way to handle exceptions is to use `yield` on a future
## then check the future's ``failed`` property. For example: ## then check the future's `failed` property. For example:
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## var future = sock.recv(100) ## var future = sock.recv(100)
@ -110,7 +110,7 @@
## if future.failed: ## if future.failed:
## # Handle exception ## # Handle exception
## ##
## The ``async`` procedures also offer limited support for the try statement. ## The `async` procedures also offer limited support for the try statement.
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## try: ## try:
@ -129,9 +129,9 @@
## ##
## Futures should **never** be discarded. This is because they may contain ## Futures should **never** be discarded. This is because they may contain
## errors. If you do not care for the result of a Future then you should ## errors. If you do not care for the result of a Future then you should
## use the ``asyncCheck`` procedure instead of the ``discard`` keyword. Note ## use the `asyncCheck` procedure instead of the `discard` keyword. Note
## however that this does not wait for completion, and you should use ## however that this does not wait for completion, and you should use
## ``waitFor`` for that purpose. ## `waitFor` for that purpose.
## ##
## Examples ## Examples
## ======== ## ========
@ -144,14 +144,14 @@
## ============================= ## =============================
## ##
## It's possible to get into a situation where an async proc, or more accurately ## It's possible to get into a situation where an async proc, or more accurately
## a ``Future[T]`` gets stuck and ## a `Future[T]` gets stuck and
## never completes. This can happen for various reasons and can cause serious ## never completes. This can happen for various reasons and can cause serious
## memory leaks. When this occurs it's hard to identify the procedure that is ## memory leaks. When this occurs it's hard to identify the procedure that is
## stuck. ## stuck.
## ##
## Thankfully there is a mechanism which tracks the count of each pending future. ## Thankfully there is a mechanism which tracks the count of each pending future.
## All you need to do to enable it is compile with ``-d:futureLogging`` and ## All you need to do to enable it is compile with `-d:futureLogging` and
## use the ``getFuturesInProgress`` procedure to get the list of pending futures ## use the `getFuturesInProgress` procedure to get the list of pending futures
## together with the stack traces to the moment of their creation. ## together with the stack traces to the moment of their creation.
## ##
## You may also find it useful to use this ## You may also find it useful to use this
@ -164,7 +164,7 @@
## Limitations/Bugs ## Limitations/Bugs
## ================ ## ================
## ##
## * The effect system (``raises: []``) does not work with async procedures. ## * The effect system (`raises: []`) does not work with async procedures.
import os, tables, strutils, times, heapqueue, options, asyncstreams import os, tables, strutils, times, heapqueue, options, asyncstreams
import options, math, std/monotimes import options, math, std/monotimes
@ -232,7 +232,7 @@ template implementSetInheritable() {.dirty.} =
when declared(setInheritable): when declared(setInheritable):
proc setInheritable*(fd: AsyncFD, inheritable: bool): bool = proc setInheritable*(fd: AsyncFD, inheritable: bool): bool =
## Control whether a file handle can be inherited by child processes. ## Control whether a file handle can be inherited by child processes.
## Returns ``true`` on success. ## Returns `true` on success.
## ##
## This procedure is not guaranteed to be available for all platforms. ## This procedure is not guaranteed to be available for all platforms.
## Test for availability with `declared() <system.html#declared,untyped>`_. ## Test for availability with `declared() <system.html#declared,untyped>`_.
@ -307,7 +307,7 @@ when defined(windows) or defined(nimdoc):
return disp.ioPort return disp.ioPort
proc register*(fd: AsyncFD) = proc register*(fd: AsyncFD) =
## Registers ``fd`` with the dispatcher. ## Registers `fd` with the dispatcher.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if createIoCompletionPort(fd.Handle, p.ioPort, if createIoCompletionPort(fd.Handle, p.ioPort,
@ -430,16 +430,16 @@ when defined(windows) or defined(nimdoc):
proc recv*(socket: AsyncFD, size: int, proc recv*(socket: AsyncFD, size: int,
flags = {SocketFlag.SafeDisconn}): owned(Future[string]) = flags = {SocketFlag.SafeDisconn}): owned(Future[string]) =
## Reads **up to** ``size`` bytes from ``socket``. Returned future will ## Reads **up to** `size` bytes from `socket`. Returned future will
## complete once all the data requested is read, a part of the data has been ## complete once all the data requested is read, a part of the data has been
## read, or the socket has disconnected in which case the future will ## read, or the socket has disconnected in which case the future will
## complete with a value of ``""``. ## complete with a value of `""`.
## ##
## **Warning**: The ``Peek`` socket flag is not supported on Windows. ## **Warning**: The `Peek` socket flag is not supported on Windows.
# Things to note: # Things to note:
# * When WSARecv completes immediately then ``bytesReceived`` is very # * When WSARecv completes immediately then `bytesReceived` is very
# unreliable. # unreliable.
# * Still need to implement message-oriented socket disconnection, # * Still need to implement message-oriented socket disconnection,
# '\0' in the message currently signifies a socket disconnect. Who # '\0' in the message currently signifies a socket disconnect. Who
@ -503,17 +503,17 @@ when defined(windows) or defined(nimdoc):
proc recvInto*(socket: AsyncFD, buf: pointer, size: int, proc recvInto*(socket: AsyncFD, buf: pointer, size: int,
flags = {SocketFlag.SafeDisconn}): owned(Future[int]) = flags = {SocketFlag.SafeDisconn}): owned(Future[int]) =
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must ## Reads **up to** `size` bytes from `socket` into `buf`, which must
## at least be of that size. Returned future will complete once all the ## at least be of that size. Returned future will complete once all the
## data requested is read, a part of the data has been read, or the socket ## data requested is read, a part of the data has been read, or the socket
## has disconnected in which case the future will complete with a value of ## has disconnected in which case the future will complete with a value of
## ``0``. ## `0`.
## ##
## **Warning**: The ``Peek`` socket flag is not supported on Windows. ## **Warning**: The `Peek` socket flag is not supported on Windows.
# Things to note: # Things to note:
# * When WSARecv completes immediately then ``bytesReceived`` is very # * When WSARecv completes immediately then `bytesReceived` is very
# unreliable. # unreliable.
# * Still need to implement message-oriented socket disconnection, # * Still need to implement message-oriented socket disconnection,
# '\0' in the message currently signifies a socket disconnect. Who # '\0' in the message currently signifies a socket disconnect. Who
@ -569,10 +569,10 @@ when defined(windows) or defined(nimdoc):
proc send*(socket: AsyncFD, buf: pointer, size: int, proc send*(socket: AsyncFD, buf: pointer, size: int,
flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = flags = {SocketFlag.SafeDisconn}): owned(Future[void]) =
## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future ## Sends `size` bytes from `buf` to `socket`. The returned future
## will complete once all data has been sent. ## will complete once all data has been sent.
## ##
## **WARNING**: Use it with caution. If ``buf`` refers to GC'ed object, ## **WARNING**: Use it with caution. If `buf` refers to GC'ed object,
## you must use GC_ref/GC_unref calls to avoid early freeing of the buffer. ## you must use GC_ref/GC_unref calls to avoid early freeing of the buffer.
verifyPresence(socket) verifyPresence(socket)
var retFuture = newFuture[void]("send") var retFuture = newFuture[void]("send")
@ -607,16 +607,16 @@ when defined(windows) or defined(nimdoc):
retFuture.fail(newException(OSError, osErrorMsg(err))) retFuture.fail(newException(OSError, osErrorMsg(err)))
else: else:
retFuture.complete() retFuture.complete()
# We don't deallocate ``ol`` here because even though this completed # We don't deallocate `ol` here because even though this completed
# immediately poll will still be notified about its completion and it will # immediately poll will still be notified about its completion and it will
# free ``ol``. # free `ol`.
return retFuture return retFuture
proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr, proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr,
saddrLen: SockLen, saddrLen: SockLen,
flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = flags = {SocketFlag.SafeDisconn}): owned(Future[void]) =
## Sends ``data`` to specified destination ``saddr``, using ## Sends `data` to specified destination `saddr`, using
## socket ``socket``. The returned future will complete once all data ## socket `socket`. The returned future will complete once all data
## has been sent. ## has been sent.
verifyPresence(socket) verifyPresence(socket)
var retFuture = newFuture[void]("sendTo") var retFuture = newFuture[void]("sendTo")
@ -652,17 +652,17 @@ when defined(windows) or defined(nimdoc):
retFuture.fail(newException(OSError, osErrorMsg(err))) retFuture.fail(newException(OSError, osErrorMsg(err)))
else: else:
retFuture.complete() retFuture.complete()
# We don't deallocate ``ol`` here because even though this completed # We don't deallocate `ol` here because even though this completed
# immediately poll will still be notified about its completion and it will # immediately poll will still be notified about its completion and it will
# free ``ol``. # free `ol`.
return retFuture return retFuture
proc recvFromInto*(socket: AsyncFD, data: pointer, size: int, proc recvFromInto*(socket: AsyncFD, data: pointer, size: int,
saddr: ptr SockAddr, saddrLen: ptr SockLen, saddr: ptr SockAddr, saddrLen: ptr SockLen,
flags = {SocketFlag.SafeDisconn}): owned(Future[int]) = flags = {SocketFlag.SafeDisconn}): owned(Future[int]) =
## Receives a datagram data from ``socket`` into ``buf``, which must ## Receives a datagram data from `socket` into `buf`, which must
## be at least of size ``size``, address of datagram's sender will be ## be at least of size `size`, address of datagram's sender will be
## stored into ``saddr`` and ``saddrLen``. Returned future will complete ## stored into `saddr` and `saddrLen`. Returned future will complete
## once one datagram has been received, and will return size of packet ## once one datagram has been received, and will return size of packet
## received. ## received.
verifyPresence(socket) verifyPresence(socket)
@ -715,11 +715,11 @@ when defined(windows) or defined(nimdoc):
## The resulting client socket is automatically registered to the ## The resulting client socket is automatically registered to the
## dispatcher. ## dispatcher.
## ##
## If ``inheritable`` is false (the default), the resulting client socket will ## If `inheritable` is false (the default), the resulting client socket will
## not be inheritable by child processes. ## not be inheritable by child processes.
## ##
## The ``accept`` call may result in an error if the connecting socket ## The `accept` call may result in an error if the connecting socket
## disconnects during the duration of the ``accept``. If the ``SafeDisconn`` ## disconnects during the duration of the `accept`. If the `SafeDisconn`
## flag is specified then this error will not be raised and instead ## flag is specified then this error will not be raised and instead
## accept will be called again. ## accept will be called again.
verifyPresence(socket) verifyPresence(socket)
@ -796,9 +796,9 @@ when defined(windows) or defined(nimdoc):
GC_unref(ol) GC_unref(ol)
else: else:
completeAccept() completeAccept()
# We don't deallocate ``ol`` here because even though this completed # We don't deallocate `ol` here because even though this completed
# immediately poll will still be notified about its completion and it will # immediately poll will still be notified about its completion and it will
# free ``ol``. # free `ol`.
return retFuture return retFuture
@ -810,7 +810,7 @@ when defined(windows) or defined(nimdoc):
getGlobalDispatcher().handles.excl(socket) getGlobalDispatcher().handles.excl(socket)
proc unregister*(fd: AsyncFD) = proc unregister*(fd: AsyncFD) =
## Unregisters ``fd``. ## Unregisters `fd`.
getGlobalDispatcher().handles.excl(fd) getGlobalDispatcher().handles.excl(fd)
proc contains*(disp: PDispatcher, fd: AsyncFD): bool = proc contains*(disp: PDispatcher, fd: AsyncFD): bool =
@ -910,9 +910,9 @@ when defined(windows) or defined(nimdoc):
proc addRead*(fd: AsyncFD, cb: Callback) = proc addRead*(fd: AsyncFD, cb: Callback) =
## Start watching the file descriptor for read availability and then call ## Start watching the file descriptor for read availability and then call
## the callback ``cb``. ## the callback `cb`.
## ##
## This is not ``pure`` mechanism for Windows Completion Ports (IOCP), ## This is not `pure` mechanism for Windows Completion Ports (IOCP),
## so if you can avoid it, please do it. Use `addRead` only if really ## so if you can avoid it, please do it. Use `addRead` only if really
## need it (main usecase is adaptation of unix-like libraries to be ## need it (main usecase is adaptation of unix-like libraries to be
## asynchronous on Windows). ## asynchronous on Windows).
@ -921,16 +921,16 @@ when defined(windows) or defined(nimdoc):
## or asyncdispatch.accept(), because they are using IOCP, please use ## or asyncdispatch.accept(), because they are using IOCP, please use
## nativesockets.recv() and nativesockets.accept() instead. ## nativesockets.recv() and nativesockets.accept() instead.
## ##
## Be sure your callback ``cb`` returns ``true``, if you want to remove ## Be sure your callback `cb` returns `true`, if you want to remove
## watch of `read` notifications, and ``false``, if you want to continue ## watch of `read` notifications, and `false`, if you want to continue
## receiving notifications. ## receiving notifications.
registerWaitableEvent(fd, cb, FD_READ or FD_ACCEPT or FD_OOB or FD_CLOSE) registerWaitableEvent(fd, cb, FD_READ or FD_ACCEPT or FD_OOB or FD_CLOSE)
proc addWrite*(fd: AsyncFD, cb: Callback) = proc addWrite*(fd: AsyncFD, cb: Callback) =
## Start watching the file descriptor for write availability and then call ## Start watching the file descriptor for write availability and then call
## the callback ``cb``. ## the callback `cb`.
## ##
## This is not ``pure`` mechanism for Windows Completion Ports (IOCP), ## This is not `pure` mechanism for Windows Completion Ports (IOCP),
## so if you can avoid it, please do it. Use `addWrite` only if really ## so if you can avoid it, please do it. Use `addWrite` only if really
## need it (main usecase is adaptation of unix-like libraries to be ## need it (main usecase is adaptation of unix-like libraries to be
## asynchronous on Windows). ## asynchronous on Windows).
@ -939,8 +939,8 @@ when defined(windows) or defined(nimdoc):
## or asyncdispatch.connect(), because they are using IOCP, please use ## or asyncdispatch.connect(), because they are using IOCP, please use
## nativesockets.send() and nativesockets.connect() instead. ## nativesockets.send() and nativesockets.connect() instead.
## ##
## Be sure your callback ``cb`` returns ``true``, if you want to remove ## Be sure your callback `cb` returns `true`, if you want to remove
## watch of `write` notifications, and ``false``, if you want to continue ## watch of `write` notifications, and `false`, if you want to continue
## receiving notifications. ## receiving notifications.
registerWaitableEvent(fd, cb, FD_WRITE or FD_CONNECT or FD_CLOSE) registerWaitableEvent(fd, cb, FD_WRITE or FD_CONNECT or FD_CLOSE)
@ -980,12 +980,12 @@ when defined(windows) or defined(nimdoc):
raiseOSError(osLastError()) raiseOSError(osLastError())
proc addTimer*(timeout: int, oneshot: bool, cb: Callback) = proc addTimer*(timeout: int, oneshot: bool, cb: Callback) =
## Registers callback ``cb`` to be called when timer expired. ## Registers callback `cb` to be called when timer expired.
## ##
## Parameters: ## Parameters:
## ##
## * ``timeout`` - timeout value in milliseconds. ## * `timeout` - timeout value in milliseconds.
## * ``oneshot`` ## * `oneshot`
## * `true` - generate only one timeout event ## * `true` - generate only one timeout event
## * `false` - generate timeout events periodically ## * `false` - generate timeout events periodically
@ -1014,8 +1014,8 @@ when defined(windows) or defined(nimdoc):
registerWaitableHandle(p, hEvent, flags, pcd, timeout, timercb) registerWaitableHandle(p, hEvent, flags, pcd, timeout, timercb)
proc addProcess*(pid: int, cb: Callback) = proc addProcess*(pid: int, cb: Callback) =
## Registers callback ``cb`` to be called when process with process ID ## Registers callback `cb` to be called when process with process ID
## ``pid`` exited. ## `pid` exited.
const NULL = Handle(0) const NULL = Handle(0)
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
let procFlags = SYNCHRONIZE let procFlags = SYNCHRONIZE
@ -1033,10 +1033,10 @@ when defined(windows) or defined(nimdoc):
registerWaitableHandle(p, hProcess, flags, pcd, INFINITE, proccb) registerWaitableHandle(p, hProcess, flags, pcd, INFINITE, proccb)
proc newAsyncEvent*(): AsyncEvent = proc newAsyncEvent*(): AsyncEvent =
## Creates a new thread-safe ``AsyncEvent`` object. ## Creates a new thread-safe `AsyncEvent` object.
## ##
## New ``AsyncEvent`` object is not automatically registered with ## New `AsyncEvent` object is not automatically registered with
## dispatcher like ``AsyncSocket``. ## dispatcher like `AsyncSocket`.
var sa = SECURITY_ATTRIBUTES( var sa = SECURITY_ATTRIBUTES(
nLength: sizeof(SECURITY_ATTRIBUTES).cint, nLength: sizeof(SECURITY_ATTRIBUTES).cint,
bInheritHandle: 1 bInheritHandle: 1
@ -1048,12 +1048,12 @@ when defined(windows) or defined(nimdoc):
result.hEvent = event result.hEvent = event
proc trigger*(ev: AsyncEvent) = proc trigger*(ev: AsyncEvent) =
## Set event ``ev`` to signaled state. ## Set event `ev` to signaled state.
if setEvent(ev.hEvent) == 0: if setEvent(ev.hEvent) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
proc unregister*(ev: AsyncEvent) = proc unregister*(ev: AsyncEvent) =
## Unregisters event ``ev``. ## Unregisters event `ev`.
doAssert(ev.hWaiter != 0, "Event is not registered in the queue!") doAssert(ev.hWaiter != 0, "Event is not registered in the queue!")
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
p.handles.excl(AsyncFD(ev.hEvent)) p.handles.excl(AsyncFD(ev.hEvent))
@ -1064,14 +1064,14 @@ when defined(windows) or defined(nimdoc):
ev.hWaiter = 0 ev.hWaiter = 0
proc close*(ev: AsyncEvent) = proc close*(ev: AsyncEvent) =
## Closes event ``ev``. ## Closes event `ev`.
let res = closeHandle(ev.hEvent) let res = closeHandle(ev.hEvent)
deallocShared(cast[pointer](ev)) deallocShared(cast[pointer](ev))
if res == 0: if res == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
proc addEvent*(ev: AsyncEvent, cb: Callback) = proc addEvent*(ev: AsyncEvent, cb: Callback) =
## Registers callback ``cb`` to be called when ``ev`` will be signaled ## Registers callback `cb` to be called when `ev` will be signaled
doAssert(ev.hWaiter == 0, "Event is already registered in the queue!") doAssert(ev.hWaiter == 0, "Event is already registered in the queue!")
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
@ -1460,8 +1460,8 @@ else:
proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr, proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr,
saddrLen: SockLen, saddrLen: SockLen,
flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = flags = {SocketFlag.SafeDisconn}): owned(Future[void]) =
## Sends ``data`` of size ``size`` in bytes to specified destination ## Sends `data` of size `size` in bytes to specified destination
## (``saddr`` of size ``saddrLen`` in bytes, using socket ``socket``. ## (`saddr` of size `saddrLen` in bytes, using socket `socket`.
## The returned future will complete once all data has been sent. ## The returned future will complete once all data has been sent.
var retFuture = newFuture[void]("sendTo") var retFuture = newFuture[void]("sendTo")
@ -1491,9 +1491,9 @@ else:
proc recvFromInto*(socket: AsyncFD, data: pointer, size: int, proc recvFromInto*(socket: AsyncFD, data: pointer, size: int,
saddr: ptr SockAddr, saddrLen: ptr SockLen, saddr: ptr SockAddr, saddrLen: ptr SockLen,
flags = {SocketFlag.SafeDisconn}): owned(Future[int]) = flags = {SocketFlag.SafeDisconn}): owned(Future[int]) =
## Receives a datagram data from ``socket`` into ``data``, which must ## Receives a datagram data from `socket` into `data`, which must
## be at least of size ``size`` in bytes, address of datagram's sender ## be at least of size `size` in bytes, address of datagram's sender
## will be stored into ``saddr`` and ``saddrLen``. Returned future will ## will be stored into `saddr` and `saddrLen`. Returned future will
## complete once one datagram has been received, and will return size ## complete once one datagram has been received, and will return size
## of packet received. ## of packet received.
var retFuture = newFuture[int]("recvFromInto") var retFuture = newFuture[int]("recvFromInto")
@ -1563,45 +1563,45 @@ else:
proc addTimer*(timeout: int, oneshot: bool, cb: Callback) = proc addTimer*(timeout: int, oneshot: bool, cb: Callback) =
## Start watching for timeout expiration, and then call the ## Start watching for timeout expiration, and then call the
## callback ``cb``. ## callback `cb`.
## ``timeout`` - time in milliseconds, ## `timeout` - time in milliseconds,
## ``oneshot`` - if ``true`` only one event will be dispatched, ## `oneshot` - if `true` only one event will be dispatched,
## if ``false`` continuous events every ``timeout`` milliseconds. ## if `false` continuous events every `timeout` milliseconds.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var data = newAsyncData() var data = newAsyncData()
data.readList.add(cb) data.readList.add(cb)
p.selector.registerTimer(timeout, oneshot, data) p.selector.registerTimer(timeout, oneshot, data)
proc addSignal*(signal: int, cb: Callback) = proc addSignal*(signal: int, cb: Callback) =
## Start watching signal ``signal``, and when signal appears, call the ## Start watching signal `signal`, and when signal appears, call the
## callback ``cb``. ## callback `cb`.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var data = newAsyncData() var data = newAsyncData()
data.readList.add(cb) data.readList.add(cb)
p.selector.registerSignal(signal, data) p.selector.registerSignal(signal, data)
proc addProcess*(pid: int, cb: Callback) = proc addProcess*(pid: int, cb: Callback) =
## Start watching for process exit with pid ``pid``, and then call ## Start watching for process exit with pid `pid`, and then call
## the callback ``cb``. ## the callback `cb`.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var data = newAsyncData() var data = newAsyncData()
data.readList.add(cb) data.readList.add(cb)
p.selector.registerProcess(pid, data) p.selector.registerProcess(pid, data)
proc newAsyncEvent*(): AsyncEvent = proc newAsyncEvent*(): AsyncEvent =
## Creates new ``AsyncEvent``. ## Creates new `AsyncEvent`.
result = AsyncEvent(newSelectEvent()) result = AsyncEvent(newSelectEvent())
proc trigger*(ev: AsyncEvent) = proc trigger*(ev: AsyncEvent) =
## Sets new ``AsyncEvent`` to signaled state. ## Sets new `AsyncEvent` to signaled state.
trigger(SelectEvent(ev)) trigger(SelectEvent(ev))
proc close*(ev: AsyncEvent) = proc close*(ev: AsyncEvent) =
## Closes ``AsyncEvent`` ## Closes `AsyncEvent`
close(SelectEvent(ev)) close(SelectEvent(ev))
proc addEvent*(ev: AsyncEvent, cb: Callback) = proc addEvent*(ev: AsyncEvent, cb: Callback) =
## Start watching for event ``ev``, and call callback ``cb``, when ## Start watching for event `ev`, and call callback `cb`, when
## ev will be set to signaled state. ## ev will be set to signaled state.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var data = newAsyncData() var data = newAsyncData()
@ -1609,8 +1609,8 @@ else:
p.selector.registerEvent(SelectEvent(ev), data) p.selector.registerEvent(SelectEvent(ev), data)
proc drain*(timeout = 500) = proc drain*(timeout = 500) =
## Waits for completion of **all** events and processes them. Raises ``ValueError`` ## Waits for completion of **all** events and processes them. Raises `ValueError`
## if there are no pending operations. In contrast to ``poll`` this ## if there are no pending operations. In contrast to `poll` this
## processes as many events as are available until the timeout has elapsed. ## processes as many events as are available until the timeout has elapsed.
var curTimeout = timeout var curTimeout = timeout
let start = now() let start = now()
@ -1621,7 +1621,7 @@ proc drain*(timeout = 500) =
break break
proc poll*(timeout = 500) = proc poll*(timeout = 500) =
## Waits for completion events and processes them. Raises ``ValueError`` ## Waits for completion events and processes them. Raises `ValueError`
## if there are no pending operations. This runs the underlying OS ## if there are no pending operations. This runs the underlying OS
## `epoll`:idx: or `kqueue`:idx: primitive only once. ## `epoll`:idx: or `kqueue`:idx: primitive only once.
discard runOnce(timeout) discard runOnce(timeout)
@ -1686,13 +1686,13 @@ when defined(windows) or defined(nimdoc):
if ret: if ret:
# Request to connect completed immediately. # Request to connect completed immediately.
retFuture.complete() retFuture.complete()
# We don't deallocate ``ol`` here because even though this completed # We don't deallocate `ol` here because even though this completed
# immediately poll will still be notified about its completion and it # immediately poll will still be notified about its completion and it
# will free ``ol``. # will free `ol`.
else: else:
let lastError = osLastError() let lastError = osLastError()
if lastError.int32 != ERROR_IO_PENDING: if lastError.int32 != ERROR_IO_PENDING:
# With ERROR_IO_PENDING ``ol`` will be deallocated in ``poll``, # With ERROR_IO_PENDING `ol` will be deallocated in `poll`,
# and the future will be completed/failed there, too. # and the future will be completed/failed there, too.
GC_unref(ol) GC_unref(ol)
retFuture.fail(newException(OSError, osErrorMsg(lastError))) retFuture.fail(newException(OSError, osErrorMsg(lastError)))
@ -1802,9 +1802,9 @@ template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped,
proc dial*(address: string, port: Port, proc dial*(address: string, port: Port,
protocol: Protocol = IPPROTO_TCP): owned(Future[AsyncFD]) = protocol: Protocol = IPPROTO_TCP): owned(Future[AsyncFD]) =
## Establishes connection to the specified ``address``:``port`` pair via the ## Establishes connection to the specified `address`:`port` pair via the
## specified protocol. The procedure iterates through possible ## specified protocol. The procedure iterates through possible
## resolutions of the ``address`` until it succeeds, meaning that it ## resolutions of the `address` until it succeeds, meaning that it
## seamlessly works with both IPv4 and IPv6. ## seamlessly works with both IPv4 and IPv6.
## Returns the async file descriptor, registered in the dispatcher of ## Returns the async file descriptor, registered in the dispatcher of
## the current thread, ready to send or receive data. ## the current thread, ready to send or receive data.
@ -1832,7 +1832,7 @@ proc connect*(socket: AsyncFD, address: string, port: Port,
proc sleepAsync*(ms: int | float): owned(Future[void]) = proc sleepAsync*(ms: int | float): owned(Future[void]) =
## Suspends the execution of the current async procedure for the next ## Suspends the execution of the current async procedure for the next
## ``ms`` milliseconds. ## `ms` milliseconds.
var retFuture = newFuture[void]("sleepAsync") var retFuture = newFuture[void]("sleepAsync")
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
when ms is int: when ms is int:
@ -1843,11 +1843,11 @@ proc sleepAsync*(ms: int | float): owned(Future[void]) =
return retFuture return retFuture
proc withTimeout*[T](fut: Future[T], timeout: int): owned(Future[bool]) = proc withTimeout*[T](fut: Future[T], timeout: int): owned(Future[bool]) =
## Returns a future which will complete once ``fut`` completes or after ## Returns a future which will complete once `fut` completes or after
## ``timeout`` milliseconds has elapsed. ## `timeout` milliseconds has elapsed.
## ##
## If ``fut`` completes first the returned future will hold true, ## If `fut` completes first the returned future will hold true,
## otherwise, if ``timeout`` milliseconds has elapsed first, the returned ## otherwise, if `timeout` milliseconds has elapsed first, the returned
## future will hold false. ## future will hold false.
var retFuture = newFuture[bool]("asyncdispatch.`withTimeout`") var retFuture = newFuture[bool]("asyncdispatch.`withTimeout`")
@ -1870,7 +1870,7 @@ proc accept*(socket: AsyncFD,
## Accepts a new connection. Returns a future containing the client socket ## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection. ## corresponding to that connection.
## ##
## If ``inheritable`` is false (the default), the resulting client socket ## If `inheritable` is false (the default), the resulting client socket
## will not be inheritable by child processes. ## will not be inheritable by child processes.
## ##
## The future will complete when the connection is successfully accepted. ## The future will complete when the connection is successfully accepted.
@ -1890,7 +1890,7 @@ proc keepAlive(x: string) =
proc send*(socket: AsyncFD, data: string, proc send*(socket: AsyncFD, data: string,
flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = flags = {SocketFlag.SafeDisconn}): owned(Future[void]) =
## Sends ``data`` to ``socket``. The returned future will complete once all ## Sends `data` to `socket`. The returned future will complete once all
## data has been sent. ## data has been sent.
var retFuture = newFuture[void]("send") var retFuture = newFuture[void]("send")
if data.len > 0: if data.len > 0:

View file

@ -90,8 +90,8 @@ proc newAsyncFile*(fd: AsyncFD): AsyncFile =
register(fd) register(fd)
proc openAsync*(filename: string, mode = fmRead): AsyncFile = proc openAsync*(filename: string, mode = fmRead): AsyncFile =
## Opens a file specified by the path in ``filename`` using ## Opens a file specified by the path in `filename` using
## the specified FileMode ``mode`` asynchronously. ## the specified FileMode `mode` asynchronously.
when defined(windows) or defined(nimdoc): when defined(windows) or defined(nimdoc):
let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL
let desiredAccess = getDesiredAccess(mode) let desiredAccess = getDesiredAccess(mode)
@ -124,11 +124,11 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
result = newAsyncFile(fd.AsyncFD) result = newAsyncFile(fd.AsyncFD)
proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] = proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
## Read ``size`` bytes from the specified file asynchronously starting at ## Read `size` bytes from the specified file asynchronously starting at
## the current position of the file pointer. ## the current position of the file pointer.
## ##
## If the file pointer is past the end of the file then zero is returned ## If the file pointer is past the end of the file then zero is returned
## and no bytes are read into ``buf`` ## and no bytes are read into `buf`
var retFuture = newFuture[int]("asyncfile.readBuffer") var retFuture = newFuture[int]("asyncfile.readBuffer")
when defined(windows) or defined(nimdoc): when defined(windows) or defined(nimdoc):
@ -201,7 +201,7 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
return retFuture return retFuture
proc read*(f: AsyncFile, size: int): Future[string] = proc read*(f: AsyncFile, size: int): Future[string] =
## Read ``size`` bytes from the specified file asynchronously starting at ## Read `size` bytes from the specified file asynchronously starting at
## the current position of the file pointer. ## the current position of the file pointer.
## ##
## If the file pointer is past the end of the file then an empty string is ## If the file pointer is past the end of the file then an empty string is
@ -332,7 +332,7 @@ proc readAll*(f: AsyncFile): Future[string] {.async.} =
result.add data result.add data
proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] = proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
## Writes ``size`` bytes from ``buf`` to the file specified asynchronously. ## Writes `size` bytes from `buf` to the file specified asynchronously.
## ##
## The returned Future will complete once all data has been written to the ## The returned Future will complete once all data has been written to the
## specified file. ## specified file.
@ -401,7 +401,7 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
return retFuture return retFuture
proc write*(f: AsyncFile, data: string): Future[void] = proc write*(f: AsyncFile, data: string): Future[void] =
## Writes ``data`` to the file specified asynchronously. ## Writes `data` to the file specified asynchronously.
## ##
## The returned Future will complete once all data has been written to the ## The returned Future will complete once all data has been written to the
## specified file. ## specified file.

View file

@ -19,7 +19,7 @@
## =========================== ## ===========================
## ##
## In order to begin any sort of transfer of files you must first ## In order to begin any sort of transfer of files you must first
## connect to an FTP server. You can do so with the ``connect`` procedure. ## connect to an FTP server. You can do so with the `connect` procedure.
## ##
## .. code-block::nim ## .. code-block::nim
## import asyncdispatch, asyncftpclient ## import asyncdispatch, asyncftpclient
@ -29,16 +29,16 @@
## echo("Connected") ## echo("Connected")
## waitFor(main()) ## waitFor(main())
## ##
## A new ``main`` async procedure must be declared to allow the use of the ## A new `main` async procedure must be declared to allow the use of the
## ``await`` keyword. The connection will complete asynchronously and the ## `await` keyword. The connection will complete asynchronously and the
## client will be connected after the ``await ftp.connect()`` call. ## client will be connected after the `await ftp.connect()` call.
## ##
## Uploading a new file ## Uploading a new file
## ==================== ## ====================
## ##
## After a connection is made you can use the ``store`` procedure to upload ## After a connection is made you can use the `store` procedure to upload
## a new file to the FTP server. Make sure to check you are in the correct ## a new file to the FTP server. Make sure to check you are in the correct
## working directory before you do so with the ``pwd`` procedure, you can also ## working directory before you do so with the `pwd` procedure, you can also
## instead specify an absolute path. ## instead specify an absolute path.
## ##
## .. code-block::nim ## .. code-block::nim
@ -56,11 +56,11 @@
## ======================================== ## ========================================
## ##
## The progress of either a file upload or a file download can be checked ## The progress of either a file upload or a file download can be checked
## by specifying a ``onProgressChanged`` procedure to the ``store`` or ## by specifying a `onProgressChanged` procedure to the `store` or
## ``retrFile`` procedures. ## `retrFile` procedures.
## ##
## Procs that take an ``onProgressChanged`` callback will call this every ## Procs that take an `onProgressChanged` callback will call this every
## ``progressInterval`` milliseconds. ## `progressInterval` milliseconds.
## ##
## .. code-block::nim ## .. code-block::nim
## import asyncdispatch, asyncftpclient ## import asyncdispatch, asyncftpclient
@ -148,10 +148,10 @@ proc expectReply(ftp: AsyncFtpClient): Future[string] {.async.} =
proc send*(ftp: AsyncFtpClient, m: string): Future[string] {.async.} = proc send*(ftp: AsyncFtpClient, m: string): Future[string] {.async.} =
## Send a message to the server, and wait for a primary reply. ## Send a message to the server, and wait for a primary reply.
## ``\c\L`` is added for you. ## `\c\L` is added for you.
## ##
## You need to make sure that the message ``m`` doesn't contain any newline ## You need to make sure that the message `m` doesn't contain any newline
## characters. Failing to do so will raise ``AssertionDefect``. ## characters. Failing to do so will raise `AssertionDefect`.
## ##
## **Note:** The server may return multiple lines of coded replies. ## **Note:** The server may return multiple lines of coded replies.
doAssert(not m.contains({'\c', '\L'}), "message shouldn't contain any newline characters") doAssert(not m.contains({'\c', '\L'}), "message shouldn't contain any newline characters")
@ -183,7 +183,7 @@ proc normalizePathSep(path: string): string =
return replace(path, '\\', '/') return replace(path, '\\', '/')
proc connect*(ftp: AsyncFtpClient) {.async.} = proc connect*(ftp: AsyncFtpClient) {.async.} =
## Connect to the FTP server specified by ``ftp``. ## Connect to the FTP server specified by `ftp`.
await ftp.csock.connect(ftp.address, ftp.port) await ftp.csock.connect(ftp.address, ftp.port)
var reply = await ftp.expectReply() var reply = await ftp.expectReply()
@ -208,7 +208,7 @@ proc pwd*(ftp: AsyncFtpClient): Future[string] {.async.} =
return wd.captureBetween('"') # " return wd.captureBetween('"') # "
proc cd*(ftp: AsyncFtpClient, dir: string) {.async.} = proc cd*(ftp: AsyncFtpClient, dir: string) {.async.} =
## Changes the current directory on the remote FTP server to ``dir``. ## Changes the current directory on the remote FTP server to `dir`.
assertReply(await(ftp.send("CWD " & dir.normalizePathSep)), "250") assertReply(await(ftp.send("CWD " & dir.normalizePathSep)), "250")
proc cdup*(ftp: AsyncFtpClient) {.async.} = proc cdup*(ftp: AsyncFtpClient) {.async.} =
@ -229,10 +229,10 @@ proc getLines(ftp: AsyncFtpClient): Future[string] {.async.} =
assertReply(await(ftp.expectReply()), "226") assertReply(await(ftp.expectReply()), "226")
proc listDirs*(ftp: AsyncFtpClient, dir = ""): Future[seq[string]] {.async.} = proc listDirs*(ftp: AsyncFtpClient, dir = ""): Future[seq[string]] {.async.} =
## Returns a list of filenames in the given directory. If ``dir`` is "", ## Returns a list of filenames in the given directory. If `dir` is "",
## the current directory is used. If ``async`` is true, this ## the current directory is used. If `async` is true, this
## function will return immediately and it will be your job to ## function will return immediately and it will be your job to
## use asyncdispatch's ``poll`` to progress this operation. ## use asyncdispatch's `poll` to progress this operation.
await ftp.pasv() await ftp.pasv()
assertReply(await(ftp.send("NLST " & dir.normalizePathSep)), ["125", "150"]) assertReply(await(ftp.send("NLST " & dir.normalizePathSep)), ["125", "150"])
@ -240,15 +240,15 @@ proc listDirs*(ftp: AsyncFtpClient, dir = ""): Future[seq[string]] {.async.} =
result = splitLines(await ftp.getLines()) result = splitLines(await ftp.getLines())
proc fileExists*(ftp: AsyncFtpClient, file: string): Future[bool] {.async.} = proc fileExists*(ftp: AsyncFtpClient, file: string): Future[bool] {.async.} =
## Determines whether ``file`` exists. ## Determines whether `file` exists.
var files = await ftp.listDirs() var files = await ftp.listDirs()
for f in items(files): for f in items(files):
if f.normalizePathSep == file.normalizePathSep: return true if f.normalizePathSep == file.normalizePathSep: return true
proc createDir*(ftp: AsyncFtpClient, dir: string, recursive = false){.async.} = proc createDir*(ftp: AsyncFtpClient, dir: string, recursive = false){.async.} =
## Creates a directory ``dir``. If ``recursive`` is true, the topmost ## Creates a directory `dir`. If `recursive` is true, the topmost
## subdirectory of ``dir`` will be created first, following the secondmost... ## subdirectory of `dir` will be created first, following the secondmost...
## etc. this allows you to give a full path as the ``dir`` without worrying ## etc. this allows you to give a full path as the `dir` without worrying
## about subdirectories not existing. ## about subdirectories not existing.
if not recursive: if not recursive:
assertReply(await(ftp.send("MKD " & dir.normalizePathSep)), "257") assertReply(await(ftp.send("MKD " & dir.normalizePathSep)), "257")
@ -264,7 +264,7 @@ proc createDir*(ftp: AsyncFtpClient, dir: string, recursive = false){.async.} =
proc chmod*(ftp: AsyncFtpClient, path: string, proc chmod*(ftp: AsyncFtpClient, path: string,
permissions: set[FilePermission]) {.async.} = permissions: set[FilePermission]) {.async.} =
## Changes permission of ``path`` to ``permissions``. ## Changes permission of `path` to `permissions`.
var userOctal = 0 var userOctal = 0
var groupOctal = 0 var groupOctal = 0
var otherOctal = 0 var otherOctal = 0
@ -285,7 +285,7 @@ proc chmod*(ftp: AsyncFtpClient, path: string,
" " & path.normalizePathSep)), "200") " " & path.normalizePathSep)), "200")
proc list*(ftp: AsyncFtpClient, dir = ""): Future[string] {.async.} = proc list*(ftp: AsyncFtpClient, dir = ""): Future[string] {.async.} =
## Lists all files in ``dir``. If ``dir`` is ``""``, uses the current ## Lists all files in `dir`. If `dir` is `""`, uses the current
## working directory. ## working directory.
await ftp.pasv() await ftp.pasv()
@ -295,7 +295,7 @@ proc list*(ftp: AsyncFtpClient, dir = ""): Future[string] {.async.} =
result = await ftp.getLines() result = await ftp.getLines()
proc retrText*(ftp: AsyncFtpClient, file: string): Future[string] {.async.} = proc retrText*(ftp: AsyncFtpClient, file: string): Future[string] {.async.} =
## Retrieves ``file``. File must be ASCII text. ## Retrieves `file`. File must be ASCII text.
await ftp.pasv() await ftp.pasv()
let reply = await ftp.send("RETR " & file.normalizePathSep) let reply = await ftp.send("RETR " & file.normalizePathSep)
assertReply(reply, ["125", "150"]) assertReply(reply, ["125", "150"])
@ -331,17 +331,17 @@ proc getFile(ftp: AsyncFtpClient, file: File, total: BiggestInt,
proc defaultOnProgressChanged*(total, progress: BiggestInt, proc defaultOnProgressChanged*(total, progress: BiggestInt,
speed: float): Future[void] {.nimcall, gcsafe.} = speed: float): Future[void] {.nimcall, gcsafe.} =
## Default FTP ``onProgressChanged`` handler. Does nothing. ## Default FTP `onProgressChanged` handler. Does nothing.
result = newFuture[void]() result = newFuture[void]()
#echo(total, " ", progress, " ", speed) #echo(total, " ", progress, " ", speed)
result.complete() result.complete()
proc retrFile*(ftp: AsyncFtpClient, file, dest: string, proc retrFile*(ftp: AsyncFtpClient, file, dest: string,
onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} = onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
## Downloads ``file`` and saves it to ``dest``. ## Downloads `file` and saves it to `dest`.
## The ``EvRetr`` event is passed to the specified ``handleEvent`` function ## The `EvRetr` event is passed to the specified `handleEvent` function
## when the download is finished. The event's ``filename`` field will be equal ## when the download is finished. The event's `filename` field will be equal
## to ``file``. ## to `file`.
var destFile = open(dest, mode = fmWrite) var destFile = open(dest, mode = fmWrite)
await ftp.pasv() await ftp.pasv()
var reply = await ftp.send("RETR " & file.normalizePathSep) var reply = await ftp.send("RETR " & file.normalizePathSep)
@ -390,12 +390,12 @@ proc doUpload(ftp: AsyncFtpClient, file: File,
proc store*(ftp: AsyncFtpClient, file, dest: string, proc store*(ftp: AsyncFtpClient, file, dest: string,
onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} = onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this ## Uploads `file` to `dest` on the remote FTP server. Usage of this
## function asynchronously is recommended to view the progress of ## function asynchronously is recommended to view the progress of
## the download. ## the download.
## The ``EvStore`` event is passed to the specified ``handleEvent`` function ## The `EvStore` event is passed to the specified `handleEvent` function
## when the upload is finished, and the ``filename`` field will be ## when the upload is finished, and the `filename` field will be
## equal to ``file``. ## equal to `file`.
var destFile = open(file) var destFile = open(file)
await ftp.pasv() await ftp.pasv()
@ -406,21 +406,21 @@ proc store*(ftp: AsyncFtpClient, file, dest: string,
proc rename*(ftp: AsyncFtpClient, nameFrom: string, nameTo: string) {.async.} = proc rename*(ftp: AsyncFtpClient, nameFrom: string, nameTo: string) {.async.} =
## Rename a file or directory on the remote FTP Server from current name ## Rename a file or directory on the remote FTP Server from current name
## ``name_from`` to new name ``name_to`` ## `name_from` to new name `name_to`
assertReply(await ftp.send("RNFR " & nameFrom), "350") assertReply(await ftp.send("RNFR " & nameFrom), "350")
assertReply(await ftp.send("RNTO " & nameTo), "250") assertReply(await ftp.send("RNTO " & nameTo), "250")
proc removeFile*(ftp: AsyncFtpClient, filename: string) {.async.} = proc removeFile*(ftp: AsyncFtpClient, filename: string) {.async.} =
## Delete a file ``filename`` on the remote FTP server ## Delete a file `filename` on the remote FTP server
assertReply(await ftp.send("DELE " & filename), "250") assertReply(await ftp.send("DELE " & filename), "250")
proc removeDir*(ftp: AsyncFtpClient, dir: string) {.async.} = proc removeDir*(ftp: AsyncFtpClient, dir: string) {.async.} =
## Delete a directory ``dir`` on the remote FTP server ## Delete a directory `dir` on the remote FTP server
assertReply(await ftp.send("RMD " & dir), "250") assertReply(await ftp.send("RMD " & dir), "250")
proc newAsyncFtpClient*(address: string, port = Port(21), proc newAsyncFtpClient*(address: string, port = Port(21),
user, pass = "", progressInterval: int = 1000): AsyncFtpClient = user, pass = "", progressInterval: int = 1000): AsyncFtpClient =
## Creates a new ``AsyncFtpClient`` object. ## Creates a new `AsyncFtpClient` object.
new result new result
result.user = user result.user = user
result.pass = pass result.pass = pass

View file

@ -86,19 +86,19 @@ when isFutureLoggingEnabled:
var callSoonProc {.threadvar.}: proc (cbproc: proc ()) {.gcsafe.} var callSoonProc {.threadvar.}: proc (cbproc: proc ()) {.gcsafe.}
proc getCallSoonProc*(): (proc(cbproc: proc ()) {.gcsafe.}) = proc getCallSoonProc*(): (proc(cbproc: proc ()) {.gcsafe.}) =
## Get current implementation of ``callSoon``. ## Get current implementation of `callSoon`.
return callSoonProc return callSoonProc
proc setCallSoonProc*(p: (proc(cbproc: proc ()) {.gcsafe.})) = proc setCallSoonProc*(p: (proc(cbproc: proc ()) {.gcsafe.})) =
## Change current implementation of ``callSoon``. This is normally called when dispatcher from ``asyncdispatcher`` is initialized. ## Change current implementation of `callSoon`. This is normally called when dispatcher from `asyncdispatcher` is initialized.
callSoonProc = p callSoonProc = p
proc callSoon*(cbproc: proc ()) = proc callSoon*(cbproc: proc ()) =
## Call ``cbproc`` "soon". ## Call `cbproc` "soon".
## ##
## If async dispatcher is running, ``cbproc`` will be executed during next dispatcher tick. ## If async dispatcher is running, `cbproc` will be executed during next dispatcher tick.
## ##
## If async dispatcher is not running, ``cbproc`` will be executed immediately. ## If async dispatcher is not running, `cbproc` will be executed immediately.
if callSoonProc.isNil: if callSoonProc.isNil:
# Loop not initialized yet. Call the function directly to allow setup code to use futures. # Loop not initialized yet. Call the function directly to allow setup code to use futures.
cbproc() cbproc()
@ -117,29 +117,29 @@ template setupFutureBase(fromProc: string) =
proc newFuture*[T](fromProc: string = "unspecified"): owned(Future[T]) = proc newFuture*[T](fromProc: string = "unspecified"): owned(Future[T]) =
## Creates a new future. ## Creates a new future.
## ##
## Specifying ``fromProc``, which is a string specifying the name of the proc ## Specifying `fromProc`, which is a string specifying the name of the proc
## that this future belongs to, is a good habit as it helps with debugging. ## that this future belongs to, is a good habit as it helps with debugging.
setupFutureBase(fromProc) setupFutureBase(fromProc)
when isFutureLoggingEnabled: logFutureStart(result) when isFutureLoggingEnabled: logFutureStart(result)
proc newFutureVar*[T](fromProc = "unspecified"): owned(FutureVar[T]) = proc newFutureVar*[T](fromProc = "unspecified"): owned(FutureVar[T]) =
## Create a new ``FutureVar``. This Future type is ideally suited for ## Create a new `FutureVar`. This Future type is ideally suited for
## situations where you want to avoid unnecessary allocations of Futures. ## situations where you want to avoid unnecessary allocations of Futures.
## ##
## Specifying ``fromProc``, which is a string specifying the name of the proc ## Specifying `fromProc`, which is a string specifying the name of the proc
## that this future belongs to, is a good habit as it helps with debugging. ## that this future belongs to, is a good habit as it helps with debugging.
let fo = newFuture[T](fromProc) let fo = newFuture[T](fromProc)
result = typeof(result)(fo) result = typeof(result)(fo)
when isFutureLoggingEnabled: logFutureStart(Future[T](result)) when isFutureLoggingEnabled: logFutureStart(Future[T](result))
proc clean*[T](future: FutureVar[T]) = proc clean*[T](future: FutureVar[T]) =
## Resets the ``finished`` status of ``future``. ## Resets the `finished` status of `future`.
Future[T](future).finished = false Future[T](future).finished = false
Future[T](future).error = nil Future[T](future).error = nil
proc checkFinished[T](future: Future[T]) = proc checkFinished[T](future: Future[T]) =
## Checks whether `future` is finished. If it is then raises a ## Checks whether `future` is finished. If it is then raises a
## ``FutureError``. ## `FutureError`.
when not defined(release): when not defined(release):
if future.finished: if future.finished:
var msg = "" var msg = ""
@ -190,7 +190,7 @@ proc add(callbacks: var CallbackList, function: CallbackFunc) =
last.next = newCallback last.next = newCallback
proc complete*[T](future: Future[T], val: T) = proc complete*[T](future: Future[T], val: T) =
## Completes ``future`` with value ``val``. ## Completes `future` with value `val`.
#assert(not future.finished, "Future already finished, cannot finish twice.") #assert(not future.finished, "Future already finished, cannot finish twice.")
checkFinished(future) checkFinished(future)
assert(future.error == nil) assert(future.error == nil)
@ -200,7 +200,7 @@ proc complete*[T](future: Future[T], val: T) =
when isFutureLoggingEnabled: logFutureFinish(future) when isFutureLoggingEnabled: logFutureFinish(future)
proc complete*(future: Future[void]) = proc complete*(future: Future[void]) =
## Completes a void ``future``. ## Completes a void `future`.
#assert(not future.finished, "Future already finished, cannot finish twice.") #assert(not future.finished, "Future already finished, cannot finish twice.")
checkFinished(future) checkFinished(future)
assert(future.error == nil) assert(future.error == nil)
@ -209,7 +209,7 @@ proc complete*(future: Future[void]) =
when isFutureLoggingEnabled: logFutureFinish(future) when isFutureLoggingEnabled: logFutureFinish(future)
proc complete*[T](future: FutureVar[T]) = proc complete*[T](future: FutureVar[T]) =
## Completes a ``FutureVar``. ## Completes a `FutureVar`.
template fut: untyped = Future[T](future) template fut: untyped = Future[T](future)
checkFinished(fut) checkFinished(fut)
assert(fut.error == nil) assert(fut.error == nil)
@ -218,7 +218,7 @@ proc complete*[T](future: FutureVar[T]) =
when isFutureLoggingEnabled: logFutureFinish(Future[T](future)) when isFutureLoggingEnabled: logFutureFinish(Future[T](future))
proc complete*[T](future: FutureVar[T], val: T) = proc complete*[T](future: FutureVar[T], val: T) =
## Completes a ``FutureVar`` with value ``val``. ## Completes a `FutureVar` with value `val`.
## ##
## Any previously stored value will be overwritten. ## Any previously stored value will be overwritten.
template fut: untyped = Future[T](future) template fut: untyped = Future[T](future)
@ -230,7 +230,7 @@ proc complete*[T](future: FutureVar[T], val: T) =
when isFutureLoggingEnabled: logFutureFinish(future) when isFutureLoggingEnabled: logFutureFinish(future)
proc fail*[T](future: Future[T], error: ref Exception) = proc fail*[T](future: Future[T], error: ref Exception) =
## Completes ``future`` with ``error``. ## Completes `future` with `error`.
#assert(not future.finished, "Future already finished, cannot finish twice.") #assert(not future.finished, "Future already finished, cannot finish twice.")
checkFinished(future) checkFinished(future)
future.finished = true future.finished = true
@ -247,7 +247,7 @@ proc clearCallbacks*(future: FutureBase) =
proc addCallback*(future: FutureBase, cb: proc() {.closure, gcsafe.}) = proc addCallback*(future: FutureBase, cb: proc() {.closure, gcsafe.}) =
## Adds the callbacks proc to be called when the future completes. ## Adds the callbacks proc to be called when the future completes.
## ##
## If future has already completed then ``cb`` will be called immediately. ## If future has already completed then `cb` will be called immediately.
assert cb != nil assert cb != nil
if future.finished: if future.finished:
callSoon(cb) callSoon(cb)
@ -258,7 +258,7 @@ proc addCallback*[T](future: Future[T],
cb: proc (future: Future[T]) {.closure, gcsafe.}) = cb: proc (future: Future[T]) {.closure, gcsafe.}) =
## Adds the callbacks proc to be called when the future completes. ## Adds the callbacks proc to be called when the future completes.
## ##
## If future has already completed then ``cb`` will be called immediately. ## If future has already completed then `cb` will be called immediately.
future.addCallback( future.addCallback(
proc() = proc() =
cb(future) cb(future)
@ -267,9 +267,9 @@ proc addCallback*[T](future: Future[T],
proc `callback=`*(future: FutureBase, cb: proc () {.closure, gcsafe.}) = proc `callback=`*(future: FutureBase, cb: proc () {.closure, gcsafe.}) =
## Clears the list of callbacks and sets the callback proc to be called when the future completes. ## Clears the list of callbacks and sets the callback proc to be called when the future completes.
## ##
## If future has already completed then ``cb`` will be called immediately. ## If future has already completed then `cb` will be called immediately.
## ##
## It's recommended to use ``addCallback`` or ``then`` instead. ## It's recommended to use `addCallback` or `then` instead.
future.clearCallbacks future.clearCallbacks
future.addCallback cb future.addCallback cb
@ -277,7 +277,7 @@ proc `callback=`*[T](future: Future[T],
cb: proc (future: Future[T]) {.closure, gcsafe.}) = cb: proc (future: Future[T]) {.closure, gcsafe.}) =
## Sets the callback proc to be called when the future completes. ## Sets the callback proc to be called when the future completes.
## ##
## If future has already completed then ``cb`` will be called immediately. ## If future has already completed then `cb` will be called immediately.
future.callback = proc () = cb(future) future.callback = proc () = cb(future)
proc getHint(entry: StackTraceEntry): string = proc getHint(entry: StackTraceEntry): string =
@ -359,8 +359,8 @@ proc injectStacktrace[T](future: Future[T]) =
future.error.msg = newMsg future.error.msg = newMsg
proc read*[T](future: Future[T] | FutureVar[T]): T = proc read*[T](future: Future[T] | FutureVar[T]): T =
## Retrieves the value of ``future``. Future must be finished otherwise ## Retrieves the value of `future`. Future must be finished otherwise
## this function will fail with a ``ValueError`` exception. ## this function will fail with a `ValueError` exception.
## ##
## If the result of the future is an error then that error will be raised. ## If the result of the future is an error then that error will be raised.
{.push hint[ConvFromXtoItselfNotNeeded]: off.} {.push hint[ConvFromXtoItselfNotNeeded]: off.}
@ -380,40 +380,40 @@ proc read*[T](future: Future[T] | FutureVar[T]): T =
raise newException(ValueError, "Future still in progress.") raise newException(ValueError, "Future still in progress.")
proc readError*[T](future: Future[T]): ref Exception = proc readError*[T](future: Future[T]): ref Exception =
## Retrieves the exception stored in ``future``. ## Retrieves the exception stored in `future`.
## ##
## An ``ValueError`` exception will be thrown if no exception exists ## An `ValueError` exception will be thrown if no exception exists
## in the specified Future. ## in the specified Future.
if future.error != nil: return future.error if future.error != nil: return future.error
else: else:
raise newException(ValueError, "No error in future.") raise newException(ValueError, "No error in future.")
proc mget*[T](future: FutureVar[T]): var T = proc mget*[T](future: FutureVar[T]): var T =
## Returns a mutable value stored in ``future``. ## Returns a mutable value stored in `future`.
## ##
## Unlike ``read``, this function will not raise an exception if the ## Unlike `read`, this function will not raise an exception if the
## Future has not been finished. ## Future has not been finished.
result = Future[T](future).value result = Future[T](future).value
proc finished*(future: FutureBase | FutureVar): bool = proc finished*(future: FutureBase | FutureVar): bool =
## Determines whether ``future`` has completed. ## Determines whether `future` has completed.
## ##
## ``True`` may indicate an error or a value. Use ``failed`` to distinguish. ## `True` may indicate an error or a value. Use `failed` to distinguish.
when future is FutureVar: when future is FutureVar:
result = (FutureBase(future)).finished result = (FutureBase(future)).finished
else: else:
result = future.finished result = future.finished
proc failed*(future: FutureBase): bool = proc failed*(future: FutureBase): bool =
## Determines whether ``future`` completed with an error. ## Determines whether `future` completed with an error.
return future.error != nil return future.error != nil
proc asyncCheck*[T](future: Future[T]) = proc asyncCheck*[T](future: Future[T]) =
## Sets a callback on ``future`` which raises an exception if the future ## Sets a callback on `future` which raises an exception if the future
## finished with an error. ## finished with an error.
## ##
## This should be used instead of ``discard`` to discard void futures, ## This should be used instead of `discard` to discard void futures,
## or use ``waitFor`` if you need to wait for the future's completion. ## or use `waitFor` if you need to wait for the future's completion.
assert(not future.isNil, "Future is nil") assert(not future.isNil, "Future is nil")
# TODO: We can likely look at the stack trace here and inject the location # 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. # where the `asyncCheck` was called to give a better error stack message.
@ -424,7 +424,7 @@ proc asyncCheck*[T](future: Future[T]) =
future.callback = asyncCheckCallback future.callback = asyncCheckCallback
proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
## Returns a future which will complete once both ``fut1`` and ``fut2`` ## Returns a future which will complete once both `fut1` and `fut2`
## complete. ## complete.
var retFuture = newFuture[void]("asyncdispatch.`and`") var retFuture = newFuture[void]("asyncdispatch.`and`")
fut1.callback = fut1.callback =
@ -440,7 +440,7 @@ proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
return retFuture return retFuture
proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
## Returns a future which will complete once either ``fut1`` or ``fut2`` ## Returns a future which will complete once either `fut1` or `fut2`
## complete. ## complete.
var retFuture = newFuture[void]("asyncdispatch.`or`") var retFuture = newFuture[void]("asyncdispatch.`or`")
proc cb[X](fut: Future[X]) = proc cb[X](fut: Future[X]) =
@ -453,14 +453,14 @@ proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
proc all*[T](futs: varargs[Future[T]]): auto = proc all*[T](futs: varargs[Future[T]]): auto =
## Returns a future which will complete once ## Returns a future which will complete once
## all futures in ``futs`` complete. ## all futures in `futs` complete.
## If the argument is empty, the returned future completes immediately. ## If the argument is empty, the returned future completes immediately.
## ##
## If the awaited futures are not ``Future[void]``, the returned future ## If the awaited futures are not `Future[void]`, the returned future
## will hold the values of all awaited futures in a sequence. ## will hold the values of all awaited futures in a sequence.
## ##
## If the awaited futures *are* ``Future[void]``, ## If the awaited futures *are* `Future[void]`,
## this proc returns ``Future[void]``. ## this proc returns `Future[void]`.
when T is void: when T is void:
var var

View file

@ -73,7 +73,7 @@ type
maxFDs: int maxFDs: int
func getSocket*(a: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} = func getSocket*(a: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} =
## Returns the ``AsyncHttpServer``s internal ``AsyncSocket`` instance. ## Returns the `AsyncHttpServer`s internal `AsyncSocket` instance.
## ##
## Useful for identifying what port the AsyncHttpServer is bound to, if it ## Useful for identifying what port the AsyncHttpServer is bound to, if it
## was chosen automatically. ## was chosen automatically.
@ -90,7 +90,7 @@ func getSocket*(a: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} =
proc newAsyncHttpServer*(reuseAddr = true, reusePort = false, proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
maxBody = 8388608): AsyncHttpServer = maxBody = 8388608): AsyncHttpServer =
## Creates a new ``AsyncHttpServer`` instance. ## Creates a new `AsyncHttpServer` instance.
result = AsyncHttpServer(reuseAddr: reuseAddr, reusePort: reusePort, maxBody: maxBody) result = AsyncHttpServer(reuseAddr: reuseAddr, reusePort: reusePort, maxBody: maxBody)
proc addHeaders(msg: var string, headers: HttpHeaders) = proc addHeaders(msg: var string, headers: HttpHeaders) =
@ -105,7 +105,7 @@ proc sendHeaders*(req: Request, headers: HttpHeaders): Future[void] =
proc respond*(req: Request, code: HttpCode, content: string, proc respond*(req: Request, code: HttpCode, content: string,
headers: HttpHeaders = nil): Future[void] = headers: HttpHeaders = nil): Future[void] =
## Responds to the request with the specified ``HttpCode``, headers and ## Responds to the request with the specified `HttpCode`, headers and
## content. ## content.
## ##
## This procedure will **not** close the client socket. ## This procedure will **not** close the client socket.
@ -138,7 +138,7 @@ proc respond*(req: Request, code: HttpCode, content: string,
result = req.client.send(msg) result = req.client.send(msg)
proc respondError(req: Request, code: HttpCode): Future[void] = proc respondError(req: Request, code: HttpCode): Future[void] =
## Responds to the request with the specified ``HttpCode``. ## Responds to the request with the specified `HttpCode`.
let content = $code let content = $code
var msg = "HTTP/1.1 " & content & "\c\L" var msg = "HTTP/1.1 " & content & "\c\L"

View file

@ -8,27 +8,27 @@
# #
## This module implements a high-level asynchronous sockets API based on the ## This module implements a high-level asynchronous sockets API based on the
## asynchronous dispatcher defined in the ``asyncdispatch`` module. ## asynchronous dispatcher defined in the `asyncdispatch` module.
## ##
## Asynchronous IO in Nim ## Asynchronous IO in Nim
## ====================== ## ======================
## ##
## Async IO in Nim consists of multiple layers (from highest to lowest): ## Async IO in Nim consists of multiple layers (from highest to lowest):
## ##
## * ``asyncnet`` module ## * `asyncnet` module
## ##
## * Async await ## * Async await
## ##
## * ``asyncdispatch`` module (event loop) ## * `asyncdispatch` module (event loop)
## ##
## * ``selectors`` module ## * `selectors` module
## ##
## Each builds on top of the layers below it. The selectors module is an ## Each builds on top of the layers below it. The selectors module is an
## abstraction for the various system ``select()`` mechanisms such as epoll or ## abstraction for the various system `select()` mechanisms such as epoll or
## kqueue. If you wish you can use it directly, and some people have done so ## kqueue. If you wish you can use it directly, and some people have done so
## `successfully <http://goran.krampe.se/2014/10/25/nim-socketserver/>`_. ## `successfully <http://goran.krampe.se/2014/10/25/nim-socketserver/>`_.
## But you must be aware that on Windows it only supports ## But you must be aware that on Windows it only supports
## ``select()``. ## `select()`.
## ##
## The async dispatcher implements the proactor pattern and also has an ## The async dispatcher implements the proactor pattern and also has an
## implementation of IOCP. It implements the proactor pattern for other ## implementation of IOCP. It implements the proactor pattern for other
@ -45,16 +45,16 @@
## layers interchangeably (as long as you only care about non-Windows ## layers interchangeably (as long as you only care about non-Windows
## platforms). ## platforms).
## ##
## For most applications using ``asyncnet`` is the way to go as it builds ## For most applications using `asyncnet` is the way to go as it builds
## over all the layers, providing some extra features such as buffering. ## over all the layers, providing some extra features such as buffering.
## ##
## SSL ## SSL
## === ## ===
## ##
## SSL can be enabled by compiling with the ``-d:ssl`` flag. ## SSL can be enabled by compiling with the `-d:ssl` flag.
## ##
## You must create a new SSL context with the ``newContext`` function defined ## You must create a new SSL context with the `newContext` function defined
## in the ``net`` module. You may then call ``wrapSocket`` on your socket using ## in the `net` module. You may then call `wrapSocket` on your socket using
## the newly created SSL context to get an SSL socket. ## the newly created SSL context to get an SSL socket.
## ##
## Examples ## Examples
@ -134,16 +134,16 @@ proc newAsyncSocket*(fd: AsyncFD, domain: Domain = AF_INET,
protocol: Protocol = IPPROTO_TCP, protocol: Protocol = IPPROTO_TCP,
buffered = true, buffered = true,
inheritable = defined(nimInheritHandles)): owned(AsyncSocket) = inheritable = defined(nimInheritHandles)): owned(AsyncSocket) =
## Creates a new ``AsyncSocket`` based on the supplied params. ## Creates a new `AsyncSocket` based on the supplied params.
## ##
## The supplied ``fd``'s non-blocking state will be enabled implicitly. ## The supplied `fd`'s non-blocking state will be enabled implicitly.
## ##
## If ``inheritable`` is false (the default), the supplied ``fd`` will not ## If `inheritable` is false (the default), the supplied `fd` will not
## be inheritable by child processes. ## be inheritable by child processes.
## ##
## **Note**: This procedure will **NOT** register ``fd`` with the global ## **Note**: This procedure will **NOT** register `fd` with the global
## async dispatcher. You need to do this manually. If you have used ## async dispatcher. You need to do this manually. If you have used
## ``newAsyncNativeSocket`` to create ``fd`` then it's already registered. ## `newAsyncNativeSocket` to create `fd` then it's already registered.
assert fd != osInvalidSocket.AsyncFD assert fd != osInvalidSocket.AsyncFD
new(result) new(result)
result.fd = fd.SocketHandle result.fd = fd.SocketHandle
@ -165,7 +165,7 @@ proc newAsyncSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
## This procedure will also create a brand new file descriptor for ## This procedure will also create a brand new file descriptor for
## this socket. ## this socket.
## ##
## If ``inheritable`` is false (the default), the new file descriptor will not ## If `inheritable` is false (the default), the new file descriptor will not
## be inheritable by child processes. ## be inheritable by child processes.
let fd = createAsyncNativeSocket(domain, sockType, protocol, inheritable) let fd = createAsyncNativeSocket(domain, sockType, protocol, inheritable)
if fd.SocketHandle == osInvalidSocket: if fd.SocketHandle == osInvalidSocket:
@ -192,7 +192,7 @@ proc newAsyncSocket*(domain, sockType, protocol: cint,
## This procedure will also create a brand new file descriptor for ## This procedure will also create a brand new file descriptor for
## this socket. ## this socket.
## ##
## If ``inheritable`` is false (the default), the new file descriptor will not ## If `inheritable` is false (the default), the new file descriptor will not
## be inheritable by child processes. ## be inheritable by child processes.
let fd = createAsyncNativeSocket(domain, sockType, protocol, inheritable) let fd = createAsyncNativeSocket(domain, sockType, protocol, inheritable)
if fd.SocketHandle == osInvalidSocket: if fd.SocketHandle == osInvalidSocket:
@ -233,7 +233,7 @@ when defineSsl:
proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag], proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag],
sslError: cint): owned(Future[bool]) {.async.} = sslError: cint): owned(Future[bool]) {.async.} =
## Returns ``true`` if ``socket`` is still connected, otherwise ``false``. ## Returns `true` if `socket` is still connected, otherwise `false`.
result = true result = true
case sslError case sslError
of SSL_ERROR_WANT_WRITE: of SSL_ERROR_WANT_WRITE:
@ -279,9 +279,9 @@ when defineSsl:
proc dial*(address: string, port: Port, protocol = IPPROTO_TCP, proc dial*(address: string, port: Port, protocol = IPPROTO_TCP,
buffered = true): owned(Future[AsyncSocket]) {.async.} = buffered = true): owned(Future[AsyncSocket]) {.async.} =
## Establishes connection to the specified ``address``:``port`` pair via the ## Establishes connection to the specified `address`:`port` pair via the
## specified protocol. The procedure iterates through possible ## specified protocol. The procedure iterates through possible
## resolutions of the ``address`` until it succeeds, meaning that it ## resolutions of the `address` until it succeeds, meaning that it
## seamlessly works with both IPv4 and IPv6. ## seamlessly works with both IPv4 and IPv6.
## Returns AsyncSocket ready to send or receive data. ## Returns AsyncSocket ready to send or receive data.
let asyncFd = await asyncdispatch.dial(address, port, protocol) let asyncFd = await asyncdispatch.dial(address, port, protocol)
@ -290,9 +290,9 @@ proc dial*(address: string, port: Port, protocol = IPPROTO_TCP,
result = newAsyncSocket(asyncFd, domain, sockType, protocol, buffered) result = newAsyncSocket(asyncFd, domain, sockType, protocol, buffered)
proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} = proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} =
## Connects ``socket`` to server at ``address:port``. ## Connects `socket` to server at `address:port`.
## ##
## Returns a ``Future`` which will complete when the connection succeeds ## Returns a `Future` which will complete when the connection succeeds
## or an error occurs. ## or an error occurs.
await connect(socket.fd.AsyncFD, address, port, socket.domain) await connect(socket.fd.AsyncFD, address, port, socket.domain)
if socket.isSsl: if socket.isSsl:
@ -308,7 +308,7 @@ proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} =
template readInto(buf: pointer, size: int, socket: AsyncSocket, template readInto(buf: pointer, size: int, socket: AsyncSocket,
flags: set[SocketFlag]): int = flags: set[SocketFlag]): int =
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. Note that ## Reads **up to** `size` bytes from `socket` into `buf`. Note that
## this is a template and not a proc. ## this is a template and not a proc.
assert(not socket.closed, "Cannot `recv` on a closed socket") assert(not socket.closed, "Cannot `recv` on a closed socket")
var res = 0 var res = 0
@ -332,10 +332,10 @@ template readIntoBuf(socket: AsyncSocket,
proc recvInto*(socket: AsyncSocket, buf: pointer, size: int, proc recvInto*(socket: AsyncSocket, buf: pointer, size: int,
flags = {SocketFlag.SafeDisconn}): owned(Future[int]) {.async.} = flags = {SocketFlag.SafeDisconn}): owned(Future[int]) {.async.} =
## Reads **up to** ``size`` bytes from ``socket`` into ``buf``. ## Reads **up to** `size` bytes from `socket` into `buf`.
## ##
## For buffered sockets this function will attempt to read all the requested ## For buffered sockets this function will attempt to read all the requested
## data. It will read this data in ``BufferSize`` chunks. ## data. It will read this data in `BufferSize` chunks.
## ##
## For unbuffered sockets this function makes no effort to read ## For unbuffered sockets this function makes no effort to read
## all the data requested. It will return as much data as the operating system ## all the data requested. It will return as much data as the operating system
@ -346,7 +346,7 @@ proc recvInto*(socket: AsyncSocket, buf: pointer, size: int,
## requested data. ## requested data.
## ##
## If socket is disconnected and no data is available ## If socket is disconnected and no data is available
## to be read then the future will complete with a value of ``0``. ## to be read then the future will complete with a value of `0`.
if socket.isBuffered: if socket.isBuffered:
let originalBufPos = socket.currPos let originalBufPos = socket.currPos
@ -380,10 +380,10 @@ proc recvInto*(socket: AsyncSocket, buf: pointer, size: int,
proc recv*(socket: AsyncSocket, size: int, proc recv*(socket: AsyncSocket, size: int,
flags = {SocketFlag.SafeDisconn}): owned(Future[string]) {.async.} = flags = {SocketFlag.SafeDisconn}): owned(Future[string]) {.async.} =
## Reads **up to** ``size`` bytes from ``socket``. ## Reads **up to** `size` bytes from `socket`.
## ##
## For buffered sockets this function will attempt to read all the requested ## For buffered sockets this function will attempt to read all the requested
## data. It will read this data in ``BufferSize`` chunks. ## data. It will read this data in `BufferSize` chunks.
## ##
## For unbuffered sockets this function makes no effort to read ## For unbuffered sockets this function makes no effort to read
## all the data requested. It will return as much data as the operating system ## all the data requested. It will return as much data as the operating system
@ -394,7 +394,7 @@ proc recv*(socket: AsyncSocket, size: int,
## requested data. ## requested data.
## ##
## If socket is disconnected and no data is available ## If socket is disconnected and no data is available
## to be read then the future will complete with a value of ``""``. ## to be read then the future will complete with a value of `""`.
if socket.isBuffered: if socket.isBuffered:
result = newString(size) result = newString(size)
shallow(result) shallow(result)
@ -432,7 +432,7 @@ proc recv*(socket: AsyncSocket, size: int,
proc send*(socket: AsyncSocket, buf: pointer, size: int, proc send*(socket: AsyncSocket, buf: pointer, size: int,
flags = {SocketFlag.SafeDisconn}) {.async.} = flags = {SocketFlag.SafeDisconn}) {.async.} =
## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future will complete once all ## Sends `size` bytes from `buf` to `socket`. The returned future will complete once all
## data has been sent. ## data has been sent.
assert socket != nil assert socket != nil
assert(not socket.closed, "Cannot `send` on a closed socket") assert(not socket.closed, "Cannot `send` on a closed socket")
@ -446,7 +446,7 @@ proc send*(socket: AsyncSocket, buf: pointer, size: int,
proc send*(socket: AsyncSocket, data: string, proc send*(socket: AsyncSocket, data: string,
flags = {SocketFlag.SafeDisconn}) {.async.} = flags = {SocketFlag.SafeDisconn}) {.async.} =
## Sends ``data`` to ``socket``. The returned future will complete once all ## Sends `data` to `socket`. The returned future will complete once all
## data has been sent. ## data has been sent.
assert socket != nil assert socket != nil
if socket.isSsl: if socket.isSsl:
@ -464,7 +464,7 @@ proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn},
## Accepts a new connection. Returns a future containing the client socket ## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection and the remote address of the client. ## corresponding to that connection and the remote address of the client.
## ##
## If ``inheritable`` is false (the default), the resulting client socket will ## If `inheritable` is false (the default), the resulting client socket will
## not be inheritable by child processes. ## not be inheritable by child processes.
## ##
## The future will complete when the connection is successfully accepted. ## The future will complete when the connection is successfully accepted.
@ -486,7 +486,7 @@ proc accept*(socket: AsyncSocket,
flags = {SocketFlag.SafeDisconn}): owned(Future[AsyncSocket]) = flags = {SocketFlag.SafeDisconn}): owned(Future[AsyncSocket]) =
## Accepts a new connection. Returns a future containing the client socket ## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection. ## corresponding to that connection.
## If ``inheritable`` is false (the default), the resulting client socket will ## If `inheritable` is false (the default), the resulting client socket will
## not be inheritable by child processes. ## not be inheritable by child processes.
## The future will complete when the connection is successfully accepted. ## The future will complete when the connection is successfully accepted.
var retFut = newFuture[AsyncSocket]("asyncnet.accept") var retFut = newFuture[AsyncSocket]("asyncnet.accept")
@ -502,25 +502,25 @@ proc accept*(socket: AsyncSocket,
proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {.async.} = flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {.async.} =
## Reads a line of data from ``socket`` into ``resString``. ## Reads a line of data from `socket` into `resString`.
## ##
## If a full line is read ``\r\L`` is not ## If a full line is read `\r\L` is not
## added to ``line``, however if solely ``\r\L`` is read then ``line`` ## added to `line`, however if solely `\r\L` is read then `line`
## will be set to it. ## will be set to it.
## ##
## If the socket is disconnected, ``line`` will be set to ``""``. ## If the socket is disconnected, `line` will be set to `""`.
## ##
## If the socket is disconnected in the middle of a line (before ``\r\L`` ## If the socket is disconnected in the middle of a line (before `\r\L`
## is read) then line will be set to ``""``. ## is read) then line will be set to `""`.
## The partial line **will be lost**. ## The partial line **will be lost**.
## ##
## The ``maxLength`` parameter determines the maximum amount of characters ## The `maxLength` parameter determines the maximum amount of characters
## that can be read. ``resString`` will be truncated after that. ## that can be read. `resString` will be truncated after that.
## ##
## **Warning**: The ``Peek`` flag is not yet implemented. ## **Warning**: The `Peek` flag is not yet implemented.
## ##
## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the ## **Warning**: `recvLineInto` on unbuffered sockets assumes that the
## protocol uses ``\r\L`` to delimit a new line. ## protocol uses `\r\L` to delimit a new line.
assert SocketFlag.Peek notin flags ## TODO: assert SocketFlag.Peek notin flags ## TODO:
result = newFuture[void]("asyncnet.recvLineInto") result = newFuture[void]("asyncnet.recvLineInto")
@ -595,26 +595,26 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
proc recvLine*(socket: AsyncSocket, proc recvLine*(socket: AsyncSocket,
flags = {SocketFlag.SafeDisconn}, flags = {SocketFlag.SafeDisconn},
maxLength = MaxLineLength): owned(Future[string]) {.async.} = maxLength = MaxLineLength): owned(Future[string]) {.async.} =
## Reads a line of data from ``socket``. Returned future will complete once ## Reads a line of data from `socket`. Returned future will complete once
## a full line is read or an error occurs. ## a full line is read or an error occurs.
## ##
## If a full line is read ``\r\L`` is not ## If a full line is read `\r\L` is not
## added to ``line``, however if solely ``\r\L`` is read then ``line`` ## added to `line`, however if solely `\r\L` is read then `line`
## will be set to it. ## will be set to it.
## ##
## If the socket is disconnected, ``line`` will be set to ``""``. ## If the socket is disconnected, `line` will be set to `""`.
## ##
## If the socket is disconnected in the middle of a line (before ``\r\L`` ## If the socket is disconnected in the middle of a line (before `\r\L`
## is read) then line will be set to ``""``. ## is read) then line will be set to `""`.
## The partial line **will be lost**. ## The partial line **will be lost**.
## ##
## The ``maxLength`` parameter determines the maximum amount of characters ## The `maxLength` parameter determines the maximum amount of characters
## that can be read. The result is truncated after that. ## that can be read. The result is truncated after that.
## ##
## **Warning**: The ``Peek`` flag is not yet implemented. ## **Warning**: The `Peek` flag is not yet implemented.
## ##
## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol ## **Warning**: `recvLine` on unbuffered sockets assumes that the protocol
## uses ``\r\L`` to delimit a new line. ## uses `\r\L` to delimit a new line.
assert SocketFlag.Peek notin flags ## TODO: assert SocketFlag.Peek notin flags ## TODO:
# TODO: Optimise this # TODO: Optimise this
@ -625,8 +625,8 @@ proc recvLine*(socket: AsyncSocket,
proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [
ReadIOEffect].} = ReadIOEffect].} =
## Marks ``socket`` as accepting connections. ## Marks `socket` as accepting connections.
## ``Backlog`` specifies the maximum length of the ## `Backlog` specifies the maximum length of the
## queue of pending connections. ## queue of pending connections.
## ##
## Raises an OSError error upon failure. ## Raises an OSError error upon failure.
@ -634,9 +634,9 @@ proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [
proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {. proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {.
tags: [ReadIOEffect].} = tags: [ReadIOEffect].} =
## Binds ``address``:``port`` to the socket. ## Binds `address`:`port` to the socket.
## ##
## If ``address`` is "" then ADDR_ANY will be bound. ## If `address` is "" then ADDR_ANY will be bound.
var realaddr = address var realaddr = address
if realaddr == "": if realaddr == "":
case socket.domain case socket.domain
@ -735,7 +735,7 @@ proc close*(socket: AsyncSocket) =
when defineSsl: when defineSsl:
proc wrapSocket*(ctx: SslContext, socket: AsyncSocket) = proc wrapSocket*(ctx: SslContext, socket: AsyncSocket) =
## Wraps a socket in an SSL context. This function effectively turns ## Wraps a socket in an SSL context. This function effectively turns
## ``socket`` into an SSL socket. ## `socket` into an SSL socket.
## ##
## **Disclaimer**: This code is not well tested, may be very unsafe and ## **Disclaimer**: This code is not well tested, may be very unsafe and
## prone to security vulnerabilities. ## prone to security vulnerabilities.
@ -755,8 +755,8 @@ when defineSsl:
handshake: SslHandshakeType, handshake: SslHandshakeType,
hostname: string = "") = hostname: string = "") =
## Wraps a connected socket in an SSL context. This function effectively ## Wraps a connected socket in an SSL context. This function effectively
## turns ``socket`` into an SSL socket. ## turns `socket` into an SSL socket.
## ``hostname`` should be specified so that the client knows which hostname ## `hostname` should be specified so that the client knows which hostname
## the server certificate should be validated against. ## the server certificate should be validated against.
## ##
## This should be called on a connected socket, and will perform ## This should be called on a connected socket, and will perform
@ -789,18 +789,18 @@ when defineSsl:
proc getSockOpt*(socket: AsyncSocket, opt: SOBool, level = SOL_SOCKET): bool {. proc getSockOpt*(socket: AsyncSocket, opt: SOBool, level = SOL_SOCKET): bool {.
tags: [ReadIOEffect].} = tags: [ReadIOEffect].} =
## Retrieves option ``opt`` as a boolean value. ## Retrieves option `opt` as a boolean value.
var res = getSockOptInt(socket.fd, cint(level), toCInt(opt)) var res = getSockOptInt(socket.fd, cint(level), toCInt(opt))
result = res != 0 result = res != 0
proc setSockOpt*(socket: AsyncSocket, opt: SOBool, value: bool, proc setSockOpt*(socket: AsyncSocket, opt: SOBool, value: bool,
level = SOL_SOCKET) {.tags: [WriteIOEffect].} = level = SOL_SOCKET) {.tags: [WriteIOEffect].} =
## Sets option ``opt`` to a boolean value specified by ``value``. ## Sets option `opt` to a boolean value specified by `value`.
var valuei = cint(if value: 1 else: 0) var valuei = cint(if value: 1 else: 0)
setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei) setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
proc isSsl*(socket: AsyncSocket): bool = proc isSsl*(socket: AsyncSocket): bool =
## Determines whether ``socket`` is a SSL socket. ## Determines whether `socket` is a SSL socket.
socket.isSsl socket.isSsl
proc getFd*(socket: AsyncSocket): SocketHandle = proc getFd*(socket: AsyncSocket): SocketHandle =
@ -814,7 +814,7 @@ proc isClosed*(socket: AsyncSocket): bool =
proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string, proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string,
flags = {SocketFlag.SafeDisconn}): owned(Future[void]) flags = {SocketFlag.SafeDisconn}): owned(Future[void])
{.async, since: (1, 3).} = {.async, since: (1, 3).} =
## This proc sends ``data`` to the specified ``address``, which may be an IP ## This proc sends `data` to the specified `address`, which may be an IP
## address or a hostname. If a hostname is specified this function will try ## address or a hostname. If a hostname is specified this function will try
## each IP of that hostname. The returned future will complete once all data ## each IP of that hostname. The returned future will complete once all data
## has been sent. ## has been sent.
@ -861,9 +861,9 @@ proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int,
address: FutureVar[string], port: FutureVar[Port], address: FutureVar[string], port: FutureVar[Port],
flags = {SocketFlag.SafeDisconn}): owned(Future[int]) flags = {SocketFlag.SafeDisconn}): owned(Future[int])
{.async, since: (1, 3).} = {.async, since: (1, 3).} =
## Receives a datagram data from ``socket`` into ``data``, which must be at ## Receives a datagram data from `socket` into `data`, which must be at
## least of size ``size``. The address and port of datagram's sender will be ## least of size `size`. The address and port of datagram's sender will be
## stored into ``address`` and ``port``, respectively. Returned future will ## stored into `address` and `port`, respectively. Returned future will
## complete once one datagram has been received, and will return size of ## complete once one datagram has been received, and will return size of
## packet received. ## packet received.
## ##
@ -872,8 +872,8 @@ proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int,
## This proc is normally used with connectionless sockets (UDP sockets). ## This proc is normally used with connectionless sockets (UDP sockets).
## ##
## **Notes** ## **Notes**
## * ``data`` must be initialized to the length of ``size``. ## * `data` must be initialized to the length of `size`.
## * ``address`` must be initialized to 46 in length. ## * `address` must be initialized to 46 in length.
template adaptRecvFromToDomain(domain: Domain) = template adaptRecvFromToDomain(domain: Domain) =
var lAddr = sizeof(sAddr).SockLen var lAddr = sizeof(sAddr).SockLen
@ -915,8 +915,8 @@ proc recvFrom*(socket: AsyncSocket, size: int,
flags = {SocketFlag.SafeDisconn}): flags = {SocketFlag.SafeDisconn}):
owned(Future[tuple[data: string, address: string, port: Port]]) owned(Future[tuple[data: string, address: string, port: Port]])
{.async, since: (1, 3).} = {.async, since: (1, 3).} =
## Receives a datagram data from ``socket``, which must be at least of size ## Receives a datagram data from `socket`, which must be at least of size
## ``size``. Returned future will complete once one datagram has been received ## `size`. Returned future will complete once one datagram has been received
## and will return tuple with: data of packet received; and address and port ## and will return tuple with: data of packet received; and address and port
## of datagram's sender. ## of datagram's sender.
## ##

View file

@ -24,14 +24,14 @@ type
error*: ref Exception error*: ref Exception
proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] = proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] =
## Create a new ``FutureStream``. This future's callback is activated when ## Create a new `FutureStream`. This future's callback is activated when
## two events occur: ## two events occur:
## ##
## * New data is written into the future stream. ## * New data is written into the future stream.
## * The future stream is completed (this means that no more data will be ## * The future stream is completed (this means that no more data will be
## written). ## written).
## ##
## Specifying ``fromProc``, which is a string specifying the name of the proc ## Specifying `fromProc`, which is a string specifying the name of the proc
## that this future belongs to, is a good habit as it helps with debugging. ## that this future belongs to, is a good habit as it helps with debugging.
## ##
## **Note:** The API of FutureStream is still new and so has a higher ## **Note:** The API of FutureStream is still new and so has a higher
@ -40,14 +40,14 @@ proc newFutureStream*[T](fromProc = "unspecified"): FutureStream[T] =
result.queue = initDeque[T]() result.queue = initDeque[T]()
proc complete*[T](future: FutureStream[T]) = proc complete*[T](future: FutureStream[T]) =
## Completes a ``FutureStream`` signalling the end of data. ## Completes a `FutureStream` signalling the end of data.
assert(future.error == nil, "Trying to complete failed stream") assert(future.error == nil, "Trying to complete failed stream")
future.finished = true future.finished = true
if not future.cb.isNil: if not future.cb.isNil:
future.cb() future.cb()
proc fail*[T](future: FutureStream[T], error: ref Exception) = proc fail*[T](future: FutureStream[T], error: ref Exception) =
## Completes ``future`` with ``error``. ## Completes `future` with `error`.
assert(not future.finished) assert(not future.finished)
future.finished = true future.finished = true
future.error = error future.error = error
@ -60,9 +60,9 @@ proc `callback=`*[T](future: FutureStream[T],
## future stream. ## future stream.
## ##
## The callback is also called when the future is completed. So you should ## The callback is also called when the future is completed. So you should
## use ``finished`` to check whether data is available. ## use `finished` to check whether data is available.
## ##
## If the future stream already has data or is finished then ``cb`` will be ## If the future stream already has data or is finished then `cb` will be
## called immediately. ## called immediately.
proc named() = cb(future) proc named() = cb(future)
future.cb = named future.cb = named
@ -70,19 +70,19 @@ proc `callback=`*[T](future: FutureStream[T],
callSoon(future.cb) callSoon(future.cb)
proc finished*[T](future: FutureStream[T]): bool = proc finished*[T](future: FutureStream[T]): bool =
## Check if a ``FutureStream`` is finished. ``true`` value means that ## Check if a `FutureStream` is finished. `true` value means that
## no more data will be placed inside the stream *and* that there is ## no more data will be placed inside the stream *and* that there is
## no data waiting to be retrieved. ## no data waiting to be retrieved.
result = future.finished and future.queue.len == 0 result = future.finished and future.queue.len == 0
proc failed*[T](future: FutureStream[T]): bool = proc failed*[T](future: FutureStream[T]): bool =
## Determines whether ``future`` completed with an error. ## Determines whether `future` completed with an error.
return future.error != nil return future.error != nil
proc write*[T](future: FutureStream[T], value: T): Future[void] = proc write*[T](future: FutureStream[T], value: T): Future[void] =
## Writes the specified value inside the specified future stream. ## Writes the specified value inside the specified future stream.
## ##
## This will raise ``ValueError`` if ``future`` is finished. ## This will raise `ValueError` if `future` is finished.
result = newFuture[void]("FutureStream.put") result = newFuture[void]("FutureStream.put")
if future.finished: if future.finished:
let msg = "FutureStream is finished and so no longer accepts new data." let msg = "FutureStream is finished and so no longer accepts new data."
@ -95,14 +95,14 @@ proc write*[T](future: FutureStream[T], value: T): Future[void] =
result.complete() result.complete()
proc read*[T](future: FutureStream[T]): owned(Future[(bool, T)]) = proc read*[T](future: FutureStream[T]): owned(Future[(bool, T)]) =
## Returns a future that will complete when the ``FutureStream`` has data ## Returns a future that will complete when the `FutureStream` has data
## placed into it. The future will be completed with the oldest ## placed into it. The future will be completed with the oldest
## value stored inside the stream. The return value will also determine ## value stored inside the stream. The return value will also determine
## whether data was retrieved, ``false`` means that the future stream was ## whether data was retrieved, `false` means that the future stream was
## completed and no data was retrieved. ## completed and no data was retrieved.
## ##
## This function will remove the data that was returned from the underlying ## This function will remove the data that was returned from the underlying
## ``FutureStream``. ## `FutureStream`.
var resFut = newFuture[(bool, T)]("FutureStream.take") var resFut = newFuture[(bool, T)]("FutureStream.take")
let savedCb = future.cb let savedCb = future.cb
proc newCb(fs: FutureStream[T]) = proc newCb(fs: FutureStream[T]) =

View file

@ -27,7 +27,7 @@ else:
const osOpenCmd* = const osOpenCmd* =
when defined(macos) or defined(macosx) or defined(windows): "open" else: "xdg-open" ## \ when defined(macos) or defined(macosx) or defined(windows): "open" else: "xdg-open" ## \
## Alias for the operating system specific *"open"* command, ## Alias for the operating system specific *"open"* command,
## ``"open"`` on OSX, MacOS and Windows, ``"xdg-open"`` on Linux, BSD, etc. ## `"open"` on OSX, MacOS and Windows, `"xdg-open"` on Linux, BSD, etc.
proc prepare(s: string): string = proc prepare(s: string): string =
if s.contains("://"): if s.contains("://"):
@ -47,7 +47,7 @@ proc openDefaultBrowserImpl(url: string) =
if execShellCmd(osOpenCmd & " " & u) == 0: return if execShellCmd(osOpenCmd & " " & u) == 0: return
for b in getEnv("BROWSER").split(PathSep): for b in getEnv("BROWSER").split(PathSep):
try: try:
# we use ``startProcess`` here because we don't want to block! # we use `startProcess` here because we don't want to block!
discard startProcess(command = b, args = [url], options = {poUsePath}) discard startProcess(command = b, args = [url], options = {poUsePath})
return return
except OSError: except OSError:
@ -57,9 +57,9 @@ proc openDefaultBrowser*(url: string) =
## Opens `url` with the user's default browser. This does not block. ## Opens `url` with the user's default browser. This does not block.
## The URL must not be empty string, to open on a blank page see `openDefaultBrowser()`. ## The URL must not be empty string, to open on a blank page see `openDefaultBrowser()`.
## ##
## Under Windows, ``ShellExecute`` is used. Under Mac OS X the ``open`` ## Under Windows, `ShellExecute` is used. Under Mac OS X the `open`
## command is used. Under Unix, it is checked if ``xdg-open`` exists and ## command is used. Under Unix, it is checked if `xdg-open` exists and
## used if it does. Otherwise the environment variable ``BROWSER`` is ## used if it does. Otherwise the environment variable `BROWSER` is
## used to determine the default browser to use. ## used to determine the default browser to use.
## ##
## This proc doesn't raise an exception on error, beware. ## This proc doesn't raise an exception on error, beware.
@ -73,9 +73,9 @@ proc openDefaultBrowser*() {.since: (1, 1).} =
## Opens the user's default browser without any `url` (blank page). This does not block. ## Opens the user's default browser without any `url` (blank page). This does not block.
## Implements IETF RFC-6694 Section 3, "about:blank" must be reserved for a blank page. ## Implements IETF RFC-6694 Section 3, "about:blank" must be reserved for a blank page.
## ##
## Under Windows, ``ShellExecute`` is used. Under Mac OS X the ``open`` ## Under Windows, `ShellExecute` is used. Under Mac OS X the `open`
## command is used. Under Unix, it is checked if ``xdg-open`` exists and ## command is used. Under Unix, it is checked if `xdg-open` exists and
## used if it does. Otherwise the environment variable ``BROWSER`` is ## used if it does. Otherwise the environment variable `BROWSER` is
## used to determine the default browser to use. ## used to determine the default browser to use.
## ##
## This proc doesn't raise an exception on error, beware. ## This proc doesn't raise an exception on error, beware.

View file

@ -440,7 +440,7 @@ proc colorNameCmp(x: tuple[name: string, col: Color], y: string): int =
proc parseColor*(name: string): Color = proc parseColor*(name: string): Color =
## Parses `name` to a color value. ## Parses `name` to a color value.
## ##
## If no valid color could be parsed ``ValueError`` is raised. ## If no valid color could be parsed `ValueError` is raised.
## Case insensitive. ## Case insensitive.
## ##
runnableExamples: runnableExamples:
@ -461,7 +461,7 @@ proc parseColor*(name: string): Color =
proc isColor*(name: string): bool = proc isColor*(name: string): bool =
## Returns true if `name` is a known color name or a hexadecimal color ## Returns true if `name` is a known color name or a hexadecimal color
## prefixed with ``#``. Case insensitive. ## prefixed with `#`. Case insensitive.
## ##
runnableExamples: runnableExamples:
var var

View file

@ -229,7 +229,7 @@ proc switchTo(current, to: CoroutinePtr) =
nimGC_setStackBottom(ctx.ncbottom) nimGC_setStackBottom(ctx.ncbottom)
proc suspend*(sleepTime: float = 0) = proc suspend*(sleepTime: float = 0) =
## Stops coroutine execution and resumes no sooner than after ``sleeptime`` seconds. ## Stops coroutine execution and resumes no sooner than after `sleeptime` seconds.
## Until then other coroutines are executed. ## Until then other coroutines are executed.
var current = getCurrent() var current = getCurrent()
current.sleepTime = sleepTime current.sleepTime = sleepTime
@ -335,9 +335,9 @@ proc run*() =
ctx.current = ctx.current.next ctx.current = ctx.current.next
proc alive*(c: CoroutineRef): bool = c.coro != nil and c.coro.state != CORO_FINISHED proc alive*(c: CoroutineRef): bool = c.coro != nil and c.coro.state != CORO_FINISHED
## Returns ``true`` if coroutine has not returned, ``false`` otherwise. ## Returns `true` if coroutine has not returned, `false` otherwise.
proc wait*(c: CoroutineRef, interval = 0.01) = proc wait*(c: CoroutineRef, interval = 0.01) =
## Returns only after coroutine ``c`` has returned. ``interval`` is time in seconds how often. ## Returns only after coroutine `c` has returned. `interval` is time in seconds how often.
while alive(c): while alive(c):
suspend(interval) suspend(interval)

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## Common datatypes and definitions for all ``db_*.nim`` ( ## Common datatypes and definitions for all `db_*.nim` (
## `db_mysql <db_mysql.html>`_, `db_postgres <db_postgres.html>`_, ## `db_mysql <db_mysql.html>`_, `db_postgres <db_postgres.html>`_,
## and `db_sqlite <db_sqlite.html>`_) modules. ## and `db_sqlite <db_sqlite.html>`_) modules.
@ -86,7 +86,7 @@ type
template sql*(query: string): SqlQuery = template sql*(query: string): SqlQuery =
## constructs a SqlQuery from the string `query`. This is supposed to be ## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier: ## used as a raw-string-literal modifier:
## ``sql"update user set counter = counter + 1"`` ## `sql"update user set counter = counter + 1"`
## ##
## If assertions are turned off, it does nothing. If assertions are turned ## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax. ## on, later versions will check the string for valid syntax.

View file

@ -201,7 +201,7 @@ proc detectOsImpl(d: Distribution): bool =
template detectOs*(d: untyped): bool = template detectOs*(d: untyped): bool =
## Distro/OS detection. For convenience the ## Distro/OS detection. For convenience the
## required ``Distribution.`` qualifier is added to the ## required `Distribution.` qualifier is added to the
## enum value. ## enum value.
detectOsImpl(Distribution.d) detectOsImpl(Distribution.d)