summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorDominik Picheta <dominikpicheta@googlemail.com>2014-09-12 22:54:02 +0100
committerDominik Picheta <dominikpicheta@googlemail.com>2014-09-12 22:54:02 +0100
commit6f155af802bec7dc1b4b8ff31e0135dd6692a93c (patch)
treee15bff0975c43f048a6c3a06b3831c28e81d4649
parent950078003767de36132f179ca00d8c81dbcc162a (diff)
downloadNim-6f155af802bec7dc1b4b8ff31e0135dd6692a93c.tar.gz
Lots of documentation improvements for asyncdispatch.
Ref #1487.
-rw-r--r--lib/pure/asyncdispatch.nim140
-rw-r--r--lib/pure/asyncftpclient.nim18
-rw-r--r--lib/pure/asynchttpserver.nim16
-rw-r--r--lib/pure/asyncnet.nim18
-rw-r--r--lib/pure/ftpclient.nim2
-rw-r--r--lib/pure/httpclient.nim13
-rw-r--r--lib/pure/os.nim2
7 files changed, 183 insertions, 26 deletions
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 5363d8862..cac98e160 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -18,42 +18,137 @@ export Port, SocketFlag
 #{.injectStmt: newGcInvariant().}
 
 ## AsyncDispatch
-## -------------
+## *************
 ##
-## This module implements a brand new dispatcher based on Futures.
-## On Windows IOCP is used and on other operating systems the ``selectors``
-## module is used instead.
+## 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``
+## keyword.
 ##
-## **Note:** This module is still largely experimental.
+## 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
+## 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
+## 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.
+##
+## Future objects can also store a callback procedure which will be called
+## automatically once the future completes.
+##
+## Futures therefore can be thought of as an implementation of the proactor
+## 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``
+## 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
+## requested amount of data is read **or** an exception occurs.
+##
+## Code to read some data from a socket may look something like this:
+##
+##   .. code-block::nim
+##      var future = socket.recv(100)
+##      future.callback =
+##        proc () =
+##          echo(future.read)
+##
+## 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
+## 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
+## 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.
+##
+## Asynchronous procedures
+## -----------------------
+##
+## Asynchronous procedures remove the pain of working with callbacks. They do
+## 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.
+##
+## Inside asynchronous procedures ``await`` can be used to call any
+## procedures which return a
+## ``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
+## asynchronous procedure will resume its execution. During the period
+## 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)``,
+## 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")``.
+##
+## Discarding futures
+## ------------------
+##
+## 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.
+##
+## Examples
+## --------
+##
+## For examples take a look at the documentation for the modules implementing
+## asynchronous IO. A good place to start is the
+## `asyncnet module <asyncnet.html>`_.
+##
+## Limitations/Bugs
+## ----------------
+## 
+## * ``except`` statement (without `try`) does not work inside async procedures.
+## * The effect system (``raises: []``) does not work with async procedures.
+## * Can't await in a ``except`` body
 
 
-# TODO: ``except`` statement (without `try`) does not work.
-# TODO: Multiple exception names in a ``except`` don't work.
-# TODO: The effect system (raises: []) has trouble with my try transformation.
-# TODO: Can't await in a 'except' body
-# TODO: getCurrentException(Msg) don't work
 # TODO: Check if yielded future is nil and throw a more meaningful exception
 
 # -- Futures
 
 type
-  FutureBase* = ref object of RootObj
+  FutureBase* = ref object of RootObj ## Untyped future.
     cb: proc () {.closure,gcsafe.}
     finished: bool
-    error*: ref Exception
+    error*: ref Exception ## Stored exception
     errorStackTrace*: string
     when not defined(release):
       stackTrace: string ## For debugging purposes only.
       id: int
       fromProc: string
 
-  Future*[T] = ref object of FutureBase
-    value: T
+  Future*[T] = ref object of FutureBase ## Typed future.
+    value: T ## Stored value
 
 {.deprecated: [PFutureBase: FutureBase, PFuture: Future].}
 
 
-var currentID* = 0
+var currentID = 0
 proc newFuture*[T](fromProc: string = "unspecified"): Future[T] =
   ## Creates a new future.
   ##
@@ -161,6 +256,10 @@ proc read*[T](future: Future[T]): T =
     raise newException(ValueError, "Future still in progress.")
 
 proc readError*[T](future: Future[T]): ref Exception =
+  ## Retrieves the exception stored in ``future``.
+  ##
+  ## 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.")
@@ -173,7 +272,7 @@ proc finished*[T](future: Future[T]): bool =
 
 proc failed*(future: FutureBase): bool =
   ## Determines whether ``future`` completed with an error.
-  future.error != nil
+  return future.error != nil
 
 proc asyncCheck*[T](future: Future[T]) =
   ## Sets a callback on ``future`` which raises an exception if the future
