summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/concurrency/threadpool.nim117
-rw-r--r--lib/pure/hashes.nim131
-rw-r--r--lib/pure/md5.nim107
-rw-r--r--lib/pure/typetraits.nim36
-rw-r--r--lib/system/channels.nim40
-rw-r--r--lib/system/threads.nim71
6 files changed, 325 insertions, 177 deletions
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 4846c610f..15dbb10de 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -8,6 +8,12 @@
 #
 
 ## Implements Nim's `spawn <manual.html#parallel-amp-spawn>`_.
+##
+## **See also:**
+## * `threads module <threads.html>`_
+## * `chanels module <channels.html>`_
+## * `locks module <locks.html>`_
+## * `asyncdispatch module <asyncdispatch.html>`_
 
 when not compileOption("threads"):
   {.error: "Threadpool requires --threads:on option.".}
@@ -53,7 +59,7 @@ type
       cacheAlign: array[CacheLineSize-4*sizeof(int), byte]
     left: int
     cacheAlign2: array[CacheLineSize-sizeof(int), byte]
-    interest: bool ## wether the master is interested in the "all done" event
+    interest: bool # whether the master is interested in the "all done" event
 
 proc barrierEnter(b: ptr Barrier) {.compilerProc, inline.} =
   # due to the signaling between threads, it is ensured we are the only
@@ -93,11 +99,10 @@ type
     cv: Semaphore
     idx: int
 
-  FlowVarBase* = ref FlowVarBaseObj ## untyped base class for 'FlowVar[T]'
+  FlowVarBase* = ref FlowVarBaseObj ## Untyped base class for ``FlowVar[T]``.
   FlowVarBaseObj = object of RootObj
     ready, usesSemaphore, awaited: bool
-    cv: Semaphore #\
-    # for 'blockUntilAny' support
+    cv: Semaphore  # for 'blockUntilAny' support
     ai: ptr AwaitInfo
     idx: int
     data: pointer  # we incRef and unref it to keep it alive; note this MUST NOT
@@ -107,7 +112,7 @@ type
   FlowVarObj[T] = object of FlowVarBaseObj
     blob: T
 
-  FlowVar*{.compilerProc.}[T] = ref FlowVarObj[T] ## a data flow variable
+  FlowVar*{.compilerProc.}[T] = ref FlowVarObj[T] ## A data flow variable.
 
   ToFreeQueue = object
     len: int
@@ -129,8 +134,9 @@ type
     readyForTask: Semaphore
 
 proc blockUntil*(fv: FlowVarBase) =
-  ## waits until the value for the flowVar arrives. Usually it is not necessary
-  ## to call this explicitly.
+  ## Waits until the value for the ``fv`` arrives.
+  ##
+  ## Usually it is not necessary to call this explicitly.
   if fv.usesSemaphore and not fv.awaited:
     fv.awaited = true
     blockUntil(fv.cv)
@@ -216,10 +222,12 @@ proc nimFlowVarSignal(fv: FlowVarBase) {.compilerProc.} =
     signal(fv.cv)
 
 proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
-  ## blocks until the ``fv`` is available and then passes its value
-  ## to ``action``. Note that due to Nim's parameter passing semantics this
-  ## means that ``T`` doesn't need to be copied and so ``awaitAndThen`` can
-  ## sometimes be more efficient than ``^``.
+  ## Blocks until the ``fv`` is available and then passes its value
+  ## to ``action``.
+  ##
+  ## Note that due to Nim's parameter passing semantics this
+  ## means that ``T`` doesn't need to be copied so ``awaitAndThen`` can
+  ## sometimes be more efficient than `^ proc <#^,FlowVar[T]>`_.
   blockUntil(fv)
   when T is string or T is seq:
     action(cast[T](fv.data))
@@ -230,18 +238,18 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
   finished(fv)
 
 proc unsafeRead*[T](fv: FlowVar[ref T]): ptr T =
-  ## blocks until the value is available and then returns this value.
+  ## Blocks until the value is available and then returns this value.
   blockUntil(fv)
   result = cast[ptr T](fv.data)
 
 proc `^`*[T](fv: FlowVar[ref T]): ref T =
-  ## blocks until the value is available and then returns this value.
+  ## Blocks until the value is available and then returns this value.
   blockUntil(fv)
   let src = cast[ref T](fv.data)
   deepCopy result, src
 
 proc `^`*[T](fv: FlowVar[T]): T =
-  ## blocks until the value is available and then returns this value.
+  ## Blocks until the value is available and then returns this value.
   blockUntil(fv)
   when T is string or T is seq:
     # XXX closures? deepCopy?
@@ -250,11 +258,14 @@ proc `^`*[T](fv: FlowVar[T]): T =
     result = fv.blob
 
 proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int =
-  ## awaits any of the given flowVars. Returns the index of one flowVar for
-  ## which a value arrived. A flowVar only supports one call to 'blockUntilAny' at
-  ## the same time. That means if you blockUntilAny([a,b]) and blockUntilAny([b,c]) the second
-  ## call will only blockUntil 'c'. If there is no flowVar left to be able to wait
-  ## on, -1 is returned.
+  ## Awaits any of the given ``flowVars``. Returns the index of one ``flowVar``
+  ## for which a value arrived.
+  ##
+  ## A ``flowVar`` only supports one call to ``blockUntilAny`` at the same time.
+  ## That means if you ``blockUntilAny([a,b])`` and ``blockUntilAny([b,c])``
+  ## the second call will only block until ``c``. If there is no ``flowVar`` left
+  ## to be able to wait on, -1 is returned.
+  ##
   ## **Note**: This results in non-deterministic behaviour and should be avoided.
   var ai: AwaitInfo
   ai.cv.initSemaphore()
