summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/pure/collections/deques.nim5
-rw-r--r--lib/pure/collections/queues.nim5
-rw-r--r--lib/pure/collections/sets.nim4
-rw-r--r--lib/pure/collections/tables.nim4
-rw-r--r--lib/pure/ioselectors.nim14
-rw-r--r--lib/pure/ioselects/ioselectors_epoll.nim320
-rw-r--r--lib/pure/osproc.nim14
-rw-r--r--lib/pure/strscans.nim6
-rw-r--r--lib/pure/times.nim29
-rw-r--r--lib/system.nim10
-rw-r--r--lib/system/mmdisp.nim2
-rw-r--r--lib/system/sysio.nim4
-rw-r--r--lib/upcoming/asyncdispatch.nim20
13 files changed, 262 insertions, 175 deletions
diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim
index c25429778..495d7896c 100644
--- a/lib/pure/collections/deques.nim
+++ b/lib/pure/collections/deques.nim
@@ -160,7 +160,10 @@ proc peekLast*[T](deq: Deque[T]): T {.inline.} =
   emptyCheck(deq)
   result = deq.data[(deq.tail - 1) and deq.mask]
 
-proc default[T](t: typedesc[T]): T {.inline.} = discard
+template default[T](t: typedesc[T]): T =
+  var v: T
+  v
+
 proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
   ## Remove and returns the first element of the `deq`.
   emptyCheck(deq)
diff --git a/lib/pure/collections/queues.nim b/lib/pure/collections/queues.nim
index e4d7eeef1..0490ae494 100644
--- a/lib/pure/collections/queues.nim
+++ b/lib/pure/collections/queues.nim
@@ -154,7 +154,10 @@ proc add*[T](q: var Queue[T], item: T) =
   q.data[q.wr] = item
   q.wr = (q.wr + 1) and q.mask
 
-proc default[T](t: typedesc[T]): T {.inline.} = discard
+template default[T](t: typedesc[T]): T =
+  var v: T
+  v
+
 proc pop*[T](q: var Queue[T]): T {.inline, discardable.} =
   ## Remove and returns the first (oldest) element of the queue `q`.
   emptyCheck(q)
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index 552e41ef7..b2ffbe58d 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -261,7 +261,9 @@ template doWhile(a, b) =
     b
     if not a: break
 
-proc default[T](t: typedesc[T]): T {.inline.} = discard
+template default[T](t: typedesc[T]): T =
+  var v: T
+  v
 
 proc excl*[A](s: var HashSet[A], key: A) =
   ## Excludes `key` from the set `s`.
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index e6e72d9ed..57e98bf5c 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -954,7 +954,7 @@ proc inc*[A](t: var CountTable[A], key: A, val = 1) =
     inc(t.counter)
 
 proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
-  ## returns the largest (key,val)-pair. Efficiency: O(n)
+  ## returns the (key,val)-pair with the smallest `val`. Efficiency: O(n)
   assert t.len > 0
   var minIdx = 0
   for h in 1..high(t.data):
@@ -1080,7 +1080,7 @@ proc inc*[A](t: CountTableRef[A], key: A, val = 1) =
   t[].inc(key, val)
 
 proc smallest*[A](t: CountTableRef[A]): (A, int) =
-  ## returns the largest (key,val)-pair. Efficiency: O(n)
+  ## returns the (key,val)-pair with the smallest `val`. Efficiency: O(n)
   t[].smallest
 
 proc largest*[A](t: CountTableRef[A]): (A, int) =
diff --git a/lib/pure/ioselectors.nim b/lib/pure/ioselectors.nim
index 744bdbaa1..5745d0b72 100644
--- a/lib/pure/ioselectors.nim
+++ b/lib/pure/ioselectors.nim
@@ -18,10 +18,12 @@
 ## Supported features: files, sockets, pipes, timers, processes, signals
 ## and user events.
 ##
