summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2017-03-03 13:00:09 +0100
committerAndreas Rumpf <rumpf_a@web.de>2017-03-03 13:00:09 +0100
commit5d125ca716b83abd850a5f270085a755dbd4c80c (patch)
treea5d4f6662b02fafb0a819e91056ad6e472883fb4 /lib
parent3b0430bec6be70e3312914d6034963319f860591 (diff)
parenta42801d100c2ec33b93af89f7fd2b17cfe5d4fbc (diff)
downloadNim-5d125ca716b83abd850a5f270085a755dbd4c80c.tar.gz
Merge branch 'devel' into faster-nimsuggest
Diffstat (limited to 'lib')
-rw-r--r--lib/impure/nre.nim12
-rw-r--r--lib/nimbase.h1
-rw-r--r--lib/packages/docutils/rstgen.nim2
-rw-r--r--lib/pure/asyncdispatch.nim6
-rw-r--r--lib/pure/asynchttpserver.nim8
-rw-r--r--lib/pure/base64.nim12
-rw-r--r--lib/pure/collections/sharedtables.nim46
-rw-r--r--lib/pure/collections/tableimpl.nim9
-rw-r--r--lib/pure/coro.nim63
-rw-r--r--lib/pure/httpclient.nim12
-rw-r--r--lib/pure/logging.nim2
-rw-r--r--lib/pure/nativesockets.nim16
-rw-r--r--lib/pure/strutils.nim12
-rw-r--r--lib/pure/unittest.nim2
-rw-r--r--lib/system/channels.nim2
-rw-r--r--lib/system/gc_common.nim3
-rw-r--r--lib/upcoming/asyncdispatch.nim6
-rw-r--r--lib/windows/winlean.nim3
18 files changed, 158 insertions, 59 deletions
diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim
index dda4b033f..4013182af 100644
--- a/lib/impure/nre.nim
+++ b/lib/impure/nre.nim
@@ -516,23 +516,23 @@ iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): R
   let unicode = uint32(getinfo[culong](pattern, pcre.INFO_OPTIONS) and
     pcre.UTF8) > 0u32
   let strlen = if endpos == int.high: str.len else: endpos+1
-
   var offset = start
   var match: Option[RegexMatch]
+  var neverMatched = true
+
   while true:
     var flags = 0
-
     if match.isSome and
        match.get.matchBounds.a > match.get.matchBounds.b:
       # 0-len match
       flags = pcre.NOTEMPTY_ATSTART
-
     match = str.matchImpl(pattern, offset, endpos, flags)
 
     if match.isNone:
       # either the end of the input or the string
-      # cannot be split here
-      if offset >= strlen:
+      # cannot be split here - we also need to bail
+      # if we've never matched and we've already tried to...
+      if offset >= strlen or neverMatched:
         break
 
       if matchesCrLf and offset < (str.len - 1) and
@@ -546,11 +546,11 @@ iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): R
       else:
         offset += 1
     else:
+      neverMatched = false
       offset = match.get.matchBounds.b + 1
 
       yield match.get
 
-
 proc find*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
   ## Finds the given pattern in the string between the end and start
   ## positions.
diff --git a/lib/nimbase.h b/lib/nimbase.h
index a5d2616e7..d6763f8ff 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -39,6 +39,7 @@ __clang__
 #  pragma GCC diagnostic ignored "-Wswitch-bool"
 #  pragma GCC diagnostic ignored "-Wmacro-redefined"
 #  pragma GCC diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers"
+#  pragma GCC diagnostic ignored "-Wpointer-bool-conversion"
 #endif
 
 #if defined(_MSC_VER)
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index 47247dd7c..ce63d780c 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -870,7 +870,7 @@ proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string):
               d.config.getOrDefault"doc.listing_end" % id)
     return
 
-  var codeLines = 1 + code.strip.countLines
+  var codeLines = code.strip.countLines
   assert codeLines > 0
   result.beginTable = """<table class="line-nums-table"><tbody><tr><td class="blob-line-nums"><pre class="line-nums">"""
   var line = params.startLine
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 7fa686f00..1696c4ed9 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -167,8 +167,12 @@ type
     callbacks: Deque[proc ()]
 
 proc processTimers(p: PDispatcherBase) {.inline.} =
