summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2017-05-16 15:43:09 +0200
committerAndreas Rumpf <rumpf_a@web.de>2017-05-16 15:43:09 +0200
commitc3c37dbb1574db5078b86be29804990d153ec1c1 (patch)
tree7c042f1163b6c10910070599dc55f9c14341236e /lib
parent2d91c04f4eea1f0768b305b4903b4c455b9d06e8 (diff)
parent224eec595a6112c7aa3a4c06afacc99167580464 (diff)
downloadNim-c3c37dbb1574db5078b86be29804990d153ec1c1.tar.gz
Merge branch 'devel' into araq
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/asyncdispatch.nim12
-rw-r--r--lib/pure/asyncfile.nim10
-rw-r--r--lib/pure/asyncnet.nim9
-rw-r--r--lib/pure/collections/tables.nim41
-rw-r--r--lib/pure/httpclient.nim8
-rw-r--r--lib/pure/net.nim358
-rw-r--r--lib/pure/osproc.nim6
-rw-r--r--lib/pure/parseutils.nim2
-rw-r--r--lib/pure/strutils.nim2
-rw-r--r--lib/system/atomics.nim20
-rw-r--r--lib/system/excpt.nim9
-rw-r--r--lib/system/syslocks.nim110
-rw-r--r--lib/upcoming/asyncdispatch.nim12
13 files changed, 374 insertions, 225 deletions
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 6a877be30..1697384e0 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -9,7 +9,7 @@
 
 include "system/inclrtl"
 
-import os, oids, tables, strutils, times, heapqueue, options
+import os, tables, strutils, times, heapqueue, options
 
 import nativesockets, net, deques
 
@@ -242,6 +242,11 @@ when defined(windows) or defined(nimdoc):
     if gDisp.isNil: gDisp = newDispatcher()
     result = gDisp
 
+  proc setGlobalDispatcher*(disp: PDispatcher) =
+    if not gDisp.isNil:
+      assert gDisp.callbacks.len == 0
+    gDisp = disp
+
   proc register*(fd: AsyncFD) =
     ## Registers ``fd`` with the dispatcher.
     let p = getGlobalDispatcher()
@@ -931,6 +936,11 @@ else:
     if gDisp.isNil: gDisp = newDispatcher()
     result = gDisp
 
+  proc setGlobalDispatcher*(disp: PDispatcher) =
+    if not gDisp.isNil:
+      assert gDisp.callbacks.len == 0
+    gDisp = disp
+
   proc update(fd: AsyncFD, events: set[Event]) =
     let p = getGlobalDispatcher()
     assert fd.SocketHandle in p.selector
diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim
index 9bd060e30..8fb30075c 100644
--- a/lib/pure/asyncfile.nim
+++ b/lib/pure/asyncfile.nim
@@ -339,13 +339,17 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             assert bytesCount == size.int32
-            f.offset.inc(size)
             retFuture.complete()
           else:
             retFuture.fail(newException(OSError, osErrorMsg(errcode)))
     )
+    # passing -1 here should work according to MSDN, but doesn't. For more
+    # information see
+    # http://stackoverflow.com/questions/33650899/does-asynchronous-file-
+    #   appending-in-windows-preserve-order
     ol.offset = DWord(f.offset and 0xffffffff)
     ol.offsetHigh = DWord(f.offset shr 32)
+    f.offset.inc(size)
 
     # According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
     let ret = writeFile(f.fd.Handle, buf, size.int32, nil,
@@ -364,7 +368,6 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
         retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
       else:
         assert bytesWritten == size.int32
-        f.offset.inc(size)
         retFuture.complete()
   else:
     var written = 0
@@ -410,7 +413,6 @@ proc write*(f: AsyncFile, data: string): Future[void] =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             assert bytesCount == data.len.int32
-            f.offset.inc(data.len)
             retFuture.complete()
           else:
             retFuture.fail(newException(OSError, osErrorMsg(errcode)))
@@ -420,6 +422,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
     )
     ol.offset = DWord(f.offset and 0xffffffff)
     ol.offsetHigh = DWord(f.offset shr 32)
+    f.offset.inc(data.len)
 
     # According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
     let ret = writeFile(f.fd.Handle, buffer, data.len.int32, nil,
@@ -441,7 +444,6 @@ proc write*(f: AsyncFile, data: string): Future[void] =
         retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
       else:
         assert bytesWritten == data.len.int32
-        f.offset.inc(data.len)
         retFuture.complete()
   else:
     var written = 0
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index 11b7998b2..9f73bc3cf 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -647,9 +647,12 @@ when defineSsl:
     sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut)
 
   proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket,