-## Fully supported OS: MacOSX, FreeBSD, OpenBSD, NetBSD, Linux.
+## Fully supported OS: MacOSX, FreeBSD, OpenBSD, NetBSD, Linux (except
+## for Android).
 ##
 ## Partially supported OS: Windows (only sockets and user events),
 ## Solaris (files, sockets, handles and user events).
+## Android (files, sockets, handles and user events).
 ##
 ## TODO: ``/dev/poll``, ``event ports`` and filesystem events.
 
@@ -29,9 +31,11 @@ import os
 
 const hasThreadSupport = compileOption("threads") and defined(threadsafe)
 
-const supportedPlatform = defined(macosx) or defined(freebsd) or
-                          defined(netbsd) or defined(openbsd) or
-                          defined(linux)
+const ioselSupportedPlatform* = defined(macosx) or defined(freebsd) or
+                                defined(netbsd) or defined(openbsd) or
+                                (defined(linux) and not defined(android))
+  ## This constant is used to determine whether the destination platform is
+  ## fully supported by ``ioselectors`` module.
 
 const bsdPlatform = defined(macosx) or defined(freebsd) or
                     defined(netbsd) or defined(openbsd)
@@ -244,7 +248,7 @@ else:
       skey.key.fd = pkeyfd
       skey.key.data = pdata
 
-  when supportedPlatform:
+  when ioselSupportedPlatform:
     template blockSignals(newmask: var Sigset, oldmask: var Sigset) =
       when hasThreadSupport:
         if posix.pthread_sigmask(SIG_BLOCK, newmask, oldmask) == -1:
diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim
index 4bc6e9d51..ceba670fb 100644
--- a/lib/pure/ioselects/ioselectors_epoll.nim
+++ b/lib/pure/ioselects/ioselectors_epoll.nim
@@ -14,27 +14,29 @@ import posix, times
 # Maximum number of events that can be returned
 const MAX_EPOLL_RESULT_EVENTS = 64
 
-type
-  SignalFdInfo* {.importc: "struct signalfd_siginfo",
-                  header: "<sys/signalfd.h>", pure, final.} = object
-    ssi_signo*: uint32
-    ssi_errno*: int32
-    ssi_code*: int32
-    ssi_pid*: uint32
-    ssi_uid*: uint32
-    ssi_fd*: int32
-    ssi_tid*: uint32
-    ssi_band*: uint32
-    ssi_overrun*: uint32
-    ssi_trapno*: uint32
-    ssi_status*: int32
-    ssi_int*: int32
-    ssi_ptr*: uint64
-    ssi_utime*: uint64
-    ssi_stime*: uint64
-    ssi_addr*: uint64
-    pad* {.importc: "__pad".}: array[0..47, uint8]
+when not defined(android):
+  type
+    SignalFdInfo* {.importc: "struct signalfd_siginfo",
+                    header: "<sys/signalfd.h>", pure, final.} = object
+      ssi_signo*: uint32
+      ssi_errno*: int32
+      ssi_code*: int32
+      ssi_pid*: uint32
+      ssi_uid*: uint32
+      ssi_fd*: int32
+      ssi_tid*: uint32
+      ssi_band*: uint32
+      ssi_overrun*: uint32
+      ssi_trapno*: uint32
+      ssi_status*: int32
+      ssi_int*: int32
+      ssi_ptr*: uint64
+      ssi_utime*: uint64
+      ssi_stime*: uint64
+      ssi_addr*: uint64
+      pad* {.importc: "__pad".}: array[0..47, uint8]
 