@@ -278,7 +289,7 @@ proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int =
 proc isReady*(fv: FlowVarBase): bool =
   ## Determines whether the specified ``FlowVarBase``'s value is available.
   ##
-  ## If ``true`` awaiting ``fv`` will not block.
+  ## If ``true``, awaiting ``fv`` will not block.
   if fv.usesSemaphore and not fv.awaited:
     acquire(fv.cv.L)
     result = fv.cv.counter > 0
@@ -291,9 +302,9 @@ proc nimArgsPassingDone(p: pointer) {.compilerProc.} =
   signal(w.taskStarted)
 
 const
-  MaxThreadPoolSize* = 256 ## maximal size of the thread pool. 256 threads
+  MaxThreadPoolSize* = 256 ## Maximum size of the thread pool. 256 threads
                            ## should be good enough for anybody ;-)
-  MaxDistinguishedThread* = 32 ## maximal number of "distinguished" threads.
+  MaxDistinguishedThread* = 32 ## Maximum number of "distinguished" threads.
 
 type
   ThreadId* = range[0..MaxDistinguishedThread-1]
@@ -368,12 +379,12 @@ when defined(nimPinToCpu):
   var gCpus: Natural
 
 proc setMinPoolSize*(size: range[1..MaxThreadPoolSize]) =
-  ## sets the minimal thread pool size. The default value of this is 4.
+  ## Sets the minimum thread pool size. The default value of this is 4.
   minPoolSize = size
 
 proc setMaxPoolSize*(size: range[1..MaxThreadPoolSize]) =
-  ## sets the maximal thread pool size. The default value of this
-  ## is ``MaxThreadPoolSize``.
+  ## Sets the maximum thread pool size. The default value of this
+  ## is ``MaxThreadPoolSize`` (256).
   maxPoolSize = size
   if currentPoolSize > maxPoolSize:
     for i in maxPoolSize..currentPoolSize-1:
@@ -413,37 +424,46 @@ proc setup() =
   for i in 0..<currentPoolSize: activateWorkerThread(i)
 
 proc preferSpawn*(): bool =
-  ## Use this proc to determine quickly if a 'spawn' or a direct call is
-  ## preferable. If it returns 'true' a 'spawn' may make sense. In general
-  ## it is not necessary to call this directly; use 'spawnX' instead.
+  ## Use this proc to determine quickly if a ``spawn`` or a direct call is
+  ## preferable.
+  ##
+  ## If it returns ``true``, a ``spawn`` may make sense. In general
+  ## it is not necessary to call this directly; use `spawnX template
+  ## <#spawnX.t>`_ instead.
   result = gSomeReady.counter > 0
 
 proc spawn*(call: typed): void {.magic: "Spawn".}
-  ## always spawns a new task, so that the 'call' is never executed on
-  ## the calling thread. 'call' has to be proc call 'p(...)' where 'p'
-  ## is gcsafe and has a return type that is either 'void' or compatible
-  ## with ``FlowVar[T]``.
+  ## Always spawns a new task, so that the ``call`` is never executed on
+  ## the calling thread.
+  ##
+  ## ``call`` has to be proc call ``p(...)`` where ``p`` is gcsafe and has a
+  ## return type that is either ``void`` or compatible with ``FlowVar[T]``.
 
 proc pinnedSpawn*(id: ThreadId; call: typed): void {.magic: "Spawn".}
-  ## always spawns a new task on the worker thread with ``id``, so that
-  ## the 'call' is **always** executed on
-  ## the thread. 'call' has to be proc call 'p(...)' where 'p'
-  ## is gcsafe and has a return type that is either 'void' or compatible
-  ## with ``FlowVar[T]``.
+  ## Always spawns a new task on the worker thread with ``id``, so that
+  ## the ``call`` is **always** executed on the thread.
+  ##
+  ## ``call`` has to be proc call ``p(...)`` where ``p`` is gcsafe and has a
+  ## return type that is either ``void`` or compatible with ``FlowVar[T]``.
 
 template spawnX*(call): void =
-  ## spawns a new task if a CPU core is ready, otherwise executes the
-  ## call in the calling thread. Usually it is advised to
-  ## use 'spawn' in order to not block the producer for an unknown
-  ## amount of time. 'call' has to be proc call 'p(...)' where 'p'
-  ## is gcsafe and has a return type that is either 'void' or compatible
-  ## with ``FlowVar[T]``.
+  ## Spawns a new task if a CPU core is ready, otherwise executes the
+  ## call in the calling thread.
+  ##
+  ## Usually it is advised to use `spawn proc <#spawn,typed>`_ in order to
+  ## not block the producer for an unknown amount of time.
+  ##
+  ## ``call`` has to be proc call ``p(...)`` where ``p`` is gcsafe and has a
+  ## return type that is either 'void' or compatible with ``FlowVar[T]``.
   (if preferSpawn(): spawn call else: call)
 
 proc parallel*(body: untyped) {.magic: "Parallel".}
-  ## a parallel section can be used to execute a block in parallel. ``body``
-  ## has to be in a DSL that is a particular subset of the language. Please
-  ## refer to the manual for further information.
+  ## A parallel section can be used to execute a block in parallel.
+  ##
+  ## ``body`` has to be in a DSL that is a particular subset of the language.
+  ##
+  ## Please refer to `the manual <manual.html#parallel-amp-spawn>`_
+  ## for further information.
 
 var
   state: ThreadPoolState
@@ -547,8 +567,9 @@ proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerProc.} =
 
 
 proc sync*() =