-                            handshake: SslHandshakeType) =
+                            handshake: SslHandshakeType,
+                            hostname: string = nil) =
     ## Wraps a connected socket in an SSL context. This function effectively
     ## turns ``socket`` into an SSL socket.
+    ## ``hostname`` should be specified so that the client knows which hostname
+    ## the server certificate should be validated against.
     ##
     ## This should be called on a connected socket, and will perform
     ## an SSL handshake immediately.
@@ -660,6 +663,10 @@ when defineSsl:
 
     case handshake
     of handshakeAsClient:
+      if not hostname.isNil and not isIpAddress(hostname):
+        # Set the SNI address for this connection. This call can fail if
+        # we're not using TLSv1+.
+        discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
       sslSetConnectState(socket.sslHandle)
     of handshakeAsServer:
       sslSetAcceptState(socket.sslHandle)
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index 969802cfc..323af5a38 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -269,6 +269,18 @@ proc del*[A, B](t: var Table[A, B], key: A) =
   ## deletes `key` from hash table `t`.
   delImpl()
 
+proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool =
+  ## Deletes the ``key`` from the table.
+  ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
+  ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
+  ## unchanged.
+  var hc: Hash
+  var index = rawGet(t, key, hc)
+  result = index >= 0
+  if result:
+    shallowCopy(val, t.data[index].val)
+    delImplIdx(t, index)
+
 proc enlarge[A, B](t: var Table[A, B]) =
   var n: KeyValuePairSeq[A, B]
   newSeq(n, len(t.data) * growthFactor)
@@ -424,6 +436,13 @@ proc del*[A, B](t: TableRef[A, B], key: A) =
   ## deletes `key` from hash table `t`.
   t[].del(key)
 
+proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
+  ## Deletes the ``key`` from the table.
+  ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
+  ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
+  ## unchanged.
+  result = t[].take(key, val)
+
 proc newTable*[A, B](initialSize=64): TableRef[A, B] =
   new(result)
   result[] = initTable[A, B](initialSize)
@@ -625,7 +644,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
   while ht >= 0 and hs >= 0:
     var nxtt = t.data[ht].next
     var nxts = s.data[hs].next
-    if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode): 
+    if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode):
       if (s.data[hs].key != t.data[ht].key) and (s.data[hs].val != t.data[ht].val):
         return false
     ht = nxtt
@@ -829,7 +848,7 @@ proc clear*[A](t: CountTableRef[A]) =
 proc clear*[A](t: var CountTable[A]) =
   ## Resets the table so that it is empty.
   clearImpl()
-  
+
 iterator pairs*[A](t: CountTable[A]): (A, int) =
   ## iterates over any (key, value) pair in the table `t`.
   for h in 0..high(t.data):
@@ -1256,17 +1275,17 @@ when isMainModule:
     var b = newOrderedTable[string, string](initialSize=2)
     b.add("wrong?", "foo")
     b.add("wrong?", "foo2")
-    assert a == b    
+    assert a == b
 
   block: #5482
-    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()  
+    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
     var b = newOrderedTable[string, string](initialSize=2)
     b.add("wrong?", "foo")
     b.add("wrong?", "foo2")
-    assert a == b    
+    assert a == b
 
   block: #5487
-    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()  
+    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
     var b = newOrderedTable[string, string]() # notice, default size!
     b.add("wrong?", "foo")
     b.add("wrong?", "foo2")
@@ -1279,13 +1298,13 @@ when isMainModule:
     b.add("wrong?", "foo2")
     assert a == b
 
-  block: 
-    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() 
-    var b = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable() 
+  block:
+    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
+    var b = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
     var c = newOrderedTable[string, string]() # notice, default size!
     c.add("wrong?", "foo")
-    c.add("wrong?", "foo2")    
+    c.add("wrong?", "foo2")
     assert a == b
     assert a == c
-    
+
 
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 1b8a20b65..4f43177a8 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -512,7 +512,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
         raise newException(HttpRequestError,
                            "The proxy server rejected a CONNECT request, " &
                            "so a secure connection could not be established.")
-      sslContext.wrapConnectedSocket(s, handshakeAsClient)
+      sslContext.wrapConnectedSocket(s, handshakeAsClient, hostUrl.hostname)
     else:
       raise newException(HttpRequestError, "SSL support not available. Cannot connect via proxy over SSL")
   else:
@@ -1060,7 +1060,8 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
     when defined(ssl):
       if isSsl:
         try:
-          client.sslContext.wrapConnectedSocket(client.socket, handshakeAsClient)
+          client.sslContext.wrapConnectedSocket(
+            client.socket, handshakeAsClient, url.hostname)
         except:
           client.socket.close()
           raise getCurrentException()
@@ -1102,7 +1103,8 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
         raise newException(HttpRequestError,
                            "The proxy server rejected a CONNECT request, " &
                            "so a secure connection could not be established.")
-      client.sslContext.wrapConnectedSocket(client.socket, handshakeAsClient)
+      client.sslContext.wrapConnectedSocket(
+        client.socket, handshakeAsClient, requestUrl.hostname)
       client.proxy = nil
     else:
       raise newException(HttpRequestError,
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index d175bd537..629e916fa 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -237,6 +237,180 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
     raiseOSError(osLastError())
   result = newSocket(fd, domain, sockType, protocol, buffered)
 
+proc parseIPv4Address(address_str: string): IpAddress =
+  ## Parses IPv4 adresses
+  ## Raises EInvalidValue on errors
+  var
+    byteCount = 0
+    currentByte:uint16 = 0
+    seperatorValid = false
+
+  result.family = IpAddressFamily.IPv4
+
+  for i in 0 .. high(address_str):
+    if address_str[i] in strutils.Digits: # Character is a number
+      currentByte = currentByte * 10 +
+        cast[uint16](ord(address_str[i]) - ord('0'))
+      if currentByte > 255'u16:
+        raise newException(ValueError,
+          "Invalid IP Address. Value is out of range")
+      seperatorValid = true
+    elif address_str[i] == '.': # IPv4 address separator
+      if not seperatorValid or byteCount >= 3:
+        raise newException(ValueError,
+          "Invalid IP Address. The address consists of too many groups")
+      result.address_v4[byteCount] = cast[uint8](currentByte)
+      currentByte = 0
+      byteCount.inc
+      seperatorValid = false
+    else:
+      raise newException(ValueError,
+        "Invalid IP Address. Address contains an invalid character")
+
+  if byteCount != 3 or not seperatorValid:
+    raise newException(ValueError, "Invalid IP Address")
+  result.address_v4[byteCount] = cast[uint8](currentByte)
+
+proc parseIPv6Address(address_str: string): IpAddress =
+  ## Parses IPv6 adresses
+  ## Raises EInvalidValue on errors
+  result.family = IpAddressFamily.IPv6
+  if address_str.len < 2:
+    raise newException(ValueError, "Invalid IP Address")
+
+  var
+    groupCount = 0
+    currentGroupStart = 0
+    currentShort:uint32 = 0
+    seperatorValid = true
+    dualColonGroup = -1
+    lastWasColon = false
+    v4StartPos = -1
+    byteCount = 0
+
+  for i,c in address_str:
+    if c == ':':
+      if not seperatorValid:
+        raise newException(ValueError,
+          "Invalid IP Address. Address contains an invalid seperator")
+      if lastWasColon:
+        if dualColonGroup != -1:
+          raise newException(ValueError,
+            "Invalid IP Address. Address contains more than one \"::\" seperator")
+        dualColonGroup = groupCount
+        seperatorValid = false
+      elif i != 0 and i != high(address_str):
+        if groupCount >= 8:
+          raise newException(ValueError,
+            "Invalid IP Address. The address consists of too many groups")
+        result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
+        result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
+        currentShort = 0
+        groupCount.inc()
+        if dualColonGroup != -1: seperatorValid = false
+      elif i == 0: # only valid if address starts with ::
+        if address_str[1] != ':':
+          raise newException(ValueError,
+            "Invalid IP Address. Address may not start with \":\"")
+      else: # i == high(address_str) - only valid if address ends with ::
+        if address_str[high(address_str)-1] != ':':
+          raise newException(ValueError,
+            "Invalid IP Address. Address may not end with \":\"")
+      lastWasColon = true
+      currentGroupStart = i + 1
+    elif c == '.': # Switch to parse IPv4 mode
+      if i < 3 or not seperatorValid or groupCount >= 7:
+        raise newException(ValueError, "Invalid IP Address")
+      v4StartPos = currentGroupStart
+      currentShort = 0
+      seperatorValid = false
+      break
+    elif c in strutils.HexDigits:
+      if c in strutils.Digits: # Normal digit
+        currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0'))
+      elif c >= 'a' and c <= 'f': # Lower case hex
+        currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10
+      else: # Upper case hex
+        currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10
+      if currentShort > 65535'u32:
+        raise newException(ValueError,
+          "Invalid IP Address. Value is out of range")
+      lastWasColon = false
+      seperatorValid = true
+    else:
+      raise newException(ValueError,
+        "Invalid IP Address. Address contains an invalid character")
+
+
+  if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
+    if seperatorValid: # Copy remaining data
+      if groupCount >= 8:
+        raise newException(ValueError,
+          "Invalid IP Address. The address consists of too many groups")
+      result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
+      result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
+      groupCount.inc()
+  else: # Must parse IPv4 address
+    for i,c in address_str[v4StartPos..high(address_str)]:
+      if c in strutils.Digits: # Character is a number
+        currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
+        if currentShort > 255'u32:
+          raise newException(ValueError,
+            "Invalid IP Address. Value is out of range")
+        seperatorValid = true
+      elif c == '.': # IPv4 address separator
+        if not seperatorValid or byteCount >= 3:
+          raise newException(ValueError, "Invalid IP Address")
+        result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
+        currentShort = 0
+        byteCount.inc()
+        seperatorValid = false
+      else: # Invalid character
+        raise newException(ValueError,
+          "Invalid IP Address. Address contains an invalid character")
+
+    if byteCount != 3 or not seperatorValid:
+      raise newException(ValueError, "Invalid IP Address")
+    result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
+    groupCount += 2
+
+  # Shift and fill zeros in case of ::
+  if groupCount > 8:
+    raise newException(ValueError,
+      "Invalid IP Address. The address consists of too many groups")
+  elif groupCount < 8: # must fill
+    if dualColonGroup == -1:
+      raise newException(ValueError,
+        "Invalid IP Address. The address consists of too few groups")
+    var toFill = 8 - groupCount # The number of groups to fill
+    var toShift = groupCount - dualColonGroup # Nr of known groups after ::
+    for i in 0..2*toShift-1: # shift
+      result.address_v6[15-i] = result.address_v6[groupCount*2-i-1]
+    for i in 0..2*toFill-1: # fill with 0s
+      result.address_v6[dualColonGroup*2+i] = 0
+  elif dualColonGroup != -1:
+    raise newException(ValueError,
+      "Invalid IP Address. The address consists of too many groups")
+
+proc parseIpAddress*(address_str: string): IpAddress =
+  ## Parses an IP address
+  ## Raises EInvalidValue on error
+  if address_str == nil:
+    raise newException(ValueError, "IP Address string is nil")
+  if address_str.contains(':'):
+    return parseIPv6Address(address_str)
+  else:
+    return parseIPv4Address(address_str)
+
+proc isIpAddress*(address_str: string): bool {.tags: [].} =
+  ## Checks if a string is an IP address
+  ## Returns true if it is, false otherwise
+  try:
+    discard parseIpAddress(address_str)
+  except ValueError:
+    return false
+  return true
+
 when defineSsl:
   CRYPTO_malloc_init()
   SslLibraryInit()
@@ -438,9 +612,12 @@ when defineSsl:
       raiseSSLError()
 
   proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket,
-                            handshake: SslHandshakeType) =
+                            handshake: SslHandshakeType,
+                            hostname: string = nil) =
     ## Wraps a connected socket in an SSL context. This function effectively
     ## turns ``socket`` into an SSL socket.