-  while p.timers.len > 0 and epochTime() >= p.timers[0].finishAt:
+  #Process just part if timers at a step
+  var count = p.timers.len
+  let t = epochTime()
+  while count > 0 and t >= p.timers[0].finishAt:
     p.timers.pop().fut.complete()
+    dec count
 
 proc processPendingCallbacks(p: PDispatcherBase) =
   while p.callbacks.len > 0:
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index 672eb34a0..80aa8bb2c 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -31,7 +31,7 @@
 ##
 ##    waitFor server.serve(Port(8080), cb)
 
-import tables, asyncnet, asyncdispatch, parseutils, uri, strutils, nativesockets
+import tables, asyncnet, asyncdispatch, parseutils, uri, strutils
 import httpcore
 
 export httpcore except parseHeader
@@ -209,7 +209,9 @@ proc processClient(client: AsyncSocket, address: string,
         continue
       else:
         request.body = await client.recv(contentLength)
-        assert request.body.len == contentLength
+        if request.body.len != contentLength:
+          await request.respond(Http400, "Bad Request. Content-Length does not match actual.")
+          continue
     elif request.reqMethod == HttpPost:
       await request.respond(Http400, "Bad Request. No Content-Length.")
       continue
@@ -241,7 +243,7 @@ proc serve*(server: AsyncHttpServer, port: Port,
   ## specified address and port.
   ##
   ## When a request is made by a client the specified callback will be called.
-  server.socket = newAsyncSocket(AF_INET6)
+  server.socket = newAsyncSocket()
   if server.reuseAddr:
     server.socket.setSockOpt(OptReuseAddr, true)
   if server.reusePort:
diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim
index 2fc0b1c5e..eee03d7ae 100644
--- a/lib/pure/base64.nim
+++ b/lib/pure/base64.nim
@@ -49,7 +49,7 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
   ## `newline` is added.
   var total = ((len(s) + 2) div 3) * 4
   var numLines = (total + lineLen - 1) div lineLen
-  if numLines > 0: inc(total, (numLines-1) * newLine.len)
+  if numLines > 0: inc(total, (numLines - 1) * newLine.len)
 
   result = newString(total)
   var i = 0
@@ -66,7 +66,8 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
     inc(r, 4)
     inc(i, 3)
     inc(currLine, 4)
-    if currLine >= lineLen and i != s.len-2:
+    # avoid index out of bounds when lineLen == encoded length
+    if currLine >= lineLen and i != s.len-2 and r < total:
       for x in items(newLine):
         result[r] = x
         inc(r)
@@ -155,12 +156,17 @@ when isMainModule:
   assert encode("asure.") == "YXN1cmUu"
   assert encode("sure.") == "c3VyZS4="
 
+  const testInputExpandsTo76 = "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
+  const testInputExpands = "++++++++++++++++++++++++++++++"
   const longText = """Man is distinguished, not only by his reason, but by this
     singular passion from other animals, which is a lust of the mind,
     that by a perseverance of delight in the continued and indefatigable
     generation of knowledge, exceeds the short vehemence of any carnal
     pleasure."""
   const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.",
-                 "asure.", longText]
+                 "asure.", longText, testInputExpandsTo76, testInputExpands]
+
   for t in items(tests):
     assert decode(encode(t)) == t
+    assert decode(encode(t, lineLen=40)) == t
+    assert decode(encode(t, lineLen=76)) == t
\ No newline at end of file
diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim
index 28509caa1..de573bcb2 100644
--- a/lib/pure/collections/sharedtables.nim
+++ b/lib/pure/collections/sharedtables.nim
@@ -130,6 +130,52 @@ proc hasKeyOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): bool =
   withLock t:
     hasKeyOrPutImpl(enlarge)
 
