summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2016-12-17 23:04:34 +0100
committerAraq <rumpf_a@web.de>2016-12-17 23:04:34 +0100
commit24239c23619e9d64562699b5d890fa2575e9c9cb (patch)
tree7b68d65e1bc5c12a465dd29814e6328cb25e30b4
parent3a4ec7f101f539fb536086b59c84e1b2c095ef82 (diff)
parentd4c33df91996e30ded5f7f2014e7cd49176b5b05 (diff)
downloadNim-24239c23619e9d64562699b5d890fa2575e9c9cb.tar.gz
Merge branch 'devel' into sighashes
-rw-r--r--compiler/ccgstmts.nim6
-rw-r--r--compiler/extccomp.nim10
-rw-r--r--compiler/msgs.nim4
-rw-r--r--compiler/pragmas.nim18
-rw-r--r--compiler/semexprs.nim4
-rw-r--r--compiler/semstmts.nim4
-rw-r--r--doc/manual/pragmas.txt12
-rw-r--r--lib/pure/ioselectors.nim14
-rw-r--r--lib/pure/ioselects/ioselectors_epoll.nim320
-rw-r--r--lib/upcoming/asyncdispatch.nim20
-rw-r--r--tests/async/tioselectors.nim11
-rw-r--r--tests/async/tupcoming_async.nim88
-rw-r--r--tests/cpp/temitlist.nim22
-rw-r--r--web/news/e029_version_0_16_0.rst22
14 files changed, 340 insertions, 215 deletions
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 7282e6af5..123d627b5 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -973,7 +973,11 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
           r = mangleName(p.module, sym)
           sym.loc.r = r       # but be consequent!
         res.add($r)