+    ## ``hostname`` should be specified so that the client knows which hostname
+    ## the server certificate should be validated against.
     ##
     ## This should be called on a connected socket, and will perform
     ## an SSL handshake immediately.
@@ -450,6 +627,10 @@ when defineSsl:
     wrapSocket(ctx, socket)
     case handshake
     of handshakeAsClient:
+      if not hostname.isNil and not isIpAddress(hostname):
+        # Discard result in case OpenSSL version doesn't support SNI, or we're
+        # not using TLSv1+
+        discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
       let ret = SSLConnect(socket.sslHandle)
       socketError(socket, ret)
     of handshakeAsServer:
@@ -1302,181 +1483,6 @@ proc `$`*(address: IpAddress): string =
             mask = mask shr 4
           printedLastGroup = true
 
-proc parseIPv4Address(address_str: string): IpAddress =
-  ## Parses IPv4 adresses
-  ## Raises EInvalidValue on errors
-  var
-    byteCount = 0
-    currentByte:uint16 = 0
-    seperatorValid = false
-
-  result.family = IpAddressFamily.IPv4
-
-  for i in 0 .. high(address_str):
-    if address_str[i] in strutils.Digits: # Character is a number
-      currentByte = currentByte * 10 +
-        cast[uint16](ord(address_str[i]) - ord('0'))
-      if currentByte > 255'u16:
-        raise newException(ValueError,
-          "Invalid IP Address. Value is out of range")
-      seperatorValid = true
-    elif address_str[i] == '.': # IPv4 address separator
-      if not seperatorValid or byteCount >= 3:
-        raise newException(ValueError,
-          "Invalid IP Address. The address consists of too many groups")
-      result.address_v4[byteCount] = cast[uint8](currentByte)
-      currentByte = 0
-      byteCount.inc
-      seperatorValid = false
-    else:
-      raise newException(ValueError,
-        "Invalid IP Address. Address contains an invalid character")
-
-  if byteCount != 3 or not seperatorValid:
-    raise newException(ValueError, "Invalid IP Address")
-  result.address_v4[byteCount] = cast[uint8](currentByte)
-
-proc parseIPv6Address(address_str: string): IpAddress =
-  ## Parses IPv6 adresses
-  ## Raises EInvalidValue on errors
-  result.family = IpAddressFamily.IPv6
-  if address_str.len < 2:
-    raise newException(ValueError, "Invalid IP Address")
-
-  var
-    groupCount = 0
-    currentGroupStart = 0
-    currentShort:uint32 = 0
-    seperatorValid = true
-    dualColonGroup = -1
-    lastWasColon = false
-    v4StartPos = -1
-    byteCount = 0
-
-  for i,c in address_str:
-    if c == ':':
-      if not seperatorValid:
-        raise newException(ValueError,
-          "Invalid IP Address. Address contains an invalid seperator")
-      if lastWasColon:
-        if dualColonGroup != -1:
-          raise newException(ValueError,
-            "Invalid IP Address. Address contains more than one \"::\" seperator")
-        dualColonGroup = groupCount
-        seperatorValid = false
-      elif i != 0 and i != high(address_str):
-        if groupCount >= 8:
-          raise newException(ValueError,
-            "Invalid IP Address. The address consists of too many groups")
-        result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
-        result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
-        currentShort = 0
-        groupCount.inc()
-        if dualColonGroup != -1: seperatorValid = false
-      elif i == 0: # only valid if address starts with ::
-        if address_str[1] != ':':
-          raise newException(ValueError,
-            "Invalid IP Address. Address may not start with \":\"")
-      else: # i == high(address_str) - only valid if address ends with ::
-        if address_str[high(address_str)-1] != ':':
-          raise newException(ValueError,
-            "Invalid IP Address. Address may not end with \":\"")
-      lastWasColon = true
-      currentGroupStart = i + 1
-    elif c == '.': # Switch to parse IPv4 mode
-      if i < 3 or not seperatorValid or groupCount >= 7:
-        raise newException(ValueError, "Invalid IP Address")
-      v4StartPos = currentGroupStart
-      currentShort = 0
-      seperatorValid = false
-      break
-    elif c in strutils.HexDigits:
-      if c in strutils.Digits: # Normal digit
-        currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0'))
-      elif c >= 'a' and c <= 'f': # Lower case hex
-        currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10
-      else: # Upper case hex
-        currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10
-      if currentShort > 65535'u32:
-        raise newException(ValueError,
-          "Invalid IP Address. Value is out of range")
-      lastWasColon = false
-      seperatorValid = true
-    else:
-      raise newException(ValueError,
-        "Invalid IP Address. Address contains an invalid character")
-
-
-  if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
-    if seperatorValid: # Copy remaining data
-      if groupCount >= 8:
-        raise newException(ValueError,
-          "Invalid IP Address. The address consists of too many groups")
-      result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
-      result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
-      groupCount.inc()
-  else: # Must parse IPv4 address
-    for i,c in address_str[v4StartPos..high(address_str)]:
-      if c in strutils.Digits: # Character is a number
-        currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
-        if currentShort > 255'u32:
-          raise newException(ValueError,
-            "Invalid IP Address. Value is out of range")
-        seperatorValid = true
-      elif c == '.': # IPv4 address separator
-        if not seperatorValid or byteCount >= 3:
-          raise newException(ValueError, "Invalid IP Address")
-        result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
-        currentShort = 0
-        byteCount.inc()
-        seperatorValid = false
-      else: # Invalid character
-        raise newException(ValueError,
-          "Invalid IP Address. Address contains an invalid character")
-
-    if byteCount != 3 or not seperatorValid:
-      raise newException(ValueError, "Invalid IP Address")
-    result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
-    groupCount += 2
-
-  # Shift and fill zeros in case of ::
-  if groupCount > 8:
-    raise newException(ValueError,
-      "Invalid IP Address. The address consists of too many groups")
-  elif groupCount < 8: # must fill
-    if dualColonGroup == -1:
-      raise newException(ValueError,
-        "Invalid IP Address. The address consists of too few groups")
-    var toFill = 8 - groupCount # The number of groups to fill
-    var toShift = groupCount - dualColonGroup # Nr of known groups after ::
-    for i in 0..2*toShift-1: # shift
-      result.address_v6[15-i] = result.address_v6[groupCount*2-i-1]
-    for i in 0..2*toFill-1: # fill with 0s
-      result.address_v6[dualColonGroup*2+i] = 0
-  elif dualColonGroup != -1:
-    raise newException(ValueError,
-      "Invalid IP Address. The address consists of too many groups")
-
-
-proc parseIpAddress*(address_str: string): IpAddress =
-  ## Parses an IP address
-  ## Raises EInvalidValue on error
-  if address_str == nil:
-    raise newException(ValueError, "IP Address string is nil")
-  if address_str.contains(':'):
-    return parseIPv6Address(address_str)
-  else:
-    return parseIPv4Address(address_str)
-
-proc isIpAddress*(address_str: string): bool {.tags: [].} =
-  ## Checks if a string is an IP address
-  ## Returns true if it is, false otherwise
-  try:
-    discard parseIpAddress(address_str)
-  except ValueError:
-    return false
-  return true
-
 proc dial*(address: string, port: Port,
            protocol = IPPROTO_TCP, buffered = true): Socket
            {.tags: [ReadIOEffect, WriteIOEffect].} =
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 82a0c0c65..c94a65a63 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -335,7 +335,8 @@ proc execProcesses*(cmds: openArray[string],
       if afterRunEvent != nil: afterRunEvent(i, p)
       close(p)
 
-proc select*(readfds: var seq[Process], timeout = 500): int {.benign.}
+proc select*(readfds: var seq[Process], timeout = 500): int
+  {.benign, deprecated.}
   ## `select` with a sensible Nim interface. `timeout` is in milliseconds.
   ## Specify -1 for no timeout. Returns the number of processes that are
   ## ready to read from. The processes that are ready to be read from are
@@ -343,6 +344,9 @@ proc select*(readfds: var seq[Process], timeout = 500): int {.benign.}
   ##
   ## **Warning**: This function may give unexpected or completely wrong
   ## results on Windows.
+  ##
+  ## **Deprecated since version 0.17.0**: This procedure isn't cross-platform
+  ## and so should not be used in newly written code.
 
 when not defined(useNimRtl):
   proc execProcess(command: string,
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index 8d53a0360..b78e8d000 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -201,7 +201,7 @@ proc parseWhile*(s: string, token: var string, validChars: set[char],
 
 proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
   ## Finds the first occurrence of ``first``, then returns everything from there
-  ## up to ``second``(if ``second`` is '\0', then ``first`` is used).
+  ## up to ``second`` (if ``second`` is '\0', then ``first`` is used).
   var i = skipUntil(s, first, start)+1+start
   result = ""
   discard s.parseUntil(result, if second == '\0': first else: second, i)
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 9383675f4..458c22f3a 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -1881,6 +1881,8 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
   ## of significant digits to be printed.
   ## `precision`'s default value is the maximum number of meaningful digits
   ## after the decimal point for Nim's ``float`` type.
+  ##
+  ## If ``precision == 0``, it tries to format it nicely.
   result = formatBiggestFloat(f, format, precision, decimalSep)
 
 proc trimZeros*(x: var string) {.noSideEffect.} =
diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim
index 3a43729dc..885b01621 100644
--- a/lib/system/atomics.nim
+++ b/lib/system/atomics.nim
@@ -165,8 +165,22 @@ when someGcc and hasThreadSupport:
 
   template fence*() = atomicThreadFence(ATOMIC_SEQ_CST)
 elif defined(vcc) and hasThreadSupport:
-  proc addAndFetch*(p: ptr int, val: int): int {.
-    importc: "_InterlockedExchangeAdd", header: "<intrin.h>".}
+  when defined(cpp):
+    when sizeof(int) == 8:
+      proc addAndFetch*(p: ptr int, val: int): int {.
+        importcpp: "_InterlockedExchangeAdd64(static_cast<NI volatile *>(#), #)",
+        header: "<intrin.h>".}
+    else:
+      proc addAndFetch*(p: ptr int, val: int): int {.
+        importcpp: "_InterlockedExchangeAdd(static_cast<NI volatile *>(#), #)",
+        header: "<intrin.h>".}
+  else:
+    when sizeof(int) == 8:
+      proc addAndFetch*(p: ptr int, val: int): int {.
+        importc: "_InterlockedExchangeAdd64", header: "<intrin.h>".}
+    else:
+      proc addAndFetch*(p: ptr int, val: int): int {.
+        importc: "_InterlockedExchangeAdd", header: "<intrin.h>".}
 
   proc fence*() {.importc: "_ReadWriteBarrier", header: "<intrin.h>".}
 
@@ -180,6 +194,7 @@ proc atomicInc*(memLoc: var int, x: int = 1): int =
     result = atomic_add_fetch(memLoc.addr, x, ATOMIC_RELAXED)
   elif defined(vcc) and hasThreadSupport:
     result = addAndFetch(memLoc.addr, x)
+    inc(result, x)
   else:
     inc(memLoc, x)
     result = memLoc
@@ -192,6 +207,7 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
       result = atomic_add_fetch(memLoc.addr, -x, ATOMIC_RELAXED)
   elif defined(vcc) and hasThreadSupport:
     result = addAndFetch(memLoc.addr, -x)
+    dec(result, x)
   else:
     dec(memLoc, x)
     result = memLoc
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index bdcb4c1f6..55f283d2d 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -299,8 +299,13 @@ proc raiseExceptionAux(e: ref Exception) =
 proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
   if e.name.isNil: e.name = ename
   when hasSomeStackTrace:
-    e.trace = ""
-    rawWriteStackTrace(e.trace)
+    if e.trace.isNil:
+      e.trace = ""
+      rawWriteStackTrace(e.trace)
+    elif framePtr != nil:
+      e.trace.add "[[reraised from:\n"
+      auxWriteStackTrace(framePtr, e.trace)
+      e.trace.add "]]\n"
   raiseExceptionAux(e)
 
 proc reraiseException() {.compilerRtl.} =
diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim
index fb354880f..f61b887ad 100644
--- a/lib/system/syslocks.nim
+++ b/lib/system/syslocks.nim
@@ -99,7 +99,7 @@ elif defined(genode):
 
 else:
   type
-    SysLock {.importc: "pthread_mutex_t", pure, final,
+    SysLockObj {.importc: "pthread_mutex_t", pure, final,
                header: """#include <sys/types.h>
                           #include <pthread.h>""".} = object
       when defined(linux) and defined(amd64):
@@ -111,7 +111,7 @@ else:
       when defined(linux) and defined(amd64):
         abi: array[4 div sizeof(cint), cint]  # actually a cint
 
-    SysCond {.importc: "pthread_cond_t", pure, final,
+    SysCondObj {.importc: "pthread_cond_t", pure, final,
                header: """#include <sys/types.h>
                           #include <pthread.h>""".} = object
       when defined(linux) and defined(amd64):
@@ -119,8 +119,62 @@ else:
 
     SysLockType = distinct cint
 
-  proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) {.
+  proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {.
     importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.}
+  proc deinitSysAux(L: var SysLockObj) {.noSideEffect,
+    importc: "pthread_mutex_destroy", header: "<pthread.h>".}
+
+  proc acquireSysAux(L: var SysLockObj) {.noSideEffect,
+    importc: "pthread_mutex_lock", header: "<pthread.h>".}
+  proc tryAcquireSysAux(L: var SysLockObj): cint {.noSideEffect,
+    importc: "pthread_mutex_trylock", header: "<pthread.h>".}
+
+  proc releaseSysAux(L: var SysLockObj) {.noSideEffect,
+    importc: "pthread_mutex_unlock", header: "<pthread.h>".}
+
+  when defined(ios):
+    # iOS will behave badly if sync primitives are moved in memory. In order
+    # to prevent this once and for all, we're doing an extra malloc when
+    # initializing the primitive.
+    type
+      SysLock = ptr SysLockObj
+      SysCond = ptr SysCondObj
+
+    when not declared(c_malloc):
+      proc c_malloc(size: csize): pointer {.
+        importc: "malloc", header: "<stdlib.h>".}
+      proc c_free(p: pointer) {.
+        importc: "free", header: "<stdlib.h>".}
+
+    proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
+      L = cast[SysLock](c_malloc(sizeof(SysLockObj)))
+      initSysLockAux(L[], attr)
+
+    proc deinitSys(L: var SysLock) =
+      deinitSysAux(L[])
+      c_free(L)
+
+    template acquireSys(L: var SysLock) =
+      acquireSysAux(L[])
+    template tryAcquireSys(L: var SysLock): bool =
+      tryAcquireSysAux(L[]) == 0'i32
+    template releaseSys(L: var SysLock) =
+      releaseSysAux(L[])
+  else:
+    type
+      SysLock = SysLockObj
+      SysCond = SysCondObj
+
+    template initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
+      initSysLockAux(L, attr)
+    template deinitSys(L: var SysLock) =
+      deinitSysAux(L)
+    template acquireSys(L: var SysLock) =
+      acquireSysAux(L)
+    template tryAcquireSys(L: var SysLock): bool =
+      tryAcquireSysAux(L) == 0'i32
+    template releaseSys(L: var SysLock) =
+      releaseSysAux(L)
 
   when insideRLocksModule:
     proc SysLockType_Reentrant: SysLockType =
@@ -130,27 +184,39 @@ else:
     proc setSysLockType(a: var SysLockAttr, t: SysLockType) {.
       importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.}
 
-  proc acquireSys(L: var SysLock) {.noSideEffect,
-    importc: "pthread_mutex_lock", header: "<pthread.h>".}
-  proc tryAcquireSysAux(L: var SysLock): cint {.noSideEffect,
-    importc: "pthread_mutex_trylock", header: "<pthread.h>".}
-
-  proc tryAcquireSys(L: var SysLock): bool {.inline.} =
-    result = tryAcquireSysAux(L) == 0'i32
-
-  proc releaseSys(L: var SysLock) {.noSideEffect,
-    importc: "pthread_mutex_unlock", header: "<pthread.h>".}
-  proc deinitSys(L: var SysLock) {.noSideEffect,
-    importc: "pthread_mutex_destroy", header: "<pthread.h>".}
-
-  when not insideRLocksModule:
-    proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) {.
+  else:
+    proc initSysCondAux(cond: var SysCondObj, cond_attr: pointer) {.
       importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
-    proc waitSysCond(cond: var SysCond, lock: var SysLock) {.
+    proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect,
+      importc: "pthread_cond_destroy", header: "<pthread.h>".}
+
+    proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj) {.
       importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
-    proc signalSysCond(cond: var SysCond) {.
+    proc signalSysCondAux(cond: var SysCondObj) {.
       importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
-    proc deinitSysCond(cond: var SysCond) {.noSideEffect,
-      importc: "pthread_cond_destroy", header: "<pthread.h>".}
+
+    when defined(ios):
+      proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) =
+        cond = cast[SysCond](c_malloc(sizeof(SysCondObj)))
+        initSysCondAux(cond[], cond_attr)
+
+      proc deinitSysCond(cond: var SysCond) =
+        deinitSysCondAux(cond[])
+        c_free(cond)
+
+      template waitSysCond(cond: var SysCond, lock: var SysLock) =
+        waitSysCondAux(cond[], lock[])
+      template signalSysCond(cond: var SysCond) =
+        signalSysCondAux(cond[])
+    else:
+      template initSysCond(cond: var SysCond, cond_attr: pointer = nil) =
+        initSysCondAux(cond, cond_attr)
+      template deinitSysCond(cond: var SysCond) =
+        deinitSysCondAux(cond)
+
+      template waitSysCond(cond: var SysCond, lock: var SysLock) =
+        waitSysCondAux(cond, lock)
+      template signalSysCond(cond: var SysCond) =
+        signalSysCondAux(cond)
 
 {.pop.}
diff --git a/lib/upcoming/asyncdispatch.nim b/lib/upcoming/asyncdispatch.nim
index 7c0497cc6..1623d8375 100644
--- a/lib/upcoming/asyncdispatch.nim
+++ b/lib/upcoming/asyncdispatch.nim
@@ -9,7 +9,7 @@
 
 include "system/inclrtl"
 
-import os, oids, tables, strutils, times, heapqueue, lists, options
+import os, tables, strutils, times, heapqueue, lists, options
 
 import nativesockets, net, deques
 
@@ -219,6 +219,11 @@ when defined(windows) or defined(nimdoc):
     if gDisp.isNil: gDisp = newDispatcher()
     result = gDisp
 
+  proc setGlobalDispatcher*(disp: PDispatcher) =
+    if not gDisp.isNil:
+      assert gDisp.callbacks.len == 0
+    gDisp = disp
+
   proc register*(fd: AsyncFD) =
     ## Registers ``fd`` with the dispatcher.
     let p = getGlobalDispatcher()
@@ -1080,6 +1085,11 @@ else:
     if gDisp.isNil: gDisp = newDispatcher()
     result = gDisp
 
+  proc setGlobalDispatcher*(disp: PDispatcher) =
+    if not gDisp.isNil:
+      assert gDisp.callbacks.len == 0
+    gDisp = disp
+
   proc register*(fd: AsyncFD) =
     let p = getGlobalDispatcher()
     var data = newAsyncData()