+proc withKey*[A, B](t: var SharedTable[A, B], key: A,
+                    mapper: proc(key: A, val: var B, pairExists: var bool)) =
+  ## Computes a new mapping for the ``key`` with the specified ``mapper``
+  ## procedure.
+  ##
+  ## The ``mapper`` takes 3 arguments:
+  ##   #. ``key`` - the current key, if it exists, or the key passed to
+  ##      ``withKey`` otherwise;
+  ##   #. ``val`` - the current value, if the key exists, or default value
+  ##      of the type otherwise;
+  ##   #. ``pairExists`` - ``true`` if the key exists, ``false`` otherwise.
+  ## The ``mapper`` can can modify ``val`` and ``pairExists`` values to change
+  ## the mapping of the key or delete it from the table.
+  ## When adding a value, make sure to set ``pairExists`` to ``true`` along
+  ## with modifying the ``val``.
+  ##
+  ## The operation is performed atomically and other operations on the table
+  ## will be blocked while the ``mapper`` is invoked, so it should be short and
+  ## simple.
+  ##
+  ## Example usage:
+  ##
+  ## .. code-block:: nim
+  ##
+  ##   # If value exists, decrement it.
+  ##   # If it becomes zero or less, delete the key
+  ##   t.withKey(1'i64) do (k: int64, v: var int, pairExists: var bool):
+  ##     if pairExists:
+  ##       dec v
+  ##       if v <= 0:
+  ##         pairExists = false
+  withLock t:
+    var hc: Hash
+    var index = rawGet(t, key, hc)
+
+    var pairExists = index >= 0
+    if pairExists:
+      mapper(t.data[index].key, t.data[index].val, pairExists)
+      if not pairExists:
+        delImplIdx(t, index)
+    else:
+      var val: B
+      mapper(key, val, pairExists)
+      if pairExists:
+        maybeRehashPutImpl(enlarge)
+
 proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) =
   ## puts a (key, value)-pair into `t`.
   withLock t:
diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim
index 5e871f08b..c0d45c392 100644
--- a/lib/pure/collections/tableimpl.nim
+++ b/lib/pure/collections/tableimpl.nim
@@ -120,9 +120,7 @@ template default[T](t: typedesc[T]): T =
   var v: T
   v
 
-template delImpl() {.dirty.} =
-  var hc: Hash
-  var i = rawGet(t, key, hc)
+template delImplIdx(t, i) =
   let msk = maxHash(t)
   if i >= 0:
     dec(t.counter)
@@ -145,6 +143,11 @@ template delImpl() {.dirty.} =
         else:
           shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop
 
+template delImpl() {.dirty.} =
+  var hc: Hash
+  var i = rawGet(t, key, hc)
+  delImplIdx(t, i)
+
 template clearImpl() {.dirty.} =
   for i in 0 .. <t.data.len:
     when compiles(t.data[i].hcode): # CountTable records don't contain a hcode
diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim
index e053f4427..b6ef30e7c 100644
--- a/lib/pure/coro.nim
+++ b/lib/pure/coro.nim
@@ -23,7 +23,6 @@ when not nimCoroutines and not defined(nimdoc):
     {.error: "Coroutines require -d:nimCoroutines".}
 
 import os
-import macros
 import lists
 include system/timers
 
@@ -120,27 +119,38 @@ const
   CORO_FINISHED = 2
 
 type
-  Stack = object
+  Stack {.pure.} = object
     top: pointer      # Top of the stack. Pointer used for deallocating stack if we own it.
     bottom: pointer   # Very bottom of the stack, acts as unique stack identifier.
     size: int
 
-  Coroutine = ref object
+  Coroutine {.pure.} = object
     execContext: Context
     fn: proc()
     state: int
     lastRun: Ticks
     sleepTime: float
     stack: Stack
+    reference: CoroutineRef
+
+  CoroutinePtr = ptr Coroutine
+
+  CoroutineRef* = ref object
+    ## CoroutineRef holds a pointer to actual coroutine object. Public API always returns
+    ## CoroutineRef instead of CoroutinePtr in order to allow holding a reference to coroutine
+    ## object while it can be safely deallocated by coroutine scheduler loop. In this case
+    ## Coroutine.reference.coro is set to nil. Public API checks for for it being nil and
+    ## gracefully fails if it is nil.
+    coro: CoroutinePtr
 
   CoroutineLoopContext = ref object
-    coroutines: DoublyLinkedList[Coroutine]
-    current: DoublyLinkedNode[Coroutine]
+    coroutines: DoublyLinkedList[CoroutinePtr]
+    current: DoublyLinkedNode[CoroutinePtr]
     loop: Coroutine
 
 var ctx {.threadvar.}: CoroutineLoopContext
 