+type
   eventFdData {.importc: "eventfd_t",
                 header: "<sys/eventfd.h>", pure, final.} = uint64
   epoll_data {.importc: "union epoll_data", header: "<sys/epoll.h>",
@@ -68,12 +70,22 @@ proc timerfd_create(clock_id: ClockId, flags: cint): cint
 proc timerfd_settime(ufd: cint, flags: cint,
                       utmr: var Itimerspec, otmr: var Itimerspec): cint
      {.cdecl, importc: "timerfd_settime", header: "<sys/timerfd.h>".}
-proc signalfd(fd: cint, mask: var Sigset, flags: cint): cint
-     {.cdecl, importc: "signalfd", header: "<sys/signalfd.h>".}
 proc eventfd(count: cuint, flags: cint): cint
      {.cdecl, importc: "eventfd", header: "<sys/eventfd.h>".}
-proc ulimit(cmd: cint): clong
-     {.importc: "ulimit", header: "<ulimit.h>", varargs.}
+
+when not defined(android):
+  proc signalfd(fd: cint, mask: var Sigset, flags: cint): cint
+       {.cdecl, importc: "signalfd", header: "<sys/signalfd.h>".}
+
+var RLIMIT_NOFILE {.importc: "RLIMIT_NOFILE",
+                    header: "<sys/resource.h>".}: cint
+type
+  rlimit {.importc: "struct rlimit",
+           header: "<sys/resource.h>", pure, final.} = object
+    rlim_cur: int
+    rlim_max: int
+proc getrlimit(resource: cint, rlp: var rlimit): cint
+     {.importc: "getrlimit",header: "<sys/resource.h>".}
 
 when hasThreadSupport:
   type
@@ -97,7 +109,10 @@ type
   SelectEvent* = ptr SelectEventImpl
 
 proc newSelector*[T](): Selector[T] =
-  var maxFD = int(ulimit(4, 0))
+  var a = rlimit()
+  if getrlimit(RLIMIT_NOFILE, a) != 0:
+    raiseOsError(osLastError())
+  var maxFD = int(a.rlim_max)
   doAssert(maxFD > 0)
 
   var epollFD = epoll_create(MAX_EPOLL_RESULT_EVENTS)
@@ -194,39 +209,53 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
   doAssert(pkey.ident != 0)
 
   if pkey.events != {}:
-    if pkey.events * {Event.Read, Event.Write} != {}:
-      var epv = epoll_event()
-      if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
-        raiseOSError(osLastError())
-      dec(s.count)
-    elif Event.Timer in pkey.events:
-      var epv = epoll_event()
-      if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
-        raiseOSError(osLastError())
-      discard posix.close(fdi.cint)
-      dec(s.count)
-    elif Event.Signal in pkey.events:
-      var epv = epoll_event()
-      if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
-        raiseOSError(osLastError())
-      var nmask, omask: Sigset
-      discard sigemptyset(nmask)
-      discard sigemptyset(omask)
-      discard sigaddset(nmask, cint(s.fds[fdi].param))
-      unblockSignals(nmask, omask)
-      discard posix.close(fdi.cint)
-      dec(s.count)
-    elif Event.Process in pkey.events:
-      var epv = epoll_event()
-      if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
-        raiseOSError(osLastError())
-      var nmask, omask: Sigset
-      discard sigemptyset(nmask)
-      discard sigemptyset(omask)
-      discard sigaddset(nmask, SIGCHLD)
-      unblockSignals(nmask, omask)
-      discard posix.close(fdi.cint)
-      dec(s.count)
+    when not defined(android):
+      if pkey.events * {Event.Read, Event.Write} != {}:
+        var epv = epoll_event()
+        if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
+          raiseOSError(osLastError())
+        dec(s.count)
+      elif Event.Timer in pkey.events:
+        var epv = epoll_event()
+        if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
+          raiseOSError(osLastError())
+        discard posix.close(fdi.cint)
+        dec(s.count)
+      elif Event.Signal in pkey.events:
+        var epv = epoll_event()
+        if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
+          raiseOSError(osLastError())
+        var nmask, omask: Sigset
+        discard sigemptyset(nmask)
+        discard sigemptyset(omask)
+        discard sigaddset(nmask, cint(s.fds[fdi].param))
+        unblockSignals(nmask, omask)
+        discard posix.close(fdi.cint)
+        dec(s.count)
+      elif Event.Process in pkey.events:
+        var epv = epoll_event()
+        if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
+          raiseOSError(osLastError())
+        var nmask, omask: Sigset
+        discard sigemptyset(nmask)
+        discard sigemptyset(omask)
+        discard sigaddset(nmask, SIGCHLD)
+        unblockSignals(nmask, omask)
+        discard posix.close(fdi.cint)
+        dec(s.count)
+    else:
+      if pkey.events * {Event.Read, Event.Write} != {}:
+        var epv = epoll_event()
+        if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
+          raiseOSError(osLastError())
+        dec(s.count)
+      elif Event.Timer in pkey.events:
+        var epv = epoll_event()
+        if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) == -1:
+          raiseOSError(osLastError())
+        discard posix.close(fdi.cint)
+        dec(s.count)
+
   pkey.ident = 0
   pkey.events = {}
 