-    else: internalError(t.sons[i].info, "genAsmOrEmitStmt()")
+    else:
+      var a: TLoc
+      initLocExpr(p, t.sons[i], a)
+      res.add($a.rdLoc)
+      #internalError(t.sons[i].info, "genAsmOrEmitStmt()")
 
   if isAsmStmt and hasGnuAsm in CC[cCompiler].props:
     for x in splitLines(res):
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 1acc98836..175e8d831 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -659,8 +659,14 @@ proc compileCFile(list: TLinkedList, script: var Rope, cmds: var TStringSeq,
 
 proc getLinkCmd(projectfile, objfiles: string): string =
   if optGenStaticLib in gGlobalOptions:
-    let name = splitFile(gProjectName).name
-    result = CC[cCompiler].buildLib % ["libfile", (libNameTmpl() % name),
+    var libname: string
+    if options.outFile.len > 0:
+      libname = options.outFile.expandTilde
+      if not libname.isAbsolute():
+        libname = getCurrentDir() / libname
+    else:
+      libname = (libNameTmpl() % splitFile(gProjectName).name)
+    result = CC[cCompiler].buildLib % ["libfile", libname,
                                        "objfiles", objfiles]
   else:
     var linkerExe = getConfigVar(cCompiler, ".linkerexe")
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 94b0bee00..e6a2b75a6 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -48,7 +48,7 @@ type
     errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType,
     errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit,
     errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType,
-    errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected,
+    errCastNotInSafeMode, errExprCannotBeCastToX, errCommaOrParRiExpected,
     errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected,
     errMagicOnlyInSystem, errPowerOfTwoExpected,
     errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv,
@@ -229,7 +229,7 @@ const
     errExprHasNoType: "expression has no type",
     errExprXHasNoType: "expression \'$1\' has no type (or is ambiguous)",
     errCastNotInSafeMode: "\'cast\' not allowed in safe mode",
-    errExprCannotBeCastedToX: "expression cannot be casted to $1",
+    errExprCannotBeCastToX: "expression cannot be cast to $1",
     errCommaOrParRiExpected: "',' or ')' expected",
     errCurlyLeOrParLeExpected: "\'{\' or \'(\' expected",
     errSectionExpected: "section (\'type\', \'proc\', etc.) expected",
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index 6697abe5e..b9f00399e 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -460,8 +460,22 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
     result = newNode(nkAsmStmt, n.info)
 
 proc pragmaEmit(c: PContext, n: PNode) =
-  discard getStrLitNode(c, n)
-  n.sons[1] = semAsmOrEmit(c, n, '`')
+  if n.kind != nkExprColonExpr:
+    localError(n.info, errStringLiteralExpected)
+  else:
+    let n1 = n[1]
+    if n1.kind == nkBracket:
+      var b = newNodeI(nkBracket, n1.info, n1.len)
+      for i in 0..<n1.len:
+        b.sons[i] = c.semExpr(c, n1[i])
+      n.sons[1] = b
+    else:
+      n.sons[1] = c.semConstExpr(c, n1)
+      case n.sons[1].kind
+      of nkStrLit, nkRStrLit, nkTripleStrLit:
+        n.sons[1] = semAsmOrEmit(c, n, '`')
+      else:
+        localError(n.info, errStringLiteralExpected)
 
 proc noVal(n: PNode) =
   if n.kind == nkExprColonExpr: invalidPragma(n)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 5e6611064..9076ce99b 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -127,7 +127,7 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
       discard
 
 proc isCastable(dst, src: PType): bool =
-  ## Checks whether the source type can be casted to the destination type.
+  ## Checks whether the source type can be cast to the destination type.
   ## Casting is very unrestrictive; casts are allowed as long as
   ## castDest.size >= src.size, and typeAllowed(dst, skParam)
   #const
@@ -223,7 +223,7 @@ proc semCast(c: PContext, n: PNode): PNode =
   addSon(result, copyTree(n.sons[0]))
   addSon(result, semExprWithType(c, n.sons[1]))
   if not isCastable(result.typ, result.sons[1].typ):
-    localError(result.info, errExprCannotBeCastedToX,
+    localError(result.info, errExprCannotBeCastToX,
                typeToString(result.typ))
 
 proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index fb57f6500..ec4279e60 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -760,8 +760,10 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       internalAssert st.kind in {tyPtr, tyRef}
       internalAssert st.lastSon.sym == nil
       incl st.flags, tfRefsAnonObj
-      st.lastSon.sym = newSym(skType, getIdent(s.name.s & ":ObjectType"),
+      let obj = newSym(skType, getIdent(s.name.s & ":ObjectType"),
                               getCurrOwner(), s.info)
+      obj.typ = st.lastSon
+      st.lastSon.sym = obj
 
 
 proc checkForMetaFields(n: PNode) =
diff --git a/doc/manual/pragmas.txt b/doc/manual/pragmas.txt
index a19f41a34..2a276c2e7 100644
--- a/doc/manual/pragmas.txt
+++ b/doc/manual/pragmas.txt
@@ -26,9 +26,6 @@ It can also be used as a statement, in that case it takes a list of *renamings*.
     Stream = ref object
   {.deprecated: [TFile: File, PStream: Stream].}
 
-The ``nimfix`` tool can be used to, without effort, automatically update your
-code and refactor it by performing these renamings.
-
 
 noSideEffect pragma
 -------------------
@@ -678,14 +675,15 @@ Example:
   {.push stackTrace:off.}
   proc embedsC() =
     var nimVar = 89
-    # use backticks to access Nim symbols within an emit section:
-    {.emit: """fprintf(stdout, "%d\n", cvariable + (int)`nimVar`);""".}
+    # access Nim symbols within an emit section outside of string literals:
+    {.emit: ["""fprintf(stdout, "%d\n", cvariable + (int)""", nimVar, ");"].}
   {.pop.}
 
   embedsC()
 
-As can be seen from the example, to Nim symbols can be referred via backticks.
-Use two backticks to produce a single verbatim backtick.
+For backwards compatibility, if the argument to the ``emit`` statement
+is a single string literal, Nim symbols can be referred to via backticks.
+This usage is however deprecated.
 
 For a toplevel emit statement the section where in the generated C/C++ file
 the code should be emitted can be influenced via the
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/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
diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim
index 71d901e69..e6b4196a3 100644
--- a/tests/async/tioselectors.nim
+++ b/tests/async/tioselectors.nim
@@ -12,11 +12,10 @@ template processTest(t, x: untyped) =
   if not x: echo(t & " FAILED\r\n")
 
 when not defined(windows):
-  import os, posix, osproc, nativesockets, times
+  import os, posix, nativesockets, times
 
-  const supportedPlatform = defined(macosx) or defined(freebsd) or
-                            defined(netbsd) or defined(openbsd) or
-                            defined(linux)
+  when ioselSupportedPlatform:
+    import osproc
 
   proc socket_notification_test(): bool =
     proc create_test_socket(): SocketHandle =
@@ -143,7 +142,7 @@ when not defined(windows):
     selector.close()
     result = true
 
-  when supportedPlatform:
+  when ioselSupportedPlatform:
     proc timer_notification_test(): bool =
       var selector = newSelector[int]()
       var timer = selector.registerTimer(100, false, 0)
@@ -462,7 +461,7 @@ when not defined(windows):
   when hasThreadSupport:
     processTest("Multithreaded user event notification test...",
                 mt_event_test())
-  when supportedPlatform:
+  when ioselSupportedPlatform:
     processTest("Timer notification test...", timer_notification_test())
     processTest("Process notification test...", process_notification_test())
     processTest("Signal notification test...", signal_notification_test())
diff --git a/tests/async/tupcoming_async.nim b/tests/async/tupcoming_async.nim
index 137794afd..7d255f213 100644
--- a/tests/async/tupcoming_async.nim
+++ b/tests/async/tupcoming_async.nim
@@ -8,11 +8,12 @@ OK
 """
 
 when defined(upcoming):
-  import asyncdispatch, times, osproc, streams
+  import asyncdispatch, times, streams, posix
+  from ioselectors import ioselSupportedPlatform
 
-  const supportedPlatform = defined(linux) or defined(freebsd) or
-                            defined(netbsd) or defined(openbsd) or
-                            defined(macosx)
+  proc delayedSet(ev: AsyncEvent, timeout: int): Future[void] {.async.} =
+    await sleepAsync(timeout)
+    ev.setEvent()
 
   proc waitEvent(ev: AsyncEvent, closeEvent = false): Future[void] =
     var retFuture = newFuture[void]("waitEvent")
@@ -25,56 +26,55 @@ when defined(upcoming):
     addEvent(ev, cb)
     return retFuture
 
-  proc waitTimer(timeout: int): Future[void] =
-    var retFuture = newFuture[void]("waitTimer")
-    proc cb(fd: AsyncFD): bool =
-      retFuture.complete()
-    addTimer(timeout, true, cb)
-    return retFuture
-
-  proc waitProcess(p: Process): Future[void] =
-    var retFuture = newFuture[void]("waitProcess")
-    proc cb(fd: AsyncFD): bool =
-      retFuture.complete()
-    addProcess(p.processID(), cb)
-    return retFuture
-
-  proc delayedSet(ev: AsyncEvent, timeout: int): Future[void] {.async.} =
-    await waitTimer(timeout)
-    ev.setEvent()
-
-  proc timerTest() =
-    waitFor(waitTimer(200))
-    echo "OK"
-
   proc eventTest() =
     var event = newAsyncEvent()
     var fut = waitEvent(event)
     asyncCheck(delayedSet(event, 500))
-    waitFor(fut or waitTimer(1000))
+    waitFor(fut or sleepAsync(1000))
     if fut.finished:
       echo "OK"
     else:
       echo "eventTest: Timeout expired before event received!"
 
-  proc processTest() =
-    when defined(windows):
-      var process = startProcess("ping.exe", "",
-                                 ["127.0.0.1", "-n", "2", "-w", "100"], nil,
-                                 {poStdErrToStdOut, poUsePath, poInteractive,
-                                 poDemon})
-    else:
-      var process = startProcess("/bin/sleep", "", ["1"], nil,
-                                 {poStdErrToStdOut, poUsePath})
-    var fut = waitProcess(process)
-    waitFor(fut or waitTimer(2000))
-    if fut.finished and process.peekExitCode() == 0:
+  when ioselSupportedPlatform or defined(windows):
+
+    import osproc
+
+    proc waitTimer(timeout: int): Future[void] =
+      var retFuture = newFuture[void]("waitTimer")
+      proc cb(fd: AsyncFD): bool =
+        retFuture.complete()
+      addTimer(timeout, true, cb)
+      return retFuture
+
+    proc waitProcess(p: Process): Future[void] =
+      var retFuture = newFuture[void]("waitProcess")
+      proc cb(fd: AsyncFD): bool =
+        retFuture.complete()
+      addProcess(p.processID(), cb)
+      return retFuture
+
+    proc timerTest() =
+      waitFor(waitTimer(200))
       echo "OK"
-    else:
-      echo "processTest: Timeout expired before process exited!"
 
-  when supportedPlatform:
-    import posix
+    proc processTest() =
+      when defined(windows):
+        var process = startProcess("ping.exe", "",
+                                   ["127.0.0.1", "-n", "2", "-w", "100"], nil,
+                                   {poStdErrToStdOut, poUsePath, poInteractive,
+                                   poDemon})
+      else:
+        var process = startProcess("/bin/sleep", "", ["1"], nil,
+                                   {poStdErrToStdOut, poUsePath})
+      var fut = waitProcess(process)
+      waitFor(fut or waitTimer(2000))
+      if fut.finished and process.peekExitCode() == 0:
+        echo "OK"
+      else:
+        echo "processTest: Timeout expired before process exited!"
+
+  when ioselSupportedPlatform:
 
     proc waitSignal(signal: int): Future[void] =
       var retFuture = newFuture[void]("waitSignal")
@@ -97,7 +97,7 @@ when defined(upcoming):
       else:
         echo "signalTest: Timeout expired before signal received!"
 
-  when supportedPlatform:
+  when ioselSupportedPlatform:
     timerTest()
     eventTest()
     processTest()
diff --git a/tests/cpp/temitlist.nim b/tests/cpp/temitlist.nim
new file mode 100644
index 000000000..cef0fc52d
--- /dev/null
+++ b/tests/cpp/temitlist.nim
@@ -0,0 +1,22 @@
+discard """
+  cmd: "nim cpp $file"
+  output: '''6.0'''
+"""
+
+# bug #4730
+
+type Vector* {.importcpp: "std::vector", header: "<vector>".}[T] = object
+
+template `[]=`*[T](v: var Vector[T], key: int, val: T) =
+  {.emit: [v, "[", key, "] = ", val, ";"].}
+
+proc setLen*[T](v: var Vector[T]; size: int) {.importcpp: "resize", nodecl.}
+proc `[]`*[T](v: var Vector[T], key: int): T {.importcpp: "(#[#])", nodecl.}
+
+proc main =
+  var v: Vector[float]
+  v.setLen 1
+  v[0] = 6.0
+  echo v[0]
+
+main()
diff --git a/web/news/e029_version_0_16_0.rst b/web/news/e029_version_0_16_0.rst
index 2a0c81b46..905504791 100644
--- a/web/news/e029_version_0_16_0.rst
+++ b/web/news/e029_version_0_16_0.rst
@@ -55,6 +55,28 @@ Compiler Additions
 Language Additions
 ------------------
 
+- The ``emit`` pragma now takes a list of Nim expressions instead
+  of a single string literal. This list can easily contain non-strings
+  like template parameters. This means ``emit`` works out of the
+  box with templates and no new quoting rules needed to be introduced.
+  The old way with backtick quoting is still supported but will be
+  deprecated.
+
+.. code-block:: nim
+  type Vector* {.importcpp: "std::vector", header: "<vector>".}[T] = object
+
+  template `[]=`*[T](v: var Vector[T], key: int, val: T) =
+    {.emit: [v, "[", key, "] = ", val, ";"].}
+
+  proc setLen*[T](v: var Vector[T]; size: int) {.importcpp: "resize", nodecl.}
+  proc `[]`*[T](v: var Vector[T], key: int): T {.importcpp: "(#[#])", nodecl.}
+
+  proc main =
+    var v: Vector[float]
+    v.setLen 1
+    v[0] = 6.0
+    echo v[0]
+
 
 Bugfixes
 --------