-proc getCurrent(): Coroutine =
+proc getCurrent(): CoroutinePtr =
   ## Returns current executing coroutine object.
   var node = ctx.current
   if node != nil:
@@ -151,7 +161,7 @@ proc initialize() =
   ## Initializes coroutine state of current thread.
   if ctx == nil:
     ctx = CoroutineLoopContext()
-    ctx.coroutines = initDoublyLinkedList[Coroutine]()
+    ctx.coroutines = initDoublyLinkedList[CoroutinePtr]()
     ctx.loop = Coroutine()
     ctx.loop.state = CORO_EXECUTING
     when coroBackend == CORO_BACKEND_FIBERS:
@@ -159,7 +169,7 @@ proc initialize() =
 
 proc runCurrentTask()
 
-proc switchTo(current, to: Coroutine) =
+proc switchTo(current, to: CoroutinePtr) =
   ## Switches execution from `current` into `to` context.
   to.lastRun = getTicks()
   # Update position of current stack so gc invoked from another stack knows how much to scan.
@@ -192,7 +202,7 @@ proc suspend*(sleepTime: float=0) =
   ## Until then other coroutines are executed.
   var current = getCurrent()
   current.sleepTime = sleepTime
-  switchTo(current, ctx.loop)
+  switchTo(current, addr(ctx.loop))
 
 proc runCurrentTask() =
   ## Starts execution of current coroutine and updates it's state through coroutine's life.
@@ -218,31 +228,33 @@ proc runCurrentTask() =
   suspend(0)                      # Exit coroutine without returning from coroExecWithStack()
   doAssert false
 
-proc start*(c: proc(), stacksize: int=defaultStackSize) =
+proc start*(c: proc(), stacksize: int=defaultStackSize): CoroutineRef {.discardable.} =
   ## Schedule coroutine for execution. It does not run immediately.
   if ctx == nil:
     initialize()
   
-  var coro = Coroutine()
-  coro.fn = c
+  var coro: CoroutinePtr
   when coroBackend == CORO_BACKEND_FIBERS:
+    coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine)))
     coro.execContext = CreateFiberEx(stacksize, stacksize,
       FIBER_FLAG_FLOAT_SWITCH, (proc(p: pointer): void {.stdcall.} = runCurrentTask()), nil)
     coro.stack.size = stacksize
   else:
-    var stack: pointer
-    while stack == nil:
-      stack = alloc0(stacksize)
-    coro.stack.top = stack
+    coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine) + stacksize))
+    coro.stack.top = cast[pointer](cast[ByteAddress](coro) + sizeof(Coroutine))
+    coro.stack.bottom = cast[pointer](cast[ByteAddress](coro.stack.top) + stacksize)
     when coroBackend == CORO_BACKEND_UCONTEXT:
       discard getcontext(coro.execContext)
-      coro.execContext.uc_stack.ss_sp = cast[pointer](cast[ByteAddress](stack) + stacksize)
-      coro.execContext.uc_stack.ss_size = coro.stack.size
-      coro.execContext.uc_link = addr ctx.loop.execContext
+      coro.execContext.uc_stack.ss_sp = coro.stack.top
+      coro.execContext.uc_stack.ss_size = stacksize
+      coro.execContext.uc_link = addr(ctx.loop.execContext)
       makecontext(coro.execContext, runCurrentTask, 0)
+  coro.fn = c
   coro.stack.size = stacksize
   coro.state = CORO_CREATED
+  coro.reference = CoroutineRef(coro: coro)
   ctx.coroutines.append(coro)
+  return coro.reference
 
 proc run*() =
   initialize()
@@ -256,7 +268,7 @@ proc run*() =
     var remaining = current.sleepTime - (float(getTicks() - current.lastRun) / 1_000_000_000)
     if remaining <= 0:
       # Save main loop context. Suspending coroutine will resume after this statement with
-      switchTo(ctx.loop, current)
+      switchTo(addr(ctx.loop), current)
     else:
       if minDelay > 0 and remaining > 0:
         minDelay = min(remaining, minDelay)