-  ## a simple barrier to wait for all spawn'ed tasks. If you need more elaborate
-  ## waiting, you have to use an explicit barrier.
+  ## A simple barrier to wait for all ``spawn``'ed tasks.
+  ##
+  ## If you need more elaborate waiting, you have to use an explicit barrier.
   var toRelease = 0
   while true:
     var allReady = true
diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim
index 6535bb0d3..a08a583c0 100644
--- a/lib/pure/hashes.nim
+++ b/lib/pure/hashes.nim
@@ -9,11 +9,11 @@
 
 ## This module implements efficient computations of hash values for diverse
 ## Nim types. All the procs are based on these two building blocks:
-## - `!& proc <#!&>`_ used to start or mix a hash value, and
-## - `!$ proc <#!$>`_ used to *finish* the hash value.
-## If you want to implement hash procs for
-## your custom types you will end up writing the following kind of skeleton of
-## code:
+## - `!& proc <#!&,Hash,int>`_ used to start or mix a hash value, and
+## - `!$ proc <#!$,Hash>`_ used to *finish* the hash value.
+##
+## If you want to implement hash procs for your custom types,
+## you will end up writing the following kind of skeleton of code:
 ##
 ## .. code-block:: Nim
 ##  proc hash(x: Something): Hash =
@@ -37,31 +37,40 @@
 ##    h = h !& hash(x.foo)
 ##    h = h !& hash(x.bar)
 ##    result = !$h
+##
+## **See also:**
+## * `md5 module <md5.html>`_ for MD5 checksum algorithm
+## * `base64 module <base64.html>`_ for a base64 encoder and decoder
+## * `std/sha1 module <sha1.html>`_ for a sha1 encoder and decoder
+## * `tables modlule <tables.html>`_ for hash tables
+
 
 import
   strutils
 
 type
-  Hash* = int ## a hash value; hash tables using these values should
+  Hash* = int  ## A hash value. Hash tables using these values should
                ## always have a size of a power of two and can use the ``and``
                ## operator instead of ``mod`` for truncation of the hash value.
 
 proc `!&`*(h: Hash, val: int): Hash {.inline.} =
-  ## mixes a hash value `h` with `val` to produce a new hash value. This is
-  ## only needed if you need to implement a hash proc for a new datatype.
+  ## Mixes a hash value `h` with `val` to produce a new hash value.
+  ##
+  ## This is only needed if you need to implement a hash proc for a new datatype.
   result = h +% val
   result = result +% result shl 10
   result = result xor (result shr 6)
 
 proc `!$`*(h: Hash): Hash {.inline.} =
-  ## finishes the computation of the hash value. This is
-  ## only needed if you need to implement a hash proc for a new datatype.
+  ## Finishes the computation of the hash value.
+  ##
+  ## This is only needed if you need to implement a hash proc for a new datatype.
   result = h +% h shl 3
   result = result xor (result shr 11)
   result = result +% result shl 15
 
 proc hashData*(data: pointer, size: int): Hash =
-  ## hashes an array of bytes of size `size`
+  ## Hashes an array of bytes of size `size`.
   var h: Hash = 0
   when defined(js):
     var p: cstring
@@ -80,7 +89,7 @@ when defined(js):
   var objectID = 0
 
 proc hash*(x: pointer): Hash {.inline.} =
-  ## efficient hashing of pointers
+  ## Efficient hashing of pointers.
   when defined(js):
     asm """
       if (typeof `x` == "object") {
@@ -97,45 +106,57 @@ proc hash*(x: pointer): Hash {.inline.} =
 
 when not defined(booting):
   proc hash*[T: proc](x: T): Hash {.inline.} =
-    ## efficient hashing of proc vars; closures are supported too.
+    ## Efficient hashing of proc vars. Closures are supported too.
     when T is "closure":
       result = hash(rawProc(x)) !& hash(rawEnv(x))
     else:
       result = hash(pointer(x))
 
 proc hash*(x: int): Hash {.inline.} =
-  ## efficient hashing of integers
+  ## Efficient hashing of integers.
   result = x
 
 proc hash*(x: int64): Hash {.inline.} =
-  ## efficient hashing of int64 integers
+  ## Efficient hashing of `int64` integers.
   result = toU32(x)
 
 proc hash*(x: uint): Hash {.inline.} =
-  ## efficient hashing of unsigned integers
+  ## Efficient hashing of unsigned integers.
   result = cast[int](x)
 
 proc hash*(x: uint64): Hash {.inline.} =
-  ## efficient hashing of uint64 integers
+  ## Efficient hashing of `uint64` integers.
   result = toU32(cast[int](x))
 
 proc hash*(x: char): Hash {.inline.} =
-  ## efficient hashing of characters
+  ## Efficient hashing of characters.
   result = ord(x)
 
 proc hash*[T: Ordinal](x: T): Hash {.inline.} =
-  ## efficient hashing of other ordinal types (e.g., enums)
+  ## Efficient hashing of other ordinal types (e.g. enums).
   result = ord(x)
 
 proc hash*(x: string): Hash =
-  ## efficient hashing of strings
+  ## Efficient hashing of strings.
+  ##
+  ## See also:
+  ## * `hashIgnoreStyle <#hashIgnoreStyle,string>`_
+  ## * `hashIgnoreCase <#hashIgnoreCase,string>`_
+  runnableExamples:
+    doAssert hash("abracadabra") == -5600162842546114722
+    doAssert hash("Abracadabra") == 2068684413884279454
+
   var h: Hash = 0
   for i in 0..x.len-1:
     h = h !& ord(x[i])
   result = !$h
 
 proc hash*(x: cstring): Hash =
-  ## efficient hashing of null-terminated strings
+  ## Efficient hashing of null-terminated strings.
+  runnableExamples:
+    doAssert hash(cstring"abracadabra") == -5600162842546114722
+    doAssert hash(cstring"Abracadabra") == 2068684413884279454
+
   var h: Hash = 0
   var i = 0
   when defined(js):
@@ -149,17 +170,27 @@ proc hash*(x: cstring): Hash =
   result = !$h
 
 proc hash*(sBuf: string, sPos, ePos: int): Hash =
-  ## efficient hashing of a string buffer, from starting
-  ## position `sPos` to ending position `ePos`
+  ## Efficient hashing of a string buffer, from starting
+  ## position `sPos` to ending position `ePos` (included).
   ##
-  ## ``hash(myStr, 0, myStr.high)`` is equivalent to ``hash(myStr)``
+  ## ``hash(myStr, 0, myStr.high)`` is equivalent to ``hash(myStr)``.
+  runnableExamples:
+    var a = "abracadabra"
+    doAssert hash(a, 0, 3) == hash(a, 7, 10)
+
   var h: Hash = 0
   for i in sPos..ePos:
     h = h !& ord(sBuf[i])
   result = !$h
 
 proc hashIgnoreStyle*(x: string): Hash =
-  ## efficient hashing of strings; style is ignored
+  ## Efficient hashing of strings; style is ignored.
+  ##
+  ## See also:
+  ## * `hashIgnoreCase <#hashIgnoreCase,string>`_
+  runnableExamples:
+    doAssert hashIgnoreStyle("aBr_aCa_dAB_ra") == hash("abracadabra")
+
   var h: Hash = 0
   var i = 0
   let xLen = x.len
@@ -176,11 +207,15 @@ proc hashIgnoreStyle*(x: string): Hash =
   result = !$h
 
 proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash =
-  ## efficient hashing of a string buffer, from starting
-  ## position `sPos` to ending position `ePos`; style is ignored
+  ## Efficient hashing of a string buffer, from starting
+  ## position `sPos` to ending position `ePos` (included); style is ignored.
   ##
   ## ``hashIgnoreStyle(myBuf, 0, myBuf.high)`` is equivalent
-  ## to ``hashIgnoreStyle(myBuf)``
+  ## to ``hashIgnoreStyle(myBuf)``.
+  runnableExamples:
+    var a = "ABracada_b_r_a"
+    doAssert hashIgnoreStyle(a, 0, 3) == hashIgnoreStyle(a, 7, a.high)
+
   var h: Hash = 0
   var i = sPos
   while i <= ePos:
@@ -195,7 +230,13 @@ proc hashIgnoreStyle*(sBuf: string, sPos, ePos: int): Hash =
   result = !$h
 
 proc hashIgnoreCase*(x: string): Hash =
-  ## efficient hashing of strings; case is ignored
+  ## Efficient hashing of strings; case is ignored.
+  ##
+  ## See also:
+  ## * `hashIgnoreStyle <#hashIgnoreStyle,string>`_
+  runnableExamples:
+    doAssert hashIgnoreCase("ABRAcaDABRA") == hashIgnoreCase("abRACAdabra")
+
   var h: Hash = 0
   for i in 0..x.len-1:
     var c = x[i]
@@ -205,11 +246,15 @@ proc hashIgnoreCase*(x: string): Hash =
   result = !$h
 
 proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash =
-  ## efficient hashing of a string buffer, from starting
-  ## position `sPos` to ending position `ePos`; case is ignored
+  ## Efficient hashing of a string buffer, from starting
+  ## position `sPos` to ending position `ePos` (included); case is ignored.
   ##
   ## ``hashIgnoreCase(myBuf, 0, myBuf.high)`` is equivalent
-  ## to ``hashIgnoreCase(myBuf)``
+  ## to ``hashIgnoreCase(myBuf)``.
+  runnableExamples:
+    var a = "ABracadabRA"
+    doAssert hashIgnoreCase(a, 0, 3) == hashIgnoreCase(a, 7, 10)
+
   var h: Hash = 0
   for i in sPos..ePos:
     var c = sBuf[i]
@@ -219,7 +264,7 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash =
   result = !$h
 
 proc hash*(x: float): Hash {.inline.} =
-  ## efficient hashing of floats.
+  ## Efficient hashing of floats.
   var y = x + 1.0
   result = cast[ptr Hash](addr(y))[]
 
@@ -231,34 +276,40 @@ proc hash*[A](x: set[A]): Hash
 
 
 proc hash*[T: tuple](x: T): Hash =
-  ## efficient hashing of tuples.
+  ## Efficient hashing of tuples.
   for f in fields(x):
     result = result !& hash(f)
   result = !$result
 
 proc hash*[A](x: openArray[A]): Hash =
-  ## efficient hashing of arrays and sequences.
+  ## Efficient hashing of arrays and sequences.
   for it in items(x): result = result !& hash(it)
   result = !$result
 
 proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash =
-  ## efficient hashing of portions of arrays and sequences.
+  ## Efficient hashing of portions of arrays and sequences, from starting
+  ## position `sPos` to ending position `ePos` (included).
   ##
-  ## ``hash(myBuf, 0, myBuf.high)`` is equivalent to ``hash(myBuf)``
+  ## ``hash(myBuf, 0, myBuf.high)`` is equivalent to ``hash(myBuf)``.
+  runnableExamples:
+    let a = [1, 2, 5, 1, 2, 6]
+    doAssert hash(a, 0, 1) == hash(a, 3, 4)
+
   for i in sPos..ePos:
     result = result !& hash(aBuf[i])
   result = !$result
 
 proc hash*[A](x: set[A]): Hash =
-  ## efficient hashing of sets.
+  ## Efficient hashing of sets.
   for it in items(x): result = result !& hash(it)
   result = !$result
 
+
 when isMainModule:
   doAssert( hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13) )
   doAssert( hash("aa bb aaaa1234") == hash(cstring("aa bb aaaa1234")) )
-  doAssert( hashIgnoreCase("aa bb aaaa1234") == hash("aa bb aaaa1234") )
-  doAssert( hashIgnoreStyle("aa bb aaaa1234") == hashIgnoreCase("aa bb aaaa1234") )
+  doAssert( hashIgnoreCase("aA bb aAAa1234") == hash("aa bb aaaa1234") )
+  doAssert( hashIgnoreStyle("aa_bb_AAaa1234") == hashIgnoreCase("aaBBAAAa1234") )
   let xx = @['H','e','l','l','o']
   let ss = "Hello"
   doAssert( hash(xx) == hash(ss) )
diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim
index 1ff3a9824..028223c93 100644
--- a/lib/pure/md5.nim
+++ b/lib/pure/md5.nim
@@ -7,13 +7,20 @@
 #    distribution, for details about the copyright.
 #
 
-## Module for computing MD5 checksums.
+## Module for computing `MD5 checksums <https://en.wikipedia.org/wiki/MD5>`_.
+##
+## **See also:**
+## * `base64 module<base64.html>`_ implements a base64 encoder and decoder
+## * `std/sha1 module <sha1.html>`_ for a sha1 encoder and decoder
+## * `hashes module<hashes.html>`_ for efficient computations of hash values
+##   for diverse Nim types
 
 type
   MD5State = array[0..3, uint32]
   MD5Block = array[0..15, uint32]
   MD5CBits = array[0..7, uint8]
-  MD5Digest* = array[0..15, uint8]
+  MD5Digest* = array[0..15, uint8] ## \
+    ## MD5 checksum of a string, obtained with `toMD5 proc <#toMD5,string>`_.
   MD5Buffer = array[0..63, uint8]
   MD5Context* {.final.} = object
     state: MD5State
@@ -161,8 +168,62 @@ proc transform(buffer: pointer, state: var MD5State) =
   state[2] = state[2] + c
   state[3] = state[3] + d
 
+proc md5Init*(c: var MD5Context) {.raises: [], tags: [].}
+proc md5Update*(c: var MD5Context, input: cstring, len: int) {.raises: [], tags: [].}
+proc md5Final*(c: var MD5Context, digest: var MD5Digest) {.raises: [], tags: [].}
+
+
+proc toMD5*(s: string): MD5Digest =
+  ## Computes the `MD5Digest` value for a string `s`.
+  ##
+  ## See also:
+  ## * `getMD5 proc <#getMD5,string>`_ which returns a string representation
+  ##   of the `MD5Digest`
+  ## * `$ proc <#$,MD5Digest>`_ for converting MD5Digest to string
+  runnableExamples:
+    assert $toMD5("abc") == "900150983cd24fb0d6963f7d28e17f72"
+
+  var c: MD5Context
+  md5Init(c)
+  md5Update(c, cstring(s), len(s))
+  md5Final(c, result)
+
+proc `$`*(d: MD5Digest): string =
+  ## Converts a `MD5Digest` value into its string representation.
+  const digits = "0123456789abcdef"
+  result = ""
+  for i in 0..15:
+    add(result, digits[(d[i].int shr 4) and 0xF])
+    add(result, digits[d[i].int and 0xF])
+
+proc getMD5*(s: string): string =
+  ## Computes an MD5 value of `s` and returns its string representation.
+  ##
+  ## See also:
+  ## * `toMD5 proc <#toMD5,string>`_ which returns the `MD5Digest` of a string
+  runnableExamples:
+    assert getMD5("abc") == "900150983cd24fb0d6963f7d28e17f72"
+
+  var
+    c: MD5Context
+    d: MD5Digest
+  md5Init(c)
+  md5Update(c, cstring(s), len(s))
+  md5Final(c, d)
+  result = $d
+
+proc `==`*(D1, D2: MD5Digest): bool =
+  ## Checks if two `MD5Digest` values are identical.
+  for i in 0..15:
+    if D1[i] != D2[i]: return false
+  return true
+
+
 proc md5Init*(c: var MD5Context) =
-  ## initializes a MD5Context
+  ## Initializes a `MD5Context`.
+  ##
+  ## If you use `toMD5 proc <#toMD5,string>`_ there's no need to call this
+  ## function explicitly.
   c.state[0] = 0x67452301'u32
   c.state[1] = 0xEFCDAB89'u32
   c.state[2] = 0x98BADCFE'u32
@@ -172,7 +233,10 @@ proc md5Init*(c: var MD5Context) =
   zeroMem(addr(c.buffer), sizeof(MD5buffer))
 
 proc md5Update*(c: var MD5Context, input: cstring, len: int) =
-  ## updates the MD5Context with the `input` data of length `len`
+  ## Updates the `MD5Context` with the `input` data of length `len`.
+  ##
+  ## If you use `toMD5 proc <#toMD5,string>`_ there's no need to call this
+  ## function explicitly.
   var input = input
   var Index = int((c.count[0] shr 3) and 0x3F)
   c.count[0] = c.count[0] + (uint32(len) shl 3)
@@ -191,7 +255,10 @@ proc md5Update*(c: var MD5Context, input: cstring, len: int) =
     copyMem(addr(c.buffer[Index]), addr(input[0]), len)
 
 proc md5Final*(c: var MD5Context, digest: var MD5Digest) =
-  ## finishes the MD5Context and stores the result in `digest`
+  ## Finishes the `MD5Context` and stores the result in `digest`.
+  ##
+  ## If you use `toMD5 proc <#toMD5,string>`_ there's no need to call this
+  ## function explicitly.
   var
     Bits: MD5CBits
     PadLen: int
@@ -204,36 +271,6 @@ proc md5Final*(c: var MD5Context, digest: var MD5Digest) =
   decode(digest, c.state)
   zeroMem(addr(c), sizeof(MD5Context))
 
-proc toMD5*(s: string): MD5Digest =
-  ## computes the MD5Digest value for a string `s`
-  var c: MD5Context
-  md5Init(c)
-  md5Update(c, cstring(s), len(s))
-  md5Final(c, result)
-
-proc `$`*(d: MD5Digest): string =
-  ## converts a MD5Digest value into its string representation
-  const digits = "0123456789abcdef"
-  result = ""
-  for i in 0..15:
-    add(result, digits[(d[i].int shr 4) and 0xF])
-    add(result, digits[d[i].int and 0xF])
-
-proc getMD5*(s: string): string =
-  ## computes an MD5 value of `s` and returns its string representation
-  var
-    c: MD5Context
-    d: MD5Digest
-  md5Init(c)
-  md5Update(c, cstring(s), len(s))
-  md5Final(c, d)
-  result = $d
-
-proc `==`*(D1, D2: MD5Digest): bool =
-  ## checks if two MD5Digest values are identical
-  for i in 0..15:
-    if D1[i] != D2[i]: return false
-  return true
 
 when isMainModule:
   assert(getMD5("Franz jagt im komplett verwahrlosten Taxi quer durch Bayern") ==
diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim
index a373a9370..b6cbaf558 100644
--- a/lib/pure/typetraits.nim
+++ b/lib/pure/typetraits.nim
@@ -8,7 +8,7 @@
 #
 
 ## This module defines compile-time reflection procs for
-## working with types
+## working with types.
 
 include "system/helpers" # for `isNamedTuple`
 
@@ -16,11 +16,13 @@ export system.`$`
 export isNamedTuple
 
 proc name*(t: typedesc): string {.magic: "TypeTrait".}
-  ## Alias for system.`$`(t) since Nim v0.20.0.
+  ## Returns the name of the given type.
+  ##
+  ## Alias for system.`$`(t) since Nim v0.20.
 
 proc arity*(t: typedesc): int {.magic: "TypeTrait".} =
-  ## Returns the arity of the given type. This is the number of "type" components or
-  ## the number of generic parameters a given type ``t`` has.
+  ## Returns the arity of the given type. This is the number of "type"
+  ## components or the number of generic parameters a given type ``t`` has.
   runnableExamples:
     assert arity(seq[string]) == 1
     assert arity(array[3, int]) == 2
@@ -31,19 +33,35 @@ proc genericHead*(t: typedesc): typedesc {.magic: "TypeTrait".}
   ## uninstantiated form.
   ##
   ## For example:
-  ##   seq[int].genericHead will be just seq
-  ##   seq[int].genericHead[float] will be seq[float]
+  ## * `seq[int].genericHead` will be just `seq`
+  ## * `seq[int].genericHead[float]` will be `seq[float]`
   ##
   ## A compile-time error will be produced if the supplied type
   ## is not generic.
+  ##
+  ## See also:
+  ## * `stripGenericParams <#stripGenericParams,typedesc>`_
+  ##
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##   type
+  ##     Functor[A] = concept f
+  ##       type MatchedGenericType = genericHead(f.type)
+  ##         # `f` will be a value of a type such as `Option[T]`
+  ##         # `MatchedGenericType` will become the `Option` type
+
 
 proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".}
-  ## This trait is similar to `genericHead`, but instead of producing
-  ## error for non-generic types, it will just return them unmodified.
+  ## This trait is similar to `genericHead <#genericHead,typedesc>`_, but
+  ## instead of producing error for non-generic types, it will just return
+  ## them unmodified.
 
 proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
   ## This trait returns true iff the type ``t`` is safe to use for
-  ## `copyMem`:idx:. Other languages name a type like these `blob`:idx:.
+  ## `copyMem`:idx:.
+  ##
+  ## Other languages name a type like these `blob`:idx:.
 
 
 when isMainModule:
diff --git a/lib/system/channels.nim b/lib/system/channels.nim
index e0eb9ae13..27293c2d4 100644
--- a/lib/system/channels.nim
+++ b/lib/system/channels.nim
@@ -178,7 +178,7 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
     copyMem(dest, src, mt.size) # copy raw bits
 
 proc rawSend(q: PRawChannel, data: pointer, typ: PNimType) =
-  ## adds an `item` to the end of the queue `q`.
+  ## Adds an `item` to the end of the queue `q`.
   var cap = q.mask+1
   if q.count >= cap:
     # start with capacity for 2 entries in the queue:
@@ -232,11 +232,14 @@ proc sendImpl(q: PRawChannel, typ: PNimType, msg: pointer, noBlock: bool): bool
   result = true
 
 proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) {.inline.} =
-  ## sends a message to a thread. `msg` is deeply copied.
+  ## Sends a message to a thread. `msg` is deeply copied.
   discard sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), false)
 
 proc trySend*[TMsg](c: var Channel[TMsg], msg: TMsg): bool {.inline.} =
-  ## Tries to send a message to a thread. `msg` is deeply copied. Doesn't block.
+  ## Tries to send a message to a thread.
+  ##
+  ## `msg` is deeply copied. Doesn't block.
+  ##
   ## Returns `false` if the message was not sent because number of pending items
   ## in the channel exceeded `maxItems`.
   sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), true)
@@ -255,8 +258,10 @@ proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
     signalSysCond(q.cond)
 
 proc recv*[TMsg](c: var Channel[TMsg]): TMsg =
-  ## receives a message from the channel `c`. This blocks until
-  ## a message has arrived! You may use ``peek`` to avoid the blocking.
+  ## Receives a message from the channel `c`.
+  ##
+  ## This blocks until a message has arrived!
+  ## You may use `peek proc <#peek,Channel[TMsg]>`_ to avoid the blocking.
   var q = cast[PRawChannel](addr(c))
   acquireSys(q.lock)
   llRecv(q, addr(result), cast[PNimType](getTypeInfo(result)))
@@ -265,8 +270,9 @@ proc recv*[TMsg](c: var Channel[TMsg]): TMsg =
 proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool,
                                                   msg: TMsg] =
   ## Tries to receive a message from the channel `c`, but this can fail
-  ## for all sort of reasons, including contention. If it fails,
-  ## it returns ``(false, default(msg))`` otherwise it
+  ## for all sort of reasons, including contention.
+  ##
+  ## If it fails, it returns ``(false, default(msg))`` otherwise it
   ## returns ``(true, msg)``.
   var q = cast[PRawChannel](addr(c))
   if q.mask != ChannelDeadMask:
@@ -277,9 +283,12 @@ proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool,
       releaseSys(q.lock)
 
 proc peek*[TMsg](c: var Channel[TMsg]): int =
-  ## returns the current number of messages in the channel `c`. Returns -1
-  ## if the channel has been closed. **Note**: This is dangerous to use
-  ## as it encourages races. It's much better to use ``tryRecv`` instead.
+  ## Returns the current number of messages in the channel `c`.
+  ##
+  ## Returns -1 if the channel has been closed.
+  ##
+  ## **Note**: This is dangerous to use as it encourages races.
+  ## It's much better to use `tryRecv proc <#tryRecv,Channel[TMsg]>`_ instead.
   var q = cast[PRawChannel](addr(c))
   if q.mask != ChannelDeadMask:
     lockChannel(q):
@@ -288,17 +297,20 @@ proc peek*[TMsg](c: var Channel[TMsg]): int =
     result = -1
 
 proc open*[TMsg](c: var Channel[TMsg], maxItems: int = 0) =
-  ## opens a channel `c` for inter thread communication. The `send` operation
-  ## will block until number of unprocessed items is less than `maxItems`.
+  ## Opens a channel `c` for inter thread communication.
+  ##
+  ## The `send` operation will block until number of unprocessed items is
+  ## less than `maxItems`.
+  ##
   ## For unlimited queue set `maxItems` to 0.
   initRawChannel(addr(c), maxItems)
 
 proc close*[TMsg](c: var Channel[TMsg]) =
-  ## closes a channel `c` and frees its associated resources.
+  ## Closes a channel `c` and frees its associated resources.
   deinitRawChannel(addr(c))
 
 proc ready*[TMsg](c: var Channel[TMsg]): bool =
-  ## returns true iff some thread is waiting on the channel `c` for
+  ## Returns true iff some thread is waiting on the channel `c` for
   ## new messages.
   var q = cast[PRawChannel](addr(c))
   result = q.ready
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index bbe170376..7a988db46 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -7,8 +7,10 @@
 #    distribution, for details about the copyright.
 #
 
-## Thread support for Nim. **Note**: This is part of the system module.
-## Do not import it directly. To activate thread support you need to compile
+## Thread support for Nim.
+##
+## **Note**: This is part of the system module. Do not import it directly.
+## To activate thread support you need to compile
 ## with the ``--threads:on`` command line switch.
 ##
 ## Nim's memory model for threads is quite different from other common
@@ -381,6 +383,7 @@ var
 proc onThreadDestruction*(handler: proc () {.closure, gcsafe.}) =
   ## Registers a *thread local* handler that is called at the thread's
   ## destruction.
+  ##
   ## A thread is destructed when the ``.thread`` proc returns
   ## normally or when it raises an exception. Note that unhandled exceptions
   ## in a thread nevertheless cause the whole process to die.
@@ -481,22 +484,22 @@ else:
 {.pop.}
 
 proc running*[TArg](t: Thread[TArg]): bool {.inline.} =
-  ## returns true if `t` is running.
+  ## Returns true if `t` is running.
   result = t.dataFn != nil
 
 proc handle*[TArg](t: Thread[TArg]): SysThread {.inline.} =
-  ## returns the thread handle of `t`.
+  ## Returns the thread handle of `t`.
   result = t.sys
 
 when hostOS == "windows":
   const MAXIMUM_WAIT_OBJECTS = 64
 
   proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
-    ## waits for the thread `t` to finish.
+    ## Waits for the thread `t` to finish.
     discard waitForSingleObject(t.sys, -1'i32)
 
   proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
-    ## waits for every thread in `t` to finish.
+    ## Waits for every thread in `t` to finish.
     var a: array[MAXIMUM_WAIT_OBJECTS, SysThread]
     var k = 0
     while k < len(t):
@@ -508,25 +511,25 @@ when hostOS == "windows":
 
 elif defined(genode):
   proc joinThread*[TArg](t: Thread[TArg]) {.importcpp.}
-    ## waits for the thread `t` to finish.
+    ## Waits for the thread `t` to finish.
 
   proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
-    ## waits for every thread in `t` to finish.
+    ## Waits for every thread in `t` to finish.
     for i in 0..t.high: joinThread(t[i])
 
 else:
   proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
-    ## waits for the thread `t` to finish.
+    ## Waits for the thread `t` to finish.
     discard pthread_join(t.sys, nil)
 
   proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
-    ## waits for every thread in `t` to finish.
+    ## Waits for every thread in `t` to finish.
     for i in 0..t.high: joinThread(t[i])
 
 when false:
   # XXX a thread should really release its heap here somehow:
   proc destroyThread*[TArg](t: var Thread[TArg]) =
-    ## forces the thread `t` to terminate. This is potentially dangerous if
+    ## Forces the thread `t` to terminate. This is potentially dangerous if
     ## you don't have full control over `t` and its acquired resources.
     when hostOS == "windows":
       discard TerminateThread(t.sys, 1'i32)
@@ -543,8 +546,10 @@ when hostOS == "windows":
   proc createThread*[TArg](t: var Thread[TArg],
                            tp: proc (arg: TArg) {.thread, nimcall.},
                            param: TArg) =
-    ## creates a new thread `t` and starts its execution. Entry point is the
-    ## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you
+    ## Creates a new thread `t` and starts its execution.
+    ##
+    ## Entry point is the proc `tp`.
+    ## `param` is passed to `tp`. `TArg` can be ``void`` if you
     ## don't need to pass any data to the thread.
     t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
 
@@ -558,14 +563,15 @@ when hostOS == "windows":
       raise newException(ResourceExhaustedError, "cannot create thread")
 
   proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
-    ## pins a thread to a `CPU`:idx:. In other words sets a
-    ## thread's `affinity`:idx:. If you don't know what this means, you
-    ## shouldn't use this proc.
+    ## Pins a thread to a `CPU`:idx:.
+    ##
+    ## In other words sets a thread's `affinity`:idx:.
+    ## If you don't know what this means, you shouldn't use this proc.
     setThreadAffinityMask(t.sys, uint(1 shl cpu))
 
 elif defined(genode):
   var affinityOffset: cuint = 1
-    ## CPU affinity offset for next thread, safe to roll-over
+    ## CPU affinity offset for next thread, safe to roll-over.
 
   proc createThread*[TArg](t: var Thread[TArg],
                            tp: proc (arg: TArg) {.thread, nimcall.},
@@ -589,8 +595,10 @@ else:
   proc createThread*[TArg](t: var Thread[TArg],
                            tp: proc (arg: TArg) {.thread, nimcall.},
                            param: TArg) =
-    ## creates a new thread `t` and starts its execution. Entry point is the
-    ## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you
+    ## Creates a new thread `t` and starts its execution.
+    ##
+    ## Entry point is the proc `tp`. `param` is passed to `tp`.
+    ## `TArg` can be ``void`` if you
     ## don't need to pass any data to the thread.
     t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
 
@@ -604,9 +612,10 @@ else:
       raise newException(ResourceExhaustedError, "cannot create thread")
 
   proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
-    ## pins a thread to a `CPU`:idx:. In other words sets a
-    ## thread's `affinity`:idx:. If you don't know what this means, you
-    ## shouldn't use this proc.
+    ## Pins a thread to a `CPU`:idx:.
+    ##
+    ## In other words sets a thread's `affinity`:idx:.
+    ## If you don't know what this means, you shouldn't use this proc.
     when not defined(macosx):
       var s {.noinit.}: CpuSet
       cpusetZero(s)
@@ -618,7 +627,7 @@ proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) =
 
 when false:
   proc mainThreadId*[TArg](): ThreadId[TArg] =
-    ## returns the thread ID of the main thread.
+    ## Returns the thread ID of the main thread.
     result = cast[ThreadId[TArg]](addr(mainThread))
 
 when useStackMaskHack:
@@ -632,7 +641,7 @@ var threadId {.threadvar.}: int
 
 when defined(windows):
   proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
+    ## Gets the ID of the currently running thread.
     if threadId == 0:
       threadId = int(getCurrentThreadId())
     result = threadId
@@ -645,7 +654,7 @@ elif defined(linux):
     var NR_gettid {.importc: "__NR_gettid", header: "<sys/syscall.h>".}: clong
 
   proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
+    ## Gets the ID of the currently running thread.
     if threadId == 0:
       threadId = int(syscall(NR_gettid))
     result = threadId
@@ -654,7 +663,7 @@ elif defined(dragonfly):
   proc lwp_gettid(): int32 {.importc, header: "unistd.h".}
 
   proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
+    ## Gets the ID of the currently running thread.
     if threadId == 0:
       threadId = int(lwp_gettid())
     result = threadId
@@ -672,7 +681,7 @@ elif defined(netbsd):
   proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".}
 
   proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
+    ## Gets the ID of the currently running thread.
     if threadId == 0:
       threadId = int(lwp_self())
     result = threadId
@@ -682,7 +691,7 @@ elif defined(freebsd):
   var SYS_thr_self {.importc:"SYS_thr_self", header:"<sys/syscall.h>"}: cint
 
   proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
+    ## Gets the ID of the currently running thread.
     var tid = 0.cint
     if threadId == 0:
       discard syscall(SYS_thr_self, addr tid)
@@ -694,7 +703,7 @@ elif defined(macosx):
   var SYS_thread_selfid {.importc:"SYS_thread_selfid", header:"<sys/syscall.h>".}: cint
 
   proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
+    ## Gets the ID of the currently running thread.
     if threadId == 0:
       threadId = int(syscall(SYS_thread_selfid))
     result = threadId
@@ -704,7 +713,7 @@ elif defined(solaris):
   proc thr_self(): thread_t {.importc, header: "<thread.h>".}
 
   proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
+    ## Gets the ID of the currently running thread.
     if threadId == 0:
       threadId = int(thr_self())
     result = threadId
@@ -714,7 +723,7 @@ elif defined(haiku):
   proc find_thread(name: cstring): thr_id {.importc, header: "<OS.h>".}
 
   proc getThreadId*(): int =
-    ## get the ID of the currently running thread.
+    ## Gets the ID of the currently running thread.
     if threadId == 0:
       threadId = int(find_thread(nil))
     result = threadId