@@ -280,60 +309,61 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
   inc(s.count)
   result = fdi
 
-proc registerSignal*[T](s: Selector[T], signal: int,
-                        data: T): int {.discardable.} =
-  var
-    nmask: Sigset
-    omask: Sigset
+when not defined(android):
+  proc registerSignal*[T](s: Selector[T], signal: int,
+                          data: T): int {.discardable.} =
+    var
+      nmask: Sigset
+      omask: Sigset
 
-  discard sigemptyset(nmask)
-  discard sigemptyset(omask)
-  discard sigaddset(nmask, cint(signal))
-  blockSignals(nmask, omask)
+    discard sigemptyset(nmask)
+    discard sigemptyset(omask)
+    discard sigaddset(nmask, cint(signal))
+    blockSignals(nmask, omask)
 
-  let fdi = signalfd(-1, nmask, 0).int
-  if fdi == -1:
-    raiseOSError(osLastError())
-  setNonBlocking(fdi.cint)
+    let fdi = signalfd(-1, nmask, 0).int
+    if fdi == -1:
+      raiseOSError(osLastError())
+    setNonBlocking(fdi.cint)
 
-  s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+    s.checkFd(fdi)
+    doAssert(s.fds[fdi].ident == 0)
 
-  var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
-  epv.data.u64 = fdi.uint
-  if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
-    raiseOSError(osLastError())
-  s.setKey(fdi, signal, {Event.Signal}, signal, data)
-  inc(s.count)
-  result = fdi
+    var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
+    epv.data.u64 = fdi.uint
+    if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
+      raiseOSError(osLastError())
+    s.setKey(fdi, signal, {Event.Signal}, signal, data)
+    inc(s.count)
+    result = fdi
 
-proc registerProcess*[T](s: Selector, pid: int,
-                         data: T): int {.discardable.} =
-  var
-    nmask: Sigset
-    omask: Sigset
+  proc registerProcess*[T](s: Selector, pid: int,
+                           data: T): int {.discardable.} =
+    var
+      nmask: Sigset
+      omask: Sigset
 
-  discard sigemptyset(nmask)
-  discard sigemptyset(omask)
-  discard sigaddset(nmask, posix.SIGCHLD)
-  blockSignals(nmask, omask)
+    discard sigemptyset(nmask)
+    discard sigemptyset(omask)
+    discard sigaddset(nmask, posix.SIGCHLD)
+    blockSignals(nmask, omask)
 
-  let fdi = signalfd(-1, nmask, 0).int
-  if fdi == -1:
-    raiseOSError(osLastError())
-  setNonBlocking(fdi.cint)
+    let fdi = signalfd(-1, nmask, 0).int
+    if fdi == -1:
+      raiseOSError(osLastError())
+    setNonBlocking(fdi.cint)
 
-  s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+    s.checkFd(fdi)
+    doAssert(s.fds[fdi].ident == 0)
 
-  var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
-  epv.data.u64 = fdi.uint
-  epv.events = EPOLLIN or EPOLLRDHUP
-  if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
-    raiseOSError(osLastError())
-  s.setKey(fdi, pid, {Event.Process, Event.Oneshot}, pid, data)
-  inc(s.count)
-  result = fdi
+    var epv = epoll_event(events: EPOLLIN or EPOLLRDHUP)
+    epv.data.u64 = fdi.uint
+    epv.events = EPOLLIN or EPOLLRDHUP
+    if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fdi.cint, addr epv) == -1:
+      raiseOSError(osLastError())
+    s.setKey(fdi, pid, {Event.Process, Event.Oneshot}, pid, data)
+    inc(s.count)
+    result = fdi
 
 proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
   let fdi = int(ev.efd)