@@ -269,14 +281,14 @@ proc run*() =
         # If first coroutine ends then `prev` is nil even if more coroutines 
         # are to be scheduled.
         next = ctx.current.next
+      current.reference.coro = nil
       ctx.coroutines.remove(ctx.current)
       GC_removeStack(current.stack.bottom)
       when coroBackend == CORO_BACKEND_FIBERS:
         DeleteFiber(current.execContext)
       else:
         dealloc(current.stack.top)
-      current.stack.top = nil
-      current.stack.bottom = nil
+      dealloc(current)
       ctx.current = next
     elif ctx.current == nil or ctx.current.next == nil:
       ctx.current = ctx.coroutines.head
@@ -284,13 +296,10 @@ proc run*() =
     else:
       ctx.current = ctx.current.next
 
-proc alive*(c: proc()): bool =
+proc alive*(c: CoroutineRef): bool = c.coro != nil and c.coro.state != CORO_FINISHED
   ## Returns ``true`` if coroutine has not returned, ``false`` otherwise.
-  for coro in items(ctx.coroutines):
-    if coro.fn == c:
-      return coro.state != CORO_FINISHED
 
-proc wait*(c: proc(), interval=0.01) =
+proc wait*(c: CoroutineRef, interval=0.01) =
   ## Returns only after coroutine ``c`` has returned. ``interval`` is time in seconds how often.
   while alive(c):
     suspend(interval)
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 59918f766..662e75471 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -337,8 +337,16 @@ proc parseResponse(s: Socket, getBody: bool, timeout: int): Response =
 when not defined(ssl):
   type SSLContext = ref object
 var defaultSSLContext {.threadvar.}: SSLContext
+
 when defined(ssl):
   defaultSSLContext = newContext(verifyMode = CVerifyNone)
+  template contextOrDefault(ctx: SSLContext): SSLContext =
+    var result = ctx
+    if ctx == nil:
+      if defaultSSLContext == nil:
+        defaultSSLContext = newContext(verifyMode = CVerifyNone)
+      result = defaultSSLContext
+    result
 
 proc newProxy*(url: string, auth = ""): Proxy =
   ## Constructs a new ``TProxy`` object.