@@ -950,7 +1049,7 @@ proc accept*(socket: TAsyncFD,
 
 # -- Await Macro
 
-template createCb*(retFutureSym, iteratorNameSym,
+template createCb(retFutureSym, iteratorNameSym,
                    name: expr): stmt {.immediate.} =
   var nameIterVar = iteratorNameSym
   #{.push stackTrace: off.}
@@ -1193,7 +1292,7 @@ macro async*(prc: stmt): stmt {.immediate.} =
 
   # -> createCb(retFuture)
   var cbName = newIdentNode("cb")
-  var procCb = newCall("createCb", retFutureSym, iteratorNameSym,
+  var procCb = newCall(bindSym"createCb", retFutureSym, iteratorNameSym,
                        newStrLitNode(prc[0].getName))
   outerProcBody.add procCb
 
@@ -1232,7 +1331,10 @@ proc recvLine*(socket: TAsyncFD): Future[string] {.async.} =
   ## is read) then line will be set to ``""``.
   ## The partial line **will be lost**.
   ##
-  ## **Warning**: This assumes that lines are delimited by ``\r\l``.
+  ## **Warning**: This assumes that lines are delimited by ``\r\L``.
+  ##
+  ## **Note**: This procedure is mostly used for testing. You likely want to
+  ## use ``asyncnet.recvLine`` instead.
   
   template addNLIfEmpty(): stmt =
     if result.len == 0:
diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim
index c5a97adfc..fc38dc31a 100644
--- a/lib/pure/asyncftpclient.nim
+++ b/lib/pure/asyncftpclient.nim
@@ -6,6 +6,24 @@
 #    distribution, for details about the copyright.
 #
 
+## This module implement an asynchronous FTP client.
+##
+## Examples
+## --------
+##
+## .. code-block::nim
+##
+##      var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
+##      proc main(ftp: AsyncFtpClient) {.async.} =
+##        await ftp.connect()
+##        echo await ftp.pwd()
+##        echo await ftp.listDirs()
+##        await ftp.store("payload.jpg", "payload.jpg")
+##        await ftp.retrFile("payload.jpg", "payload2.jpg")
+##        echo("Finished")
+##
+##      waitFor main(ftp)
+
 import asyncdispatch, asyncnet, strutils, parseutils, os, times
 
 from ftpclient import FtpBaseObj, ReplyError, FtpEvent
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index 931a0c15a..9de0a6268 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -9,7 +9,20 @@
 
 ## This module implements a high performance asynchronous HTTP server.
 ##
-## **Note:** This module is still largely experimental.
+## Examples
+## --------
+##
+## This example will create an HTTP server on port 8080. The server will
+## respond to all requests with a ``200 OK`` response code and "Hello World"
+## as the response body.
+##
+## .. code-block::nim
+##    var server = newAsyncHttpServer()
+##    proc cb(req: TRequest) {.async.} =
+##      await req.respond(Http200, "Hello World")
+##
+##    asyncCheck server.serve(Port(8080), cb)
+##    runForever()
 
 import strtabs, asyncnet, asyncdispatch, parseutils, uri, strutils
 type
@@ -52,6 +65,7 @@ proc `==`*(protocol: tuple[orig: string, major, minor: int],
   result = protocol.major == major and protocol.minor == minor
 
 proc newAsyncHttpServer*(): PAsyncHttpServer =
+  ## Creates a new ``AsyncHttpServer`` instance.
   new result
 
 proc addHeaders(msg: var string, headers: PStringTable) =
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index 4539dfdcb..a16111349 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -10,8 +10,20 @@
 ## This module implements a high-level asynchronous sockets API based on the
 ## asynchronous dispatcher defined in the ``asyncdispatch`` module.
 ##
-## Example
-## =======
+## SSL
+## ---
+##
+## 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
+## the newly created SSL context to get an SSL socket.
+##
+## Examples
+## --------
+##
+## Chat server
+## ^^^^^^^^^^^
 ## 
 ## The following example demonstrates a simple chat server.
 ##
@@ -41,8 +53,6 @@
 ##   asyncCheck serve()
 ##   runForever()
 ##
-##
-## **Note:** This module is still largely experimental.
 
 import asyncdispatch
 import rawsockets
diff --git a/lib/pure/ftpclient.nim b/lib/pure/ftpclient.nim
index 07c9b5662..37b25871d 100644
--- a/lib/pure/ftpclient.nim
+++ b/lib/pure/ftpclient.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2012 Dominik Picheta
+#        (c) Copyright 2014 Dominik Picheta
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 1548cf231..892ddac40 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -48,6 +48,19 @@
 ##    
 ##   echo(postContent("http://validator.w3.org/check", headers, body))
 ##
+## Asynchronous HTTP requests
+## ==========================
+##
+## You simply have to create a new instance of the ``AsyncHttpClient`` object.
+## You may then use ``await`` on the functions defined for that object.
+## Keep in mind that the following code needs to be inside an asynchronous
+## procedure.
+##
+## .. code-block::nim
+##
+##    var client = newAsyncHttpClient()
+##    var resp = await client.request("http://google.com")
+##
 ## SSL/TLS support
 ## ===============
 ## This requires the OpenSSL library, fortunately it's widely used and installed
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 6b3ee6e6d..c08297b7c 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -80,7 +80,7 @@ when defined(Nimdoc): # only for proper documentation:
       ## search patch components (as in PATH), such as ':' for POSIX or ';' for
       ## Windows.
 
-    FileSystemCaseSensitive* = True
+    FileSystemCaseSensitive* = true
       ## True if the file system is case sensitive, false otherwise. Used by
       ## `cmpPaths` to compare filenames properly.