@@ -382,40 +412,60 @@ proc selectInto*[T](s: Selector[T], timeout: int,
         events.incl(Event.Error)
       if (pevents and EPOLLOUT) != 0:
         events.incl(Event.Write)
-      if (pevents and EPOLLIN) != 0:
-        if Event.Read in skey.events:
-          events.incl(Event.Read)
-        elif Event.Timer in skey.events:
-          var data: uint64 = 0
-          if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
-            raiseOSError(osLastError())
-          events = {Event.Timer}
-        elif Event.Signal in skey.events:
-          var data = SignalFdInfo()
-          if posix.read(fdi.cint, addr data,
-                        sizeof(SignalFdInfo)) != sizeof(SignalFdInfo):
-            raiseOsError(osLastError())
-          events = {Event.Signal}
-        elif Event.Process in skey.events:
-          var data = SignalFdInfo()
-          if posix.read(fdi.cint, addr data,
-                        sizeof(SignalFdInfo)) != sizeof(SignalFdInfo):
-            raiseOsError(osLastError())
-          if cast[int](data.ssi_pid) == skey.param:
-            events = {Event.Process}
-          else:
-            inc(i)
-            continue
-        elif Event.User in skey.events:
-          var data: uint64 = 0
-          if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
-            let err = osLastError()
-            if err == OSErrorCode(EAGAIN):
+      when not defined(android):
+        if (pevents and EPOLLIN) != 0:
+          if Event.Read in skey.events:
+            events.incl(Event.Read)
+          elif Event.Timer in skey.events:
+            var data: uint64 = 0
+            if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
+              raiseOSError(osLastError())
+            events = {Event.Timer}
+          elif Event.Signal in skey.events:
+            var data = SignalFdInfo()
+            if posix.read(fdi.cint, addr data,
+                          sizeof(SignalFdInfo)) != sizeof(SignalFdInfo):
+              raiseOsError(osLastError())
+            events = {Event.Signal}
+          elif Event.Process in skey.events:
+            var data = SignalFdInfo()
+            if posix.read(fdi.cint, addr data,
+                          sizeof(SignalFdInfo)) != sizeof(SignalFdInfo):
+              raiseOsError(osLastError())
+            if cast[int](data.ssi_pid) == skey.param:
+              events = {Event.Process}
+            else:
               inc(i)
               continue
-            else:
-              raiseOSError(err)
-          events = {Event.User}
+          elif Event.User in skey.events:
+            var data: uint64 = 0
+            if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
+              let err = osLastError()
+              if err == OSErrorCode(EAGAIN):
+                inc(i)
+                continue
+              else:
+                raiseOSError(err)
+            events = {Event.User}
+      else:
+        if (pevents and EPOLLIN) != 0:
+          if Event.Read in skey.events:
+            events.incl(Event.Read)
+          elif Event.Timer in skey.events:
+            var data: uint64 = 0
+            if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
+              raiseOSError(osLastError())
+            events = {Event.Timer}
+          elif Event.User in skey.events:
+            var data: uint64 = 0
+            if posix.read(fdi.cint, addr data, sizeof(uint64)) != sizeof(uint64):
+              let err = osLastError()
+              if err == OSErrorCode(EAGAIN):
+                inc(i)
+                continue
+              else:
+                raiseOSError(err)
+            events = {Event.User}
 
       skey.key.events = events
       results[k] = skey.key
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 76bd2dfe1..1d43bb321 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -965,10 +965,16 @@ elif not defined(useNimRtl):
     var ret : int
     var status : cint = 1
     ret = waitpid(p.id, status, WNOHANG)
-    if WIFEXITED(status):
-      p.exitStatus = status
-    if ret == 0: return true # Can't establish status. Assume running.
-    result = ret == int(p.id)
+    if ret == int(p.id):
+      if WIFEXITED(status):
+        p.exitStatus = status
+        return false
+      else:
+        return true
+    elif ret == 0:
+      return true # Can't establish status. Assume running.
+    else:
+      return false
 
   proc terminate(p: Process) =
     if kill(p.id, SIGTERM) != 0'i32:
diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index 246f018c5..fc400173f 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -76,7 +76,7 @@ to a variable (that was passed to the ``scanf`` macro) while ``$[]`` merely
 optional tokens.
 
 
-In this example, we define a helper proc ``skipSep`` that skips some separators
+In this example, we define a helper proc ``someSep`` that skips some separators
 which we then use in our scanf pattern to help us in the matching process:
 
 .. code-block:: nim
@@ -86,14 +86,14 @@ which we then use in our scanf pattern to help us in the matching process:
     result = 0
     while input[start+result] in seps: inc result
 
-  if scanf(input, "$w${someSep}$w", key, value):
+  if scanf(input, "$w$[someSep]$w", key, value):
     ...
 
 It also possible to pass arguments to a user definable matcher:
 
 .. code-block:: nim
 
-  proc ndigits(input: string; start: int; intVal: var int; n: int): int =
+  proc ndigits(input: string; intVal: var int; start: int; n: int): int =
     # matches exactly ``n`` digits. Matchers need to return 0 if nothing
     # matched or otherwise the number of processed chars.
     var x = 0
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 1767a37be..cf4e7dde6 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -436,6 +436,11 @@ when not defined(JS):
     TimeInfoPtr = ptr StructTM
     Clock {.importc: "clock_t".} = distinct int
 
+  when not defined(windows):
+    # This is not ANSI C, but common enough
+    proc timegm(t: StructTM): Time {.
+      importc: "timegm", header: "<time.h>", tags: [].}
+
   proc localtime(timer: ptr Time): TimeInfoPtr {.
     importc: "localtime", header: "<time.h>", tags: [].}
   proc gmtime(timer: ptr Time): TimeInfoPtr {.
@@ -515,20 +520,22 @@ when not defined(JS):
     # the conversion is not expensive
 
   proc timeInfoToTime(timeInfo: TimeInfo): Time =
-    var cTimeInfo = timeInfo # for C++ we have to make a copy,
-    # because the header of mktime is broken in my version of libc
-    result = mktime(timeInfoToTM(cTimeInfo))
-    # mktime is defined to interpret the input as local time. As timeInfoToTM
-    # does ignore the timezone, we need to adjust this here.
-    result = Time(TimeImpl(result) - getTimezone() + timeInfo.timezone)
+    toTime(timeInfo)
 
   proc toTime(timeInfo: TimeInfo): Time =
-    var cTimeInfo = timeInfo # for C++ we have to make a copy,
+    var cTimeInfo = timeInfo # for C++ we have to make a copy
     # because the header of mktime is broken in my version of libc
-    result = mktime(timeInfoToTM(cTimeInfo))
-    # mktime is defined to interpret the input as local time. As timeInfoToTM
-    # does ignore the timezone, we need to adjust this here.
-    result = Time(TimeImpl(result) - getTimezone() + timeInfo.timezone)
+
+    when defined(windows):
+      # On Windows `mktime` is broken enough to make this work.
+      result = mktime(timeInfoToTM(cTimeInfo))
+      # mktime is defined to interpret the input as local time. As timeInfoToTM
+      # does ignore the timezone, we need to adjust this here.
+      result = Time(TimeImpl(result) - getTimezone() + timeInfo.timezone)
+    else:
+      result = timegm(timeInfoToTM(cTimeInfo))
+      # As timeInfoToTM does ignore the timezone, we need to adjust this here.
+      result = Time(TimeImpl(result) + timeInfo.timezone)
 
   const
     epochDiff = 116444736000000000'i64
diff --git a/lib/system.nim b/lib/system.nim
index 69d3db291..8209dbc23 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2595,6 +2595,14 @@ else:
     if x < 0: -x else: x
 {.pop.}
 
+type
+  FileSeekPos* = enum ## Position relative to which seek should happen
+                      # The values are ordered so that they match with stdio
+                      # SEEK_SET, SEEK_CUR and SEEK_END respectively.
+    fspSet            ## Seek to absolute value
+    fspCur            ## Seek relative to current position
+    fspEnd            ## Seek relative to end
+
 when not defined(JS): #and not defined(nimscript):
   {.push stack_trace: off, profiler:off.}
 
@@ -2858,7 +2866,7 @@ when not defined(JS): #and not defined(nimscript):
       ## file `f`. Returns the number of actual written bytes, which may be less
       ## than `len` in case of an error.
 
-    proc setFilePos*(f: File, pos: int64) {.benign.}
+    proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign.}
       ## sets the position of the file pointer that is used for read/write
       ## operations. The file's first byte has the index zero.
 
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index 63af49e35..431f84bfd 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -20,7 +20,7 @@ const
   alwaysCycleGC = defined(smokeCycles)
   alwaysGC = defined(fulldebug) # collect after every memory
                                 # allocation (for debugging)
-  leakDetector = false
+  leakDetector = defined(leakDetector)
   overwriteFree = defined(nimBurnFree) # overwrite memory with 0xFF before free
   trackAllocationSource = leakDetector
 
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 5c10392f1..29c5777cc 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -336,8 +336,8 @@ proc open(f: var File, filehandle: FileHandle, mode: FileMode): bool =
   f = c_fdopen(filehandle, FormatOpen[mode])
   result = f != nil
 
-proc setFilePos(f: File, pos: int64) =
-  if c_fseek(f, clong(pos), 0) != 0:
+proc setFilePos(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) =
+  if c_fseek(f, clong(pos), cint(relativeTo)) != 0:
     raiseEIO("cannot set file position")
 
 proc getFilePos(f: File): int64 =
diff --git a/lib/upcoming/asyncdispatch.nim b/lib/upcoming/asyncdispatch.nim
index 9a35cf3c8..ca5c1f64c 100644
--- a/lib/upcoming/asyncdispatch.nim
+++ b/lib/upcoming/asyncdispatch.nim
@@ -1092,11 +1092,6 @@ else:
   import ioselectors
   from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
                     MSG_NOSIGNAL
-
-  const supportedPlatform = defined(linux) or defined(freebsd) or
-                            defined(netbsd) or defined(openbsd) or
-                            defined(macosx)
-
   type
     AsyncFD* = distinct cint
     Callback = proc (fd: AsyncFD): bool {.closure,gcsafe.}
@@ -1191,7 +1186,7 @@ else:
     var keys: array[64, ReadyKey[AsyncData]]
 
     let p = getGlobalDispatcher()
-    when supportedPlatform:
+    when ioselSupportedPlatform:
       let customSet = {Event.Timer, Event.Signal, Event.Process,
                        Event.Vnode, Event.User}
 
@@ -1225,7 +1220,7 @@ else:
               else:
                 break
 
-        when supportedPlatform:
+        when ioselSupportedPlatform:
           if (customSet * events) != {}:
             for node in keys[i].data.readCBs[].nodes():
               let cb = node.value
@@ -1234,6 +1229,15 @@ else:
               if cb(fd.AsyncFD):
                 keys[i].data.readCBs[].remove(node)
                 p.selector.unregister(fd)
+        else:
+          if Event.User in events or events == {Event.Error}:
+            for node in keys[i].data.readCBs[].nodes():
+              let cb = node.value
+              custom = true
+              if cb != nil:
+                if cb(fd.AsyncFD):
+                  keys[i].data.readCBs[].remove(node)
+                  p.selector.unregister(fd)
 
         # because state `data` can be modified in callback we need to update
         # descriptor events with currently registered callbacks.
@@ -1496,7 +1500,7 @@ else:
     addRead(socket, cb)
     return retFuture
 
-  when supportedPlatform:
+  when ioselSupportedPlatform:
 
     proc addTimer*(timeout: int, oneshot: bool, cb: Callback) =
       ## Start watching for timeout expiration, and then call the