@@ -805,7 +813,7 @@ proc newHttpClient*(userAgent = defUserAgent,
   result.bodyStream = newStringStream()
   result.getBody = true
   when defined(ssl):
-    result.sslContext = sslContext
+    result.sslContext = contextOrDefault(sslContext)
 
 type
   AsyncHttpClient* = HttpClientBase[AsyncSocket]
@@ -837,7 +845,7 @@ proc newAsyncHttpClient*(userAgent = defUserAgent,
   result.bodyStream = newFutureStream[string]("newAsyncHttpClient")
   result.getBody = true
   when defined(ssl):
-    result.sslContext = sslContext
+    result.sslContext = contextOrDefault(sslContext)
 
 proc close*(client: HttpClient | AsyncHttpClient) =
   ## Closes any connections held by the HTTP client.
diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim
index 65724f75a..598ee01d3 100644
--- a/lib/pure/logging.nim
+++ b/lib/pure/logging.nim
@@ -139,7 +139,7 @@ proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): str
     result.add(arg)
 
 method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {.
-            raises: [Exception],
+            raises: [Exception], gcsafe,
             tags: [TimeEffect, WriteIOEffect, ReadIOEffect], base.} =
   ## Override this method in custom loggers. Default implementation does
   ## nothing.
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index d51dbd475..0a7ffb3b3 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -374,6 +374,22 @@ proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
   result.addrList = cstringArrayToSeq(s.h_addr_list)
   result.length = int(s.h_length)
 
+proc getHostname*(): string {.tags: [ReadIOEffect].} =
+  ## Returns the local hostname (not the FQDN)
+  # https://tools.ietf.org/html/rfc1035#section-2.3.1
+  # https://tools.ietf.org/html/rfc2181#section-11
+  const size = 64
+  result = newString(size)
+  when useWinVersion:
+    let success = winlean.getHostname(result, size)
+  else:
+    # Posix
+    let success = posix.getHostname(result, size)
+  if success != 0.cint:
+    raiseOSError(osLastError())
+  let x = len(cstring(result))
+  result.setLen(x)
+
 proc getSockDomain*(socket: SocketHandle): Domain =
   ## returns the socket's domain (AF_INET or AF_INET6).
   var name: SockAddr
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 9b2526337..a87df7f52 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -767,20 +767,16 @@ proc splitLines*(s: string): seq[string] {.noSideEffect,
 
 proc countLines*(s: string): int {.noSideEffect,
   rtl, extern: "nsuCountLines".} =
-  ## Returns the number of new line separators in the string `s`.
+  ## Returns the number of lines in the string `s`.
   ##
   ## This is the same as ``len(splitLines(s))``, but much more efficient
   ## because it doesn't modify the string creating temporal objects. Every
   ## `character literal <manual.html#character-literals>`_ newline combination
   ## (CR, LF, CR-LF) is supported.
   ##
-  ## Despite its name this proc might not actually return the *number of lines*
-  ## in `s` because the concept of what a line is can vary. For example, a
-  ## string like ``Hello world`` is a line of text, but the proc will return a
-  ## value of zero because there are no newline separators.  Also, text editors
-  ## usually don't count trailing newline characters in a text file as a new
-  ## empty line, but this proc will.
-  var i = 0
+  ## In this context, a line is any string seperated by a newline combination.
+  ## A line can be an empty string.
+  var i = 1
   while i < s.len:
     case s[i]
     of '\c':
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 55f273154..dd7009d63 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -47,6 +47,8 @@
 ## Tests can be nested, however failure of a nested test will not mark the
 ## parent test as failed. Setup and teardown are inherited. Setup can be
 ## overridden locally.
+## Compiled test files return the number of failed test as exit code, while
+## nim c -r <testfile.nim> exits with 0 or 1
 
 import
   macros
diff --git a/lib/system/channels.nim b/lib/system/channels.nim
index 4b8b895a5..42096323c 100644
--- a/lib/system/channels.nim
+++ b/lib/system/channels.nim
@@ -13,6 +13,8 @@
 ##
 ## **Note:** The current implementation of message passing is slow and does
 ## not work with cyclic data structures.
+## **Note:** Channels cannot be passed between threads. Use globals or pass
+## them by `ptr`.
 
 when not declared(NimString):
   {.error: "You must not import this module explicitly".}
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index e3b861fad..b0eb25616 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -290,9 +290,6 @@ elif stackIncreases:
 
   template forEachStackSlot(gch, gcMark: untyped) {.dirty.} =
     var registers {.noinit.}: C_JmpBuf
-    # sp will traverse the JMP_BUF as well (jmp_buf size is added,
-    # otherwise sp would be below the registers structure).
-    var regAddr = addr(registers) +% jmpbufSize
 
     if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
       for stack in gch.stack.items():
diff --git a/lib/upcoming/asyncdispatch.nim b/lib/upcoming/asyncdispatch.nim
index 1787f062f..feee87bae 100644
--- a/lib/upcoming/asyncdispatch.nim
+++ b/lib/upcoming/asyncdispatch.nim
@@ -138,8 +138,12 @@ type
     callbacks: Deque[proc ()]
 
 proc processTimers(p: PDispatcherBase) {.inline.} =
-  while p.timers.len > 0 and epochTime() >= p.timers[0].finishAt:
+  #Process just part if timers at a step
+  var count = p.timers.len
+  let t = epochTime()
+  while count > 0 and t >= p.timers[0].finishAt:
     p.timers.pop().fut.complete()
+    dec count
 
 proc processPendingCallbacks(p: PDispatcherBase) =
   while p.callbacks.len > 0:
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index fa9ce9eed..1a251d0cc 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -542,6 +542,9 @@ proc gethostbyaddr*(ip: ptr InAddr, len: cuint, theType: cint): ptr Hostent {.
 proc gethostbyname*(name: cstring): ptr Hostent {.
   stdcall, importc: "gethostbyname", dynlib: ws2dll.}
 
+proc gethostname*(hostname: cstring, len: cint): cint {.
+  stdcall, importc: "gethostname", dynlib: ws2dll.}
+
 proc socket*(af, typ, protocol: cint): SocketHandle {.
   stdcall, importc: "socket", dynlib: ws2dll.}