mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-12 22:33:49 +00:00
use single backtick (#17166)
This commit is contained in:
@@ -8,25 +8,25 @@
|
||||
#
|
||||
|
||||
## This module implements asynchronous IO. This includes a dispatcher,
|
||||
## a ``Future`` type implementation, and an ``async`` macro which allows
|
||||
## asynchronous code to be written in a synchronous style with the ``await``
|
||||
## a `Future` type implementation, and an `async` macro which allows
|
||||
## asynchronous code to be written in a synchronous style with the `await`
|
||||
## keyword.
|
||||
##
|
||||
## 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``)
|
||||
## 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`)
|
||||
## 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
|
||||
## other operating systems.
|
||||
##
|
||||
## The ``poll`` function will not, on its own, return any events. Instead
|
||||
## an appropriate ``Future`` object will be completed. A ``Future`` is a
|
||||
## The `poll` function will not, on its own, return any events. Instead
|
||||
## 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
|
||||
## 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.
|
||||
## The latter situation occurs when the operation to complete a future fails
|
||||
## 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
|
||||
## automatically once the future completes.
|
||||
@@ -35,9 +35,9 @@
|
||||
## pattern. In this
|
||||
## 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
|
||||
## 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
|
||||
## 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.
|
||||
##
|
||||
## Code to read some data from a socket may look something like this:
|
||||
@@ -49,18 +49,18 @@
|
||||
## 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
|
||||
## code which will be executed before an asynchronous request is made, in most
|
||||
## cases this code sets up the request.
|
||||
##
|
||||
## 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
|
||||
## is made. This ``Future`` instance will complete once the requested amount
|
||||
## 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
|
||||
## 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
|
||||
## 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``.
|
||||
## The ``read`` function is used for this and it checks whether the future
|
||||
## 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
|
||||
## 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.
|
||||
##
|
||||
@@ -71,14 +71,14 @@
|
||||
## this by allowing you to write asynchronous code the same way as you would
|
||||
## write synchronous code.
|
||||
##
|
||||
## An asynchronous procedure is marked using the ``{.async.}`` pragma.
|
||||
## 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
|
||||
## a return type then ``Future[void]`` is assumed.
|
||||
## An asynchronous procedure is marked using the `{.async.}` pragma.
|
||||
## 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
|
||||
## 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
|
||||
## ``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
|
||||
## suspend its execution
|
||||
## until the awaited procedure's Future completes. At which point the
|
||||
@@ -86,23 +86,23 @@
|
||||
## when an asynchronous procedure is suspended other asynchronous procedures
|
||||
## will be run by the dispatcher.
|
||||
##
|
||||
## 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)``,
|
||||
## 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)`,
|
||||
## 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
|
||||
## be used to await a procedure returning a ``Future[void]``:
|
||||
## ``await socket.send("foobar")``.
|
||||
## automatically. It can be used to await a `Future` object, and it can
|
||||
## be used to await a procedure returning a `Future[void]`:
|
||||
## `await socket.send("foobar")`.
|
||||
##
|
||||
## 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
|
||||
## ``await``. The following section shows different ways that you can handle
|
||||
## 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
|
||||
## `await`. The following section shows different ways that you can handle
|
||||
## exceptions in async procs.
|
||||
##
|
||||
## Handling Exceptions
|
||||
## -------------------
|
||||
##
|
||||
## The most reliable way to handle exceptions is to use ``yield`` on a future
|
||||
## then check the future's ``failed`` property. For example:
|
||||
## The most reliable way to handle exceptions is to use `yield` on a future
|
||||
## then check the future's `failed` property. For example:
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
## var future = sock.recv(100)
|
||||
@@ -110,7 +110,7 @@
|
||||
## if future.failed:
|
||||
## # 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
|
||||
## try:
|
||||
@@ -129,9 +129,9 @@
|
||||
##
|
||||
## 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
|
||||
## 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
|
||||
## ``waitFor`` for that purpose.
|
||||
## `waitFor` for that purpose.
|
||||
##
|
||||
## Examples
|
||||
## ========
|
||||
@@ -144,14 +144,14 @@
|
||||
## =============================
|
||||
##
|
||||
## 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
|
||||
## memory leaks. When this occurs it's hard to identify the procedure that is
|
||||
## stuck.
|
||||
##
|
||||
## 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
|
||||
## use the ``getFuturesInProgress`` procedure to get the list of pending futures
|
||||
## 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
|
||||
## together with the stack traces to the moment of their creation.
|
||||
##
|
||||
## You may also find it useful to use this
|
||||
@@ -164,7 +164,7 @@
|
||||
## 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 options, math, std/monotimes
|
||||
@@ -232,7 +232,7 @@ template implementSetInheritable() {.dirty.} =
|
||||
when declared(setInheritable):
|
||||
proc setInheritable*(fd: AsyncFD, inheritable: bool): bool =
|
||||
## Control whether a file handle can be inherited by child processes.
|
||||
## Returns ``true`` on success.
|
||||
## Returns `true` on success.
|
||||
##
|
||||
## This procedure is not guaranteed to be available for all platforms.
|
||||
## Test for availability with `declared() <system.html#declared,untyped>`_.
|
||||
@@ -307,7 +307,7 @@ when defined(windows) or defined(nimdoc):
|
||||
return disp.ioPort
|
||||
|
||||
proc register*(fd: AsyncFD) =
|
||||
## Registers ``fd`` with the dispatcher.
|
||||
## Registers `fd` with the dispatcher.
|
||||
let p = getGlobalDispatcher()
|
||||
|
||||
if createIoCompletionPort(fd.Handle, p.ioPort,
|
||||
@@ -430,16 +430,16 @@ when defined(windows) or defined(nimdoc):
|
||||
|
||||
proc recv*(socket: AsyncFD, size: int,
|
||||
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
|
||||
## 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:
|
||||
# * When WSARecv completes immediately then ``bytesReceived`` is very
|
||||
# * When WSARecv completes immediately then `bytesReceived` is very
|
||||
# unreliable.
|
||||
# * Still need to implement message-oriented socket disconnection,
|
||||
# '\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,
|
||||
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
|
||||
## 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
|
||||
## ``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:
|
||||
# * When WSARecv completes immediately then ``bytesReceived`` is very
|
||||
# * When WSARecv completes immediately then `bytesReceived` is very
|
||||
# unreliable.
|
||||
# * Still need to implement message-oriented socket disconnection,
|
||||
# '\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,
|
||||
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.
|
||||
##
|
||||
## **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.
|
||||
verifyPresence(socket)
|
||||
var retFuture = newFuture[void]("send")
|
||||
@@ -607,16 +607,16 @@ when defined(windows) or defined(nimdoc):
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
else:
|
||||
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
|
||||
# free ``ol``.
|
||||
# free `ol`.
|
||||
return retFuture
|
||||
|
||||
proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr,
|
||||
saddrLen: SockLen,
|
||||
flags = {SocketFlag.SafeDisconn}): owned(Future[void]) =
|
||||
## Sends ``data`` to specified destination ``saddr``, using
|
||||
## socket ``socket``. The returned future will complete once all data
|
||||
## Sends `data` to specified destination `saddr`, using
|
||||
## socket `socket`. The returned future will complete once all data
|
||||
## has been sent.
|
||||
verifyPresence(socket)
|
||||
var retFuture = newFuture[void]("sendTo")
|
||||
@@ -652,17 +652,17 @@ when defined(windows) or defined(nimdoc):
|
||||
retFuture.fail(newException(OSError, osErrorMsg(err)))
|
||||
else:
|
||||
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
|
||||
# free ``ol``.
|
||||
# free `ol`.
|
||||
return retFuture
|
||||
|
||||
proc recvFromInto*(socket: AsyncFD, data: pointer, size: int,
|
||||
saddr: ptr SockAddr, saddrLen: ptr SockLen,
|
||||
flags = {SocketFlag.SafeDisconn}): owned(Future[int]) =
|
||||
## Receives a datagram data from ``socket`` into ``buf``, which must
|
||||
## be at least of size ``size``, address of datagram's sender will be
|
||||
## stored into ``saddr`` and ``saddrLen``. Returned future will complete
|
||||
## Receives a datagram data from `socket` into `buf`, which must
|
||||
## be at least of size `size`, address of datagram's sender will be
|
||||
## stored into `saddr` and `saddrLen`. Returned future will complete
|
||||
## once one datagram has been received, and will return size of packet
|
||||
## received.
|
||||
verifyPresence(socket)
|
||||
@@ -715,11 +715,11 @@ when defined(windows) or defined(nimdoc):
|
||||
## The resulting client socket is automatically registered to the
|
||||
## dispatcher.
|
||||
##
|
||||
## If ``inheritable`` is false (the default), the resulting client socket will
|
||||
## If `inheritable` is false (the default), the resulting client socket will
|
||||
## not be inheritable by child processes.
|
||||
##
|
||||
## The ``accept`` call may result in an error if the connecting socket
|
||||
## disconnects during the duration of the ``accept``. If the ``SafeDisconn``
|
||||
## The `accept` call may result in an error if the connecting socket
|
||||
## disconnects during the duration of the `accept`. If the `SafeDisconn`
|
||||
## flag is specified then this error will not be raised and instead
|
||||
## accept will be called again.
|
||||
verifyPresence(socket)
|
||||
@@ -796,9 +796,9 @@ when defined(windows) or defined(nimdoc):
|
||||
GC_unref(ol)
|
||||
else:
|
||||
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
|
||||
# free ``ol``.
|
||||
# free `ol`.
|
||||
|
||||
return retFuture
|
||||
|
||||
@@ -810,7 +810,7 @@ when defined(windows) or defined(nimdoc):
|
||||
getGlobalDispatcher().handles.excl(socket)
|
||||
|
||||
proc unregister*(fd: AsyncFD) =
|
||||
## Unregisters ``fd``.
|
||||
## Unregisters `fd`.
|
||||
getGlobalDispatcher().handles.excl(fd)
|
||||
|
||||
proc contains*(disp: PDispatcher, fd: AsyncFD): bool =
|
||||
@@ -910,9 +910,9 @@ when defined(windows) or defined(nimdoc):
|
||||
|
||||
proc addRead*(fd: AsyncFD, cb: Callback) =
|
||||
## 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
|
||||
## need it (main usecase is adaptation of unix-like libraries to be
|
||||
## asynchronous on Windows).
|
||||
@@ -921,16 +921,16 @@ when defined(windows) or defined(nimdoc):
|
||||
## or asyncdispatch.accept(), because they are using IOCP, please use
|
||||
## nativesockets.recv() and nativesockets.accept() instead.
|
||||
##
|
||||
## Be sure your callback ``cb`` returns ``true``, if you want to remove
|
||||
## watch of `read` notifications, and ``false``, if you want to continue
|
||||
## Be sure your callback `cb` returns `true`, if you want to remove
|
||||
## watch of `read` notifications, and `false`, if you want to continue
|
||||
## receiving notifications.
|
||||
registerWaitableEvent(fd, cb, FD_READ or FD_ACCEPT or FD_OOB or FD_CLOSE)
|
||||
|
||||
proc addWrite*(fd: AsyncFD, cb: Callback) =
|
||||
## 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
|
||||
## need it (main usecase is adaptation of unix-like libraries to be
|
||||
## asynchronous on Windows).
|
||||
@@ -939,8 +939,8 @@ when defined(windows) or defined(nimdoc):
|
||||
## or asyncdispatch.connect(), because they are using IOCP, please use
|
||||
## nativesockets.send() and nativesockets.connect() instead.
|
||||
##
|
||||
## Be sure your callback ``cb`` returns ``true``, if you want to remove
|
||||
## watch of `write` notifications, and ``false``, if you want to continue
|
||||
## Be sure your callback `cb` returns `true`, if you want to remove
|
||||
## watch of `write` notifications, and `false`, if you want to continue
|
||||
## receiving notifications.
|
||||
registerWaitableEvent(fd, cb, FD_WRITE or FD_CONNECT or FD_CLOSE)
|
||||
|
||||
@@ -980,12 +980,12 @@ when defined(windows) or defined(nimdoc):
|
||||
raiseOSError(osLastError())
|
||||
|
||||
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:
|
||||
##
|
||||
## * ``timeout`` - timeout value in milliseconds.
|
||||
## * ``oneshot``
|
||||
## * `timeout` - timeout value in milliseconds.
|
||||
## * `oneshot`
|
||||
## * `true` - generate only one timeout event
|
||||
## * `false` - generate timeout events periodically
|
||||
|
||||
@@ -1014,8 +1014,8 @@ when defined(windows) or defined(nimdoc):
|
||||
registerWaitableHandle(p, hEvent, flags, pcd, timeout, timercb)
|
||||
|
||||
proc addProcess*(pid: int, cb: Callback) =
|
||||
## Registers callback ``cb`` to be called when process with process ID
|
||||
## ``pid`` exited.
|
||||
## Registers callback `cb` to be called when process with process ID
|
||||
## `pid` exited.
|
||||
const NULL = Handle(0)
|
||||
let p = getGlobalDispatcher()
|
||||
let procFlags = SYNCHRONIZE
|
||||
@@ -1033,10 +1033,10 @@ when defined(windows) or defined(nimdoc):
|
||||
registerWaitableHandle(p, hProcess, flags, pcd, INFINITE, proccb)
|
||||
|
||||
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
|
||||
## dispatcher like ``AsyncSocket``.
|
||||
## New `AsyncEvent` object is not automatically registered with
|
||||
## dispatcher like `AsyncSocket`.
|
||||
var sa = SECURITY_ATTRIBUTES(
|
||||
nLength: sizeof(SECURITY_ATTRIBUTES).cint,
|
||||
bInheritHandle: 1
|
||||
@@ -1048,12 +1048,12 @@ when defined(windows) or defined(nimdoc):
|
||||
result.hEvent = event
|
||||
|
||||
proc trigger*(ev: AsyncEvent) =
|
||||
## Set event ``ev`` to signaled state.
|
||||
## Set event `ev` to signaled state.
|
||||
if setEvent(ev.hEvent) == 0:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc unregister*(ev: AsyncEvent) =
|
||||
## Unregisters event ``ev``.
|
||||
## Unregisters event `ev`.
|
||||
doAssert(ev.hWaiter != 0, "Event is not registered in the queue!")
|
||||
let p = getGlobalDispatcher()
|
||||
p.handles.excl(AsyncFD(ev.hEvent))
|
||||
@@ -1064,14 +1064,14 @@ when defined(windows) or defined(nimdoc):
|
||||
ev.hWaiter = 0
|
||||
|
||||
proc close*(ev: AsyncEvent) =
|
||||
## Closes event ``ev``.
|
||||
## Closes event `ev`.
|
||||
let res = closeHandle(ev.hEvent)
|
||||
deallocShared(cast[pointer](ev))
|
||||
if res == 0:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
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!")
|
||||
|
||||
let p = getGlobalDispatcher()
|
||||
@@ -1460,8 +1460,8 @@ else:
|
||||
proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr,
|
||||
saddrLen: SockLen,
|
||||
flags = {SocketFlag.SafeDisconn}): owned(Future[void]) =
|
||||
## Sends ``data`` of size ``size`` in bytes to specified destination
|
||||
## (``saddr`` of size ``saddrLen`` in bytes, using socket ``socket``.
|
||||
## Sends `data` of size `size` in bytes to specified destination
|
||||
## (`saddr` of size `saddrLen` in bytes, using socket `socket`.
|
||||
## The returned future will complete once all data has been sent.
|
||||
var retFuture = newFuture[void]("sendTo")
|
||||
|
||||
@@ -1491,9 +1491,9 @@ else:
|
||||
proc recvFromInto*(socket: AsyncFD, data: pointer, size: int,
|
||||
saddr: ptr SockAddr, saddrLen: ptr SockLen,
|
||||
flags = {SocketFlag.SafeDisconn}): owned(Future[int]) =
|
||||
## Receives a datagram data from ``socket`` into ``data``, which must
|
||||
## be at least of size ``size`` in bytes, address of datagram's sender
|
||||
## will be stored into ``saddr`` and ``saddrLen``. Returned future will
|
||||
## Receives a datagram data from `socket` into `data`, which must
|
||||
## be at least of size `size` in bytes, address of datagram's sender
|
||||
## will be stored into `saddr` and `saddrLen`. Returned future will
|
||||
## complete once one datagram has been received, and will return size
|
||||
## of packet received.
|
||||
var retFuture = newFuture[int]("recvFromInto")
|
||||
@@ -1563,45 +1563,45 @@ else:
|
||||
|
||||
proc addTimer*(timeout: int, oneshot: bool, cb: Callback) =
|
||||
## Start watching for timeout expiration, and then call the
|
||||
## callback ``cb``.
|
||||
## ``timeout`` - time in milliseconds,
|
||||
## ``oneshot`` - if ``true`` only one event will be dispatched,
|
||||
## if ``false`` continuous events every ``timeout`` milliseconds.
|
||||
## callback `cb`.
|
||||
## `timeout` - time in milliseconds,
|
||||
## `oneshot` - if `true` only one event will be dispatched,
|
||||
## if `false` continuous events every `timeout` milliseconds.
|
||||
let p = getGlobalDispatcher()
|
||||
var data = newAsyncData()
|
||||
data.readList.add(cb)
|
||||
p.selector.registerTimer(timeout, oneshot, data)
|
||||
|
||||
proc addSignal*(signal: int, cb: Callback) =
|
||||
## Start watching signal ``signal``, and when signal appears, call the
|
||||
## callback ``cb``.
|
||||
## Start watching signal `signal`, and when signal appears, call the
|
||||
## callback `cb`.
|
||||
let p = getGlobalDispatcher()
|
||||
var data = newAsyncData()
|
||||
data.readList.add(cb)
|
||||
p.selector.registerSignal(signal, data)
|
||||
|
||||
proc addProcess*(pid: int, cb: Callback) =
|
||||
## Start watching for process exit with pid ``pid``, and then call
|
||||
## the callback ``cb``.
|
||||
## Start watching for process exit with pid `pid`, and then call
|
||||
## the callback `cb`.
|
||||
let p = getGlobalDispatcher()
|
||||
var data = newAsyncData()
|
||||
data.readList.add(cb)
|
||||
p.selector.registerProcess(pid, data)
|
||||
|
||||
proc newAsyncEvent*(): AsyncEvent =
|
||||
## Creates new ``AsyncEvent``.
|
||||
## Creates new `AsyncEvent`.
|
||||
result = AsyncEvent(newSelectEvent())
|
||||
|
||||
proc trigger*(ev: AsyncEvent) =
|
||||
## Sets new ``AsyncEvent`` to signaled state.
|
||||
## Sets new `AsyncEvent` to signaled state.
|
||||
trigger(SelectEvent(ev))
|
||||
|
||||
proc close*(ev: AsyncEvent) =
|
||||
## Closes ``AsyncEvent``
|
||||
## Closes `AsyncEvent`
|
||||
close(SelectEvent(ev))
|
||||
|
||||
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.
|
||||
let p = getGlobalDispatcher()
|
||||
var data = newAsyncData()
|
||||
@@ -1609,8 +1609,8 @@ else:
|
||||
p.selector.registerEvent(SelectEvent(ev), data)
|
||||
|
||||
proc drain*(timeout = 500) =
|
||||
## Waits for completion of **all** events and processes them. Raises ``ValueError``
|
||||
## if there are no pending operations. In contrast to ``poll`` this
|
||||
## Waits for completion of **all** events and processes them. Raises `ValueError`
|
||||
## if there are no pending operations. In contrast to `poll` this
|
||||
## processes as many events as are available until the timeout has elapsed.
|
||||
var curTimeout = timeout
|
||||
let start = now()
|
||||
@@ -1621,7 +1621,7 @@ proc drain*(timeout = 500) =
|
||||
break
|
||||
|
||||
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
|
||||
## `epoll`:idx: or `kqueue`:idx: primitive only once.
|
||||
discard runOnce(timeout)
|
||||
@@ -1686,13 +1686,13 @@ when defined(windows) or defined(nimdoc):
|
||||
if ret:
|
||||
# Request to connect completed immediately.
|
||||
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 free ``ol``.
|
||||
# will free `ol`.
|
||||
else:
|
||||
let lastError = osLastError()
|
||||
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.
|
||||
GC_unref(ol)
|
||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||
@@ -1802,9 +1802,9 @@ template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped,
|
||||
|
||||
proc dial*(address: string, port: Port,
|
||||
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
|
||||
## 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.
|
||||
## Returns the async file descriptor, registered in the dispatcher of
|
||||
## 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]) =
|
||||
## Suspends the execution of the current async procedure for the next
|
||||
## ``ms`` milliseconds.
|
||||
## `ms` milliseconds.
|
||||
var retFuture = newFuture[void]("sleepAsync")
|
||||
let p = getGlobalDispatcher()
|
||||
when ms is int:
|
||||
@@ -1843,11 +1843,11 @@ proc sleepAsync*(ms: int | float): owned(Future[void]) =
|
||||
return retFuture
|
||||
|
||||
proc withTimeout*[T](fut: Future[T], timeout: int): owned(Future[bool]) =
|
||||
## Returns a future which will complete once ``fut`` completes or after
|
||||
## ``timeout`` milliseconds has elapsed.
|
||||
## Returns a future which will complete once `fut` completes or after
|
||||
## `timeout` milliseconds has elapsed.
|
||||
##
|
||||
## If ``fut`` completes first the returned future will hold true,
|
||||
## otherwise, if ``timeout`` milliseconds has elapsed first, the returned
|
||||
## If `fut` completes first the returned future will hold true,
|
||||
## otherwise, if `timeout` milliseconds has elapsed first, the returned
|
||||
## future will hold false.
|
||||
|
||||
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
|
||||
## 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.
|
||||
##
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
@@ -1890,7 +1890,7 @@ proc keepAlive(x: string) =
|
||||
|
||||
proc send*(socket: AsyncFD, data: string,
|
||||
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.
|
||||
var retFuture = newFuture[void]("send")
|
||||
if data.len > 0:
|
||||
|
||||
@@ -90,8 +90,8 @@ proc newAsyncFile*(fd: AsyncFD): AsyncFile =
|
||||
register(fd)
|
||||
|
||||
proc openAsync*(filename: string, mode = fmRead): AsyncFile =
|
||||
## Opens a file specified by the path in ``filename`` using
|
||||
## the specified FileMode ``mode`` asynchronously.
|
||||
## Opens a file specified by the path in `filename` using
|
||||
## the specified FileMode `mode` asynchronously.
|
||||
when defined(windows) or defined(nimdoc):
|
||||
let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL
|
||||
let desiredAccess = getDesiredAccess(mode)
|
||||
@@ -124,11 +124,11 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
|
||||
result = newAsyncFile(fd.AsyncFD)
|
||||
|
||||
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.
|
||||
##
|
||||
## 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")
|
||||
|
||||
when defined(windows) or defined(nimdoc):
|
||||
@@ -201,7 +201,7 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
|
||||
return retFuture
|
||||
|
||||
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.
|
||||
##
|
||||
## 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
|
||||
|
||||
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
|
||||
## specified file.
|
||||
@@ -401,7 +401,7 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
|
||||
return retFuture
|
||||
|
||||
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
|
||||
## specified file.
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
## ===========================
|
||||
##
|
||||
## 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
|
||||
## import asyncdispatch, asyncftpclient
|
||||
@@ -29,16 +29,16 @@
|
||||
## echo("Connected")
|
||||
## waitFor(main())
|
||||
##
|
||||
## A new ``main`` async procedure must be declared to allow the use of the
|
||||
## ``await`` keyword. The connection will complete asynchronously and the
|
||||
## client will be connected after the ``await ftp.connect()`` call.
|
||||
## A new `main` async procedure must be declared to allow the use of the
|
||||
## `await` keyword. The connection will complete asynchronously and the
|
||||
## client will be connected after the `await ftp.connect()` call.
|
||||
##
|
||||
## 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
|
||||
## 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.
|
||||
##
|
||||
## .. code-block::nim
|
||||
@@ -56,11 +56,11 @@
|
||||
## ========================================
|
||||
##
|
||||
## The progress of either a file upload or a file download can be checked
|
||||
## by specifying a ``onProgressChanged`` procedure to the ``store`` or
|
||||
## ``retrFile`` procedures.
|
||||
## by specifying a `onProgressChanged` procedure to the `store` or
|
||||
## `retrFile` procedures.
|
||||
##
|
||||
## Procs that take an ``onProgressChanged`` callback will call this every
|
||||
## ``progressInterval`` milliseconds.
|
||||
## Procs that take an `onProgressChanged` callback will call this every
|
||||
## `progressInterval` milliseconds.
|
||||
##
|
||||
## .. code-block::nim
|
||||
## import asyncdispatch, asyncftpclient
|
||||
@@ -148,10 +148,10 @@ proc expectReply(ftp: AsyncFtpClient): Future[string] {.async.} =
|
||||
|
||||
proc send*(ftp: AsyncFtpClient, m: string): Future[string] {.async.} =
|
||||
## 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
|
||||
## characters. Failing to do so will raise ``AssertionDefect``.
|
||||
## You need to make sure that the message `m` doesn't contain any newline
|
||||
## characters. Failing to do so will raise `AssertionDefect`.
|
||||
##
|
||||
## **Note:** The server may return multiple lines of coded replies.
|
||||
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, '\\', '/')
|
||||
|
||||
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)
|
||||
|
||||
var reply = await ftp.expectReply()
|
||||
@@ -208,7 +208,7 @@ proc pwd*(ftp: AsyncFtpClient): Future[string] {.async.} =
|
||||
return wd.captureBetween('"') # "
|
||||
|
||||
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")
|
||||
|
||||
proc cdup*(ftp: AsyncFtpClient) {.async.} =
|
||||
@@ -229,10 +229,10 @@ proc getLines(ftp: AsyncFtpClient): Future[string] {.async.} =
|
||||
assertReply(await(ftp.expectReply()), "226")
|
||||
|
||||
proc listDirs*(ftp: AsyncFtpClient, dir = ""): Future[seq[string]] {.async.} =
|
||||
## Returns a list of filenames in the given directory. If ``dir`` is "",
|
||||
## the current directory is used. If ``async`` is true, this
|
||||
## Returns a list of filenames in the given directory. If `dir` is "",
|
||||
## the current directory is used. If `async` is true, this
|
||||
## 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()
|
||||
|
||||
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())
|
||||
|
||||
proc fileExists*(ftp: AsyncFtpClient, file: string): Future[bool] {.async.} =
|
||||
## Determines whether ``file`` exists.
|
||||
## Determines whether `file` exists.
|
||||
var files = await ftp.listDirs()
|
||||
for f in items(files):
|
||||
if f.normalizePathSep == file.normalizePathSep: return true
|
||||
|
||||
proc createDir*(ftp: AsyncFtpClient, dir: string, recursive = false){.async.} =
|
||||
## Creates a directory ``dir``. If ``recursive`` is true, the topmost
|
||||
## subdirectory of ``dir`` will be created first, following the secondmost...
|
||||
## etc. this allows you to give a full path as the ``dir`` without worrying
|
||||
## Creates a directory `dir`. If `recursive` is true, the topmost
|
||||
## subdirectory of `dir` will be created first, following the secondmost...
|
||||
## etc. this allows you to give a full path as the `dir` without worrying
|
||||
## about subdirectories not existing.
|
||||
if not recursive:
|
||||
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,
|
||||
permissions: set[FilePermission]) {.async.} =
|
||||
## Changes permission of ``path`` to ``permissions``.
|
||||
## Changes permission of `path` to `permissions`.
|
||||
var userOctal = 0
|
||||
var groupOctal = 0
|
||||
var otherOctal = 0
|
||||
@@ -285,7 +285,7 @@ proc chmod*(ftp: AsyncFtpClient, path: string,
|
||||
" " & path.normalizePathSep)), "200")
|
||||
|
||||
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.
|
||||
await ftp.pasv()
|
||||
|
||||
@@ -295,7 +295,7 @@ proc list*(ftp: AsyncFtpClient, dir = ""): Future[string] {.async.} =
|
||||
result = await ftp.getLines()
|
||||
|
||||
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()
|
||||
let reply = await ftp.send("RETR " & file.normalizePathSep)
|
||||
assertReply(reply, ["125", "150"])
|
||||
@@ -331,17 +331,17 @@ proc getFile(ftp: AsyncFtpClient, file: File, total: BiggestInt,
|
||||
|
||||
proc defaultOnProgressChanged*(total, progress: BiggestInt,
|
||||
speed: float): Future[void] {.nimcall, gcsafe.} =
|
||||
## Default FTP ``onProgressChanged`` handler. Does nothing.
|
||||
## Default FTP `onProgressChanged` handler. Does nothing.
|
||||
result = newFuture[void]()
|
||||
#echo(total, " ", progress, " ", speed)
|
||||
result.complete()
|
||||
|
||||
proc retrFile*(ftp: AsyncFtpClient, file, dest: string,
|
||||
onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
|
||||
## Downloads ``file`` and saves it to ``dest``.
|
||||
## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
|
||||
## when the download is finished. The event's ``filename`` field will be equal
|
||||
## to ``file``.
|
||||
## Downloads `file` and saves it to `dest`.
|
||||
## The `EvRetr` event is passed to the specified `handleEvent` function
|
||||
## when the download is finished. The event's `filename` field will be equal
|
||||
## to `file`.
|
||||
var destFile = open(dest, mode = fmWrite)
|
||||
await ftp.pasv()
|
||||
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,
|
||||
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
|
||||
## the download.
|
||||
## The ``EvStore`` event is passed to the specified ``handleEvent`` function
|
||||
## when the upload is finished, and the ``filename`` field will be
|
||||
## equal to ``file``.
|
||||
## The `EvStore` event is passed to the specified `handleEvent` function
|
||||
## when the upload is finished, and the `filename` field will be
|
||||
## equal to `file`.
|
||||
var destFile = open(file)
|
||||
await ftp.pasv()
|
||||
|
||||
@@ -406,21 +406,21 @@ proc store*(ftp: AsyncFtpClient, file, dest: string,
|
||||
|
||||
proc rename*(ftp: AsyncFtpClient, nameFrom: string, nameTo: string) {.async.} =
|
||||
## 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("RNTO " & nameTo), "250")
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
proc newAsyncFtpClient*(address: string, port = Port(21),
|
||||
user, pass = "", progressInterval: int = 1000): AsyncFtpClient =
|
||||
## Creates a new ``AsyncFtpClient`` object.
|
||||
## Creates a new `AsyncFtpClient` object.
|
||||
new result
|
||||
result.user = user
|
||||
result.pass = pass
|
||||
|
||||
@@ -86,19 +86,19 @@ when isFutureLoggingEnabled:
|
||||
var callSoonProc {.threadvar.}: proc (cbproc: proc ()) {.gcsafe.}
|
||||
|
||||
proc getCallSoonProc*(): (proc(cbproc: proc ()) {.gcsafe.}) =
|
||||
## Get current implementation of ``callSoon``.
|
||||
## Get current implementation of `callSoon`.
|
||||
return callSoonProc
|
||||
|
||||
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
|
||||
|
||||
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:
|
||||
# Loop not initialized yet. Call the function directly to allow setup code to use futures.
|
||||
cbproc()
|
||||
@@ -117,29 +117,29 @@ template setupFutureBase(fromProc: string) =
|
||||
proc newFuture*[T](fromProc: string = "unspecified"): owned(Future[T]) =
|
||||
## 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.
|
||||
setupFutureBase(fromProc)
|
||||
when isFutureLoggingEnabled: logFutureStart(result)
|
||||
|
||||
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.
|
||||
##
|
||||
## 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.
|
||||
let fo = newFuture[T](fromProc)
|
||||
result = typeof(result)(fo)
|
||||
when isFutureLoggingEnabled: logFutureStart(Future[T](result))
|
||||
|
||||
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).error = nil
|
||||
|
||||
proc checkFinished[T](future: Future[T]) =
|
||||
## Checks whether `future` is finished. If it is then raises a
|
||||
## ``FutureError``.
|
||||
## `FutureError`.
|
||||
when not defined(release):
|
||||
if future.finished:
|
||||
var msg = ""
|
||||
@@ -190,7 +190,7 @@ proc add(callbacks: var CallbackList, function: CallbackFunc) =
|
||||
last.next = newCallback
|
||||
|
||||
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.")
|
||||
checkFinished(future)
|
||||
assert(future.error == nil)
|
||||
@@ -200,7 +200,7 @@ proc complete*[T](future: Future[T], val: T) =
|
||||
when isFutureLoggingEnabled: logFutureFinish(future)
|
||||
|
||||
proc complete*(future: Future[void]) =
|
||||
## Completes a void ``future``.
|
||||
## Completes a void `future`.
|
||||
#assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||
checkFinished(future)
|
||||
assert(future.error == nil)
|
||||
@@ -209,7 +209,7 @@ proc complete*(future: Future[void]) =
|
||||
when isFutureLoggingEnabled: logFutureFinish(future)
|
||||
|
||||
proc complete*[T](future: FutureVar[T]) =
|
||||
## Completes a ``FutureVar``.
|
||||
## Completes a `FutureVar`.
|
||||
template fut: untyped = Future[T](future)
|
||||
checkFinished(fut)
|
||||
assert(fut.error == nil)
|
||||
@@ -218,7 +218,7 @@ proc complete*[T](future: FutureVar[T]) =
|
||||
when isFutureLoggingEnabled: logFutureFinish(Future[T](future))
|
||||
|
||||
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.
|
||||
template fut: untyped = Future[T](future)
|
||||
@@ -230,7 +230,7 @@ proc complete*[T](future: FutureVar[T], val: T) =
|
||||
when isFutureLoggingEnabled: logFutureFinish(future)
|
||||
|
||||
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.")
|
||||
checkFinished(future)
|
||||
future.finished = true
|
||||
@@ -247,7 +247,7 @@ proc clearCallbacks*(future: FutureBase) =
|
||||
proc addCallback*(future: FutureBase, cb: proc() {.closure, gcsafe.}) =
|
||||
## 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
|
||||
if future.finished:
|
||||
callSoon(cb)
|
||||
@@ -258,7 +258,7 @@ proc addCallback*[T](future: Future[T],
|
||||
cb: proc (future: Future[T]) {.closure, gcsafe.}) =
|
||||
## 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(
|
||||
proc() =
|
||||
cb(future)
|
||||
@@ -267,9 +267,9 @@ proc addCallback*[T](future: Future[T],
|
||||
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.
|
||||
##
|
||||
## 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.addCallback cb
|
||||
|
||||
@@ -277,7 +277,7 @@ proc `callback=`*[T](future: Future[T],
|
||||
cb: proc (future: Future[T]) {.closure, gcsafe.}) =
|
||||
## 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)
|
||||
|
||||
proc getHint(entry: StackTraceEntry): string =
|
||||
@@ -359,8 +359,8 @@ proc injectStacktrace[T](future: Future[T]) =
|
||||
future.error.msg = newMsg
|
||||
|
||||
proc read*[T](future: Future[T] | FutureVar[T]): T =
|
||||
## Retrieves the value of ``future``. Future must be finished otherwise
|
||||
## this function will fail with a ``ValueError`` exception.
|
||||
## Retrieves the value of `future`. Future must be finished otherwise
|
||||
## this function will fail with a `ValueError` exception.
|
||||
##
|
||||
## If the result of the future is an error then that error will be raised.
|
||||
{.push hint[ConvFromXtoItselfNotNeeded]: off.}
|
||||
@@ -380,40 +380,40 @@ proc read*[T](future: Future[T] | FutureVar[T]): T =
|
||||
raise newException(ValueError, "Future still in progress.")
|
||||
|
||||
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.
|
||||
if future.error != nil: return future.error
|
||||
else:
|
||||
raise newException(ValueError, "No error in future.")
|
||||
|
||||
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.
|
||||
result = Future[T](future).value
|
||||
|
||||
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:
|
||||
result = (FutureBase(future)).finished
|
||||
else:
|
||||
result = future.finished
|
||||
|
||||
proc failed*(future: FutureBase): bool =
|
||||
## Determines whether ``future`` completed with an error.
|
||||
## Determines whether `future` completed with an error.
|
||||
return future.error != nil
|
||||
|
||||
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.
|
||||
##
|
||||
## This should be used instead of ``discard`` to discard void futures,
|
||||
## or use ``waitFor`` if you need to wait for the future's completion.
|
||||
## This should be used instead of `discard` to discard void futures,
|
||||
## or use `waitFor` if you need to wait for the future's completion.
|
||||
assert(not future.isNil, "Future is nil")
|
||||
# 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.
|
||||
@@ -424,7 +424,7 @@ proc asyncCheck*[T](future: Future[T]) =
|
||||
future.callback = asyncCheckCallback
|
||||
|
||||
proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
|
||||
## Returns a future which will complete once both ``fut1`` and ``fut2``
|
||||
## Returns a future which will complete once both `fut1` and `fut2`
|
||||
## complete.
|
||||
var retFuture = newFuture[void]("asyncdispatch.`and`")
|
||||
fut1.callback =
|
||||
@@ -440,7 +440,7 @@ proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] =
|
||||
return retFuture
|
||||
|
||||
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.
|
||||
var retFuture = newFuture[void]("asyncdispatch.`or`")
|
||||
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 =
|
||||
## 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 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.
|
||||
##
|
||||
## If the awaited futures *are* ``Future[void]``,
|
||||
## this proc returns ``Future[void]``.
|
||||
## If the awaited futures *are* `Future[void]`,
|
||||
## this proc returns `Future[void]`.
|
||||
|
||||
when T is void:
|
||||
var
|
||||
|
||||
@@ -73,7 +73,7 @@ type
|
||||
maxFDs: int
|
||||
|
||||
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
|
||||
## was chosen automatically.
|
||||
@@ -90,7 +90,7 @@ func getSocket*(a: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} =
|
||||
|
||||
proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
|
||||
maxBody = 8388608): AsyncHttpServer =
|
||||
## Creates a new ``AsyncHttpServer`` instance.
|
||||
## Creates a new `AsyncHttpServer` instance.
|
||||
result = AsyncHttpServer(reuseAddr: reuseAddr, reusePort: reusePort, maxBody: maxBody)
|
||||
|
||||
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,
|
||||
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.
|
||||
##
|
||||
## 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)
|
||||
|
||||
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
|
||||
var msg = "HTTP/1.1 " & content & "\c\L"
|
||||
|
||||
|
||||
@@ -8,27 +8,27 @@
|
||||
#
|
||||
|
||||
## 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
|
||||
## ======================
|
||||
##
|
||||
## Async IO in Nim consists of multiple layers (from highest to lowest):
|
||||
##
|
||||
## * ``asyncnet`` module
|
||||
## * `asyncnet` module
|
||||
##
|
||||
## * 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
|
||||
## 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
|
||||
## `successfully <http://goran.krampe.se/2014/10/25/nim-socketserver/>`_.
|
||||
## But you must be aware that on Windows it only supports
|
||||
## ``select()``.
|
||||
## `select()`.
|
||||
##
|
||||
## The async dispatcher implements the proactor pattern and also has an
|
||||
## 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
|
||||
## 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.
|
||||
##
|
||||
## 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
|
||||
## in the ``net`` module. You may then call ``wrapSocket`` on your socket using
|
||||
## 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
|
||||
## the newly created SSL context to get an SSL socket.
|
||||
##
|
||||
## Examples
|
||||
@@ -134,16 +134,16 @@ proc newAsyncSocket*(fd: AsyncFD, domain: Domain = AF_INET,
|
||||
protocol: Protocol = IPPROTO_TCP,
|
||||
buffered = true,
|
||||
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.
|
||||
##
|
||||
## **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
|
||||
## ``newAsyncNativeSocket`` to create ``fd`` then it's already registered.
|
||||
## `newAsyncNativeSocket` to create `fd` then it's already registered.
|
||||
assert fd != osInvalidSocket.AsyncFD
|
||||
new(result)
|
||||
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 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.
|
||||
let fd = createAsyncNativeSocket(domain, sockType, protocol, inheritable)
|
||||
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 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.
|
||||
let fd = createAsyncNativeSocket(domain, sockType, protocol, inheritable)
|
||||
if fd.SocketHandle == osInvalidSocket:
|
||||
@@ -233,7 +233,7 @@ when defineSsl:
|
||||
|
||||
proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag],
|
||||
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
|
||||
case sslError
|
||||
of SSL_ERROR_WANT_WRITE:
|
||||
@@ -279,9 +279,9 @@ when defineSsl:
|
||||
|
||||
proc dial*(address: string, port: Port, protocol = IPPROTO_TCP,
|
||||
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
|
||||
## 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.
|
||||
## Returns AsyncSocket ready to send or receive data.
|
||||
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)
|
||||
|
||||
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.
|
||||
await connect(socket.fd.AsyncFD, address, port, socket.domain)
|
||||
if socket.isSsl:
|
||||
@@ -308,7 +308,7 @@ proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} =
|
||||
|
||||
template readInto(buf: pointer, size: int, socket: AsyncSocket,
|
||||
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.
|
||||
assert(not socket.closed, "Cannot `recv` on a closed socket")
|
||||
var res = 0
|
||||
@@ -332,10 +332,10 @@ template readIntoBuf(socket: AsyncSocket,
|
||||
|
||||
proc recvInto*(socket: AsyncSocket, buf: pointer, size: int,
|
||||
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
|
||||
## 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
|
||||
## 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.
|
||||
##
|
||||
## 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:
|
||||
let originalBufPos = socket.currPos
|
||||
|
||||
@@ -380,10 +380,10 @@ proc recvInto*(socket: AsyncSocket, buf: pointer, size: int,
|
||||
|
||||
proc recv*(socket: AsyncSocket, size: int,
|
||||
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
|
||||
## 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
|
||||
## 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.
|
||||
##
|
||||
## 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:
|
||||
result = newString(size)
|
||||
shallow(result)
|
||||
@@ -432,7 +432,7 @@ proc recv*(socket: AsyncSocket, size: int,
|
||||
|
||||
proc send*(socket: AsyncSocket, buf: pointer, size: int,
|
||||
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.
|
||||
assert socket != nil
|
||||
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,
|
||||
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.
|
||||
assert socket != nil
|
||||
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
|
||||
## 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.
|
||||
##
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
@@ -486,7 +486,7 @@ proc accept*(socket: AsyncSocket,
|
||||
flags = {SocketFlag.SafeDisconn}): owned(Future[AsyncSocket]) =
|
||||
## Accepts a new connection. Returns a future containing the client socket
|
||||
## corresponding to that connection.
|
||||
## If ``inheritable`` is false (the default), the resulting client socket will
|
||||
## If `inheritable` is false (the default), the resulting client socket will
|
||||
## not be inheritable by child processes.
|
||||
## The future will complete when the connection is successfully accepted.
|
||||
var retFut = newFuture[AsyncSocket]("asyncnet.accept")
|
||||
@@ -502,25 +502,25 @@ proc accept*(socket: AsyncSocket,
|
||||
|
||||
proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
|
||||
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
|
||||
## added to ``line``, however if solely ``\r\L`` is read then ``line``
|
||||
## If a full line is read `\r\L` is not
|
||||
## added to `line`, however if solely `\r\L` is read then `line`
|
||||
## 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``
|
||||
## is read) then line will be set to ``""``.
|
||||
## If the socket is disconnected in the middle of a line (before `\r\L`
|
||||
## is read) then line will be set to `""`.
|
||||
## The partial line **will be lost**.
|
||||
##
|
||||
## The ``maxLength`` parameter determines the maximum amount of characters
|
||||
## that can be read. ``resString`` will be truncated after that.
|
||||
## The `maxLength` parameter determines the maximum amount of characters
|
||||
## 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
|
||||
## protocol uses ``\r\L`` to delimit a new line.
|
||||
## **Warning**: `recvLineInto` on unbuffered sockets assumes that the
|
||||
## protocol uses `\r\L` to delimit a new line.
|
||||
assert SocketFlag.Peek notin flags ## TODO:
|
||||
result = newFuture[void]("asyncnet.recvLineInto")
|
||||
|
||||
@@ -595,26 +595,26 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
|
||||
proc recvLine*(socket: AsyncSocket,
|
||||
flags = {SocketFlag.SafeDisconn},
|
||||
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.
|
||||
##
|
||||
## If a full line is read ``\r\L`` is not
|
||||
## added to ``line``, however if solely ``\r\L`` is read then ``line``
|
||||
## If a full line is read `\r\L` is not
|
||||
## added to `line`, however if solely `\r\L` is read then `line`
|
||||
## 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``
|
||||
## is read) then line will be set to ``""``.
|
||||
## If the socket is disconnected in the middle of a line (before `\r\L`
|
||||
## is read) then line will be set to `""`.
|
||||
## 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.
|
||||
##
|
||||
## **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
|
||||
## uses ``\r\L`` to delimit a new line.
|
||||
## **Warning**: `recvLine` on unbuffered sockets assumes that the protocol
|
||||
## uses `\r\L` to delimit a new line.
|
||||
assert SocketFlag.Peek notin flags ## TODO:
|
||||
|
||||
# TODO: Optimise this
|
||||
@@ -625,8 +625,8 @@ proc recvLine*(socket: AsyncSocket,
|
||||
|
||||
proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [
|
||||
ReadIOEffect].} =
|
||||
## Marks ``socket`` as accepting connections.
|
||||
## ``Backlog`` specifies the maximum length of the
|
||||
## Marks `socket` as accepting connections.
|
||||
## `Backlog` specifies the maximum length of the
|
||||
## queue of pending connections.
|
||||
##
|
||||
## 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 = "") {.
|
||||
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
|
||||
if realaddr == "":
|
||||
case socket.domain
|
||||
@@ -735,7 +735,7 @@ proc close*(socket: AsyncSocket) =
|
||||
when defineSsl:
|
||||
proc wrapSocket*(ctx: SslContext, socket: AsyncSocket) =
|
||||
## 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
|
||||
## prone to security vulnerabilities.
|
||||
@@ -755,8 +755,8 @@ when defineSsl:
|
||||
handshake: SslHandshakeType,
|
||||
hostname: string = "") =
|
||||
## Wraps a connected socket in an SSL context. This function effectively
|
||||
## turns ``socket`` into an SSL socket.
|
||||
## ``hostname`` should be specified so that the client knows which hostname
|
||||
## turns `socket` into an SSL socket.
|
||||
## `hostname` should be specified so that the client knows which hostname
|
||||
## the server certificate should be validated against.
|
||||
##
|
||||
## 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 {.
|
||||
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))
|
||||
result = res != 0
|
||||
|
||||
proc setSockOpt*(socket: AsyncSocket, opt: SOBool, value: bool,
|
||||
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)
|
||||
setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei)
|
||||
|
||||
proc isSsl*(socket: AsyncSocket): bool =
|
||||
## Determines whether ``socket`` is a SSL socket.
|
||||
## Determines whether `socket` is a SSL socket.
|
||||
socket.isSsl
|
||||
|
||||
proc getFd*(socket: AsyncSocket): SocketHandle =
|
||||
@@ -814,7 +814,7 @@ proc isClosed*(socket: AsyncSocket): bool =
|
||||
proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string,
|
||||
flags = {SocketFlag.SafeDisconn}): owned(Future[void])
|
||||
{.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
|
||||
## each IP of that hostname. The returned future will complete once all data
|
||||
## has been sent.
|
||||
@@ -861,9 +861,9 @@ proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int,
|
||||
address: FutureVar[string], port: FutureVar[Port],
|
||||
flags = {SocketFlag.SafeDisconn}): owned(Future[int])
|
||||
{.async, since: (1, 3).} =
|
||||
## 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
|
||||
## stored into ``address`` and ``port``, respectively. Returned future will
|
||||
## 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
|
||||
## stored into `address` and `port`, respectively. Returned future will
|
||||
## complete once one datagram has been received, and will return size of
|
||||
## 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).
|
||||
##
|
||||
## **Notes**
|
||||
## * ``data`` must be initialized to the length of ``size``.
|
||||
## * ``address`` must be initialized to 46 in length.
|
||||
## * `data` must be initialized to the length of `size`.
|
||||
## * `address` must be initialized to 46 in length.
|
||||
template adaptRecvFromToDomain(domain: Domain) =
|
||||
var lAddr = sizeof(sAddr).SockLen
|
||||
|
||||
@@ -915,8 +915,8 @@ proc recvFrom*(socket: AsyncSocket, size: int,
|
||||
flags = {SocketFlag.SafeDisconn}):
|
||||
owned(Future[tuple[data: string, address: string, port: Port]])
|
||||
{.async, since: (1, 3).} =
|
||||
## Receives a datagram data from ``socket``, which must be at least of size
|
||||
## ``size``. Returned future will complete once one datagram has been received
|
||||
## Receives a datagram data from `socket`, which must be at least of size
|
||||
## `size`. Returned future will complete once one datagram has been received
|
||||
## and will return tuple with: data of packet received; and address and port
|
||||
## of datagram's sender.
|
||||
##
|
||||
|
||||
@@ -24,14 +24,14 @@ type
|
||||
error*: ref Exception
|
||||
|
||||
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:
|
||||
##
|
||||
## * New data is written into the future stream.
|
||||
## * The future stream is completed (this means that no more data will be
|
||||
## 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.
|
||||
##
|
||||
## **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]()
|
||||
|
||||
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")
|
||||
future.finished = true
|
||||
if not future.cb.isNil:
|
||||
future.cb()
|
||||
|
||||
proc fail*[T](future: FutureStream[T], error: ref Exception) =
|
||||
## Completes ``future`` with ``error``.
|
||||
## Completes `future` with `error`.
|
||||
assert(not future.finished)
|
||||
future.finished = true
|
||||
future.error = error
|
||||
@@ -60,9 +60,9 @@ proc `callback=`*[T](future: FutureStream[T],
|
||||
## future stream.
|
||||
##
|
||||
## 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.
|
||||
proc named() = cb(future)
|
||||
future.cb = named
|
||||
@@ -70,19 +70,19 @@ proc `callback=`*[T](future: FutureStream[T],
|
||||
callSoon(future.cb)
|
||||
|
||||
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 data waiting to be retrieved.
|
||||
result = future.finished and future.queue.len == 0
|
||||
|
||||
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
|
||||
|
||||
proc write*[T](future: FutureStream[T], value: T): Future[void] =
|
||||
## 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")
|
||||
if future.finished:
|
||||
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()
|
||||
|
||||
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
|
||||
## 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.
|
||||
##
|
||||
## This function will remove the data that was returned from the underlying
|
||||
## ``FutureStream``.
|
||||
## `FutureStream`.
|
||||
var resFut = newFuture[(bool, T)]("FutureStream.take")
|
||||
let savedCb = future.cb
|
||||
proc newCb(fs: FutureStream[T]) =
|
||||
|
||||
@@ -27,7 +27,7 @@ else:
|
||||
const osOpenCmd* =
|
||||
when defined(macos) or defined(macosx) or defined(windows): "open" else: "xdg-open" ## \
|
||||
## 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 =
|
||||
if s.contains("://"):
|
||||
@@ -47,7 +47,7 @@ proc openDefaultBrowserImpl(url: string) =
|
||||
if execShellCmd(osOpenCmd & " " & u) == 0: return
|
||||
for b in getEnv("BROWSER").split(PathSep):
|
||||
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})
|
||||
return
|
||||
except OSError:
|
||||
@@ -57,9 +57,9 @@ proc openDefaultBrowser*(url: string) =
|
||||
## 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()`.
|
||||
##
|
||||
## Under Windows, ``ShellExecute`` is used. Under Mac OS X the ``open``
|
||||
## command is used. Under Unix, it is checked if ``xdg-open`` exists and
|
||||
## used if it does. Otherwise the environment variable ``BROWSER`` is
|
||||
## Under Windows, `ShellExecute` is used. Under Mac OS X the `open`
|
||||
## command is used. Under Unix, it is checked if `xdg-open` exists and
|
||||
## used if it does. Otherwise the environment variable `BROWSER` is
|
||||
## used to determine the default browser to use.
|
||||
##
|
||||
## 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.
|
||||
## 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``
|
||||
## command is used. Under Unix, it is checked if ``xdg-open`` exists and
|
||||
## used if it does. Otherwise the environment variable ``BROWSER`` is
|
||||
## Under Windows, `ShellExecute` is used. Under Mac OS X the `open`
|
||||
## command is used. Under Unix, it is checked if `xdg-open` exists and
|
||||
## used if it does. Otherwise the environment variable `BROWSER` is
|
||||
## used to determine the default browser to use.
|
||||
##
|
||||
## This proc doesn't raise an exception on error, beware.
|
||||
|
||||
@@ -440,7 +440,7 @@ proc colorNameCmp(x: tuple[name: string, col: Color], y: string): int =
|
||||
proc parseColor*(name: string): Color =
|
||||
## 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.
|
||||
##
|
||||
runnableExamples:
|
||||
@@ -461,7 +461,7 @@ proc parseColor*(name: string): Color =
|
||||
|
||||
proc isColor*(name: string): bool =
|
||||
## Returns true if `name` is a known color name or a hexadecimal color
|
||||
## prefixed with ``#``. Case insensitive.
|
||||
## prefixed with `#`. Case insensitive.
|
||||
##
|
||||
runnableExamples:
|
||||
var
|
||||
|
||||
@@ -229,7 +229,7 @@ proc switchTo(current, to: CoroutinePtr) =
|
||||
nimGC_setStackBottom(ctx.ncbottom)
|
||||
|
||||
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.
|
||||
var current = getCurrent()
|
||||
current.sleepTime = sleepTime
|
||||
@@ -335,9 +335,9 @@ proc run*() =
|
||||
ctx.current = ctx.current.next
|
||||
|
||||
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) =
|
||||
## 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):
|
||||
suspend(interval)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# 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>`_,
|
||||
## and `db_sqlite <db_sqlite.html>`_) modules.
|
||||
|
||||
@@ -86,7 +86,7 @@ type
|
||||
template sql*(query: string): SqlQuery =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
## 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
|
||||
## on, later versions will check the string for valid syntax.
|
||||
|
||||
@@ -201,7 +201,7 @@ proc detectOsImpl(d: Distribution): bool =
|
||||
|
||||
template detectOs*(d: untyped): bool =
|
||||
## Distro/OS detection. For convenience the
|
||||
## required ``Distribution.`` qualifier is added to the
|
||||
## required `Distribution.` qualifier is added to the
|
||||
## enum value.
|
||||
detectOsImpl(Distribution.d)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user