improved documentation for several modules (#10752)

More detailed documentation for:
* md5
* hashes

Mostly cosmetic improvements for:
* threadpool
* typetraits
* channels
* threads
This commit is contained in:
Miran
2019-03-01 12:57:55 +01:00
committed by GitHub
parent e9d3c5de19
commit ca7980f301
6 changed files with 325 additions and 177 deletions

View File

@@ -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

View File

@@ -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) )

View File

@@ -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") ==

View File

@@ -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:

View File

@@ -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

View File

@@ -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