summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/core/macros.nim77
-rw-r--r--lib/packages/docutils/highlite.nim6
-rw-r--r--lib/packages/docutils/rst.nim4
-rw-r--r--lib/posix/epoll.nim2
-rw-r--r--lib/posix/inotify.nim2
-rw-r--r--lib/posix/linux.nim5
-rw-r--r--lib/posix/posix.nim2
-rw-r--r--lib/pure/asyncdispatch.nim20
-rw-r--r--lib/pure/asyncio.nim28
-rw-r--r--lib/pure/cgi.nim2
-rw-r--r--lib/pure/collections/tables.nim2
-rw-r--r--lib/pure/ftpclient.nim11
-rw-r--r--lib/pure/future.nim118
-rw-r--r--lib/pure/httpserver.nim2
-rw-r--r--lib/pure/irc.nim6
-rw-r--r--lib/pure/math.nim14
-rw-r--r--lib/pure/os.nim2
-rw-r--r--lib/pure/osproc.nim20
-rw-r--r--lib/pure/parsecfg.nim2
-rw-r--r--lib/pure/redis.nim468
-rw-r--r--lib/pure/scgi.nim9
-rw-r--r--lib/pure/sockets.nim4
-rw-r--r--lib/pure/streams.nim16
-rw-r--r--lib/pure/times.nim25
-rw-r--r--lib/system.nim138
-rw-r--r--lib/system/alloc.nim7
-rw-r--r--lib/system/assign.nim13
-rw-r--r--lib/system/avltree.nim4
-rw-r--r--lib/system/cgprocs.nim4
-rw-r--r--lib/system/channels.nim25
-rw-r--r--lib/system/excpt.nim12
-rw-r--r--lib/system/gc.nim21
-rw-r--r--lib/system/gc_ms.nim16
-rw-r--r--lib/system/hti.nim2
-rw-r--r--lib/system/inclrtl.nim8
-rw-r--r--lib/system/repr.nim8
-rw-r--r--lib/system/syslocks.nim12
-rw-r--r--lib/system/sysspawn.nim195
-rw-r--r--lib/system/sysstr.nim6
-rw-r--r--lib/system/threads.nim4
-rw-r--r--lib/windows/winlean.nim4
-rw-r--r--lib/wrappers/sqlite3.nim1
42 files changed, 926 insertions, 401 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 79bf34b7c..09dc341df 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -7,6 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
+include "system/inclrtl"
 
 ## This module contains the interface to the compiler's abstract syntax
 ## tree (`AST`:idx:). Macros operate on this tree.
@@ -109,19 +110,20 @@ const
   nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
                    nnkCallStrLit}
 
-proc `[]`*(n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}
+proc `[]`*(n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild", noSideEffect.}
   ## get `n`'s `i`'th child.
 
-proc `[]=`*(n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}
+proc `[]=`*(n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild",
+  noSideEffect.}
   ## set `n`'s `i`'th child to `child`.
 
-proc `!`*(s: string): TNimrodIdent {.magic: "StrToIdent".}
+proc `!`*(s: string): TNimrodIdent {.magic: "StrToIdent", noSideEffect.}
   ## constructs an identifier from the string `s`
 
-proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}
+proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr", noSideEffect.}
   ## converts a Nimrod identifier to a string
 
-proc `$`*(s: PNimrodSymbol): string {.magic: "IdentToStr".}
+proc `$`*(s: PNimrodSymbol): string {.magic: "IdentToStr", noSideEffect.}
   ## converts a Nimrod symbol to a string
 
 proc `==`*(a, b: TNimrodIdent): bool {.magic: "EqIdent", noSideEffect.}
@@ -130,35 +132,36 @@ proc `==`*(a, b: TNimrodIdent): bool {.magic: "EqIdent", noSideEffect.}
 proc `==`*(a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.}
   ## compares two Nimrod nodes
 
-proc len*(n: PNimrodNode): int {.magic: "NLen".}
+proc len*(n: PNimrodNode): int {.magic: "NLen", noSideEffect.}
   ## returns the number of children of `n`.
 
-proc add*(father, child: PNimrodNode): PNimrodNode {.magic: "NAdd", discardable.}
+proc add*(father, child: PNimrodNode): PNimrodNode {.magic: "NAdd", discardable,
+  noSideEffect.}
   ## Adds the `child` to the `father` node. Returns the
   ## father node so that calls can be nested.
 
 proc add*(father: PNimrodNode, children: varargs[PNimrodNode]): PNimrodNode {.
-  magic: "NAddMultiple", discardable.}
+  magic: "NAddMultiple", discardable, noSideEffect.}
   ## Adds each child of `children` to the `father` node.
   ## Returns the `father` node so that calls can be nested.
 
-proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}
+proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel", noSideEffect.}
   ## deletes `n` children of `father` starting at index `idx`.
 
-proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind".}
+proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind", noSideEffect.}
   ## returns the `kind` of the node `n`.
 
-proc intVal*(n: PNimrodNode): BiggestInt {.magic: "NIntVal".}
-proc floatVal*(n: PNimrodNode): BiggestFloat {.magic: "NFloatVal".}
-proc symbol*(n: PNimrodNode): PNimrodSymbol {.magic: "NSymbol".}
-proc ident*(n: PNimrodNode): TNimrodIdent {.magic: "NIdent".}
-proc typ*(n: PNimrodNode): typedesc {.magic: "NGetType".}
-proc strVal*(n: PNimrodNode): string  {.magic: "NStrVal".}
-
-proc `intVal=`*(n: PNimrodNode, val: BiggestInt) {.magic: "NSetIntVal".}
-proc `floatVal=`*(n: PNimrodNode, val: BiggestFloat) {.magic: "NSetFloatVal".}
-proc `symbol=`*(n: PNimrodNode, val: PNimrodSymbol) {.magic: "NSetSymbol".}
-proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent".}
+proc intVal*(n: PNimrodNode): BiggestInt {.magic: "NIntVal", noSideEffect.}
+proc floatVal*(n: PNimrodNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.}
+proc symbol*(n: PNimrodNode): PNimrodSymbol {.magic: "NSymbol", noSideEffect.}
+proc ident*(n: PNimrodNode): TNimrodIdent {.magic: "NIdent", noSideEffect.}
+proc typ*(n: PNimrodNode): typedesc {.magic: "NGetType", noSideEffect.}
+proc strVal*(n: PNimrodNode): string  {.magic: "NStrVal", noSideEffect.}
+
+proc `intVal=`*(n: PNimrodNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.}
+proc `floatVal=`*(n: PNimrodNode, val: BiggestFloat) {.magic: "NSetFloatVal", noSideEffect.}
+proc `symbol=`*(n: PNimrodNode, val: PNimrodSymbol) {.magic: "NSetSymbol", noSideEffect.}
+proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent", noSideEffect.}
 #proc `typ=`*(n: PNimrodNode, typ: typedesc) {.magic: "NSetType".}
 # this is not sound! Unfortunately forbidding 'typ=' is not enough, as you
 # can easily do:
@@ -166,24 +169,24 @@ proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent".}
 #   let fake = semCheck(2.0)
 #   bracket[0] = fake  # constructs a mixed array with ints and floats!
 
-proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}
+proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal", noSideEffect.}
 
 proc newNimNode*(kind: TNimrodNodeKind,
-                 n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
+                 n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode", noSideEffect.}
 
-proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".}
-proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".}
+proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode", noSideEffect.}
+proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree", noSideEffect.}
 
-proc error*(msg: string) {.magic: "NError".}
+proc error*(msg: string) {.magic: "NError", gcsafe.}
   ## writes an error message at compile time
 
-proc warning*(msg: string) {.magic: "NWarning".}
+proc warning*(msg: string) {.magic: "NWarning", gcsafe.}
   ## writes a warning message at compile time
 
-proc hint*(msg: string) {.magic: "NHint".}
+proc hint*(msg: string) {.magic: "NHint", gcsafe.}
   ## writes a hint message at compile time
 
-proc newStrLitNode*(s: string): PNimrodNode {.compileTime.} =
+proc newStrLitNode*(s: string): PNimrodNode {.compileTime, noSideEffect.} =
   ## creates a string literal node from `s`
   result = newNimNode(nnkStrLit)
   result.strVal = s
@@ -219,7 +222,7 @@ type
                          ## any other means in the language currently)
 
 proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {.
-              magic: "NBindSym".}
+              magic: "NBindSym", noSideEffect.}
   ## creates a node that binds `ident` to a symbol node. The bound symbol
   ## may be an overloaded symbol.
   ## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
@@ -230,11 +233,11 @@ proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {.
   ## returned even if the symbol is not ambiguous.
 
 proc genSym*(kind: TNimrodSymKind = nskLet; ident = ""): PNimrodNode {.
-  magic: "NGenSym".}
+  magic: "NGenSym", noSideEffect.}
   ## generates a fresh symbol that is guaranteed to be unique. The symbol
   ## needs to occur in a declaration context.
 
-proc callsite*(): PNimrodNode {.magic: "NCallSite".}
+proc callsite*(): PNimrodNode {.magic: "NCallSite", gcsafe.}
   ## returns the AST if the invokation expression that invoked this macro.
 
 proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
@@ -242,19 +245,19 @@ proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
   ## in a string literal node
   return newStrLitNode(repr(n))
 
-proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo".}
+proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo", noSideEffect.}
   ## returns the position the node appears in the original source file
   ## in the form filename(line, col)
 
-proc parseExpr*(s: string): PNimrodNode {.magic: "ParseExprToAst".}
+proc parseExpr*(s: string): PNimrodNode {.magic: "ParseExprToAst", noSideEffect.}
   ## Compiles the passed string to its AST representation.
   ## Expects a single expression.
 
-proc parseStmt*(s: string): PNimrodNode {.magic: "ParseStmtToAst".}
+proc parseStmt*(s: string): PNimrodNode {.magic: "ParseStmtToAst", noSideEffect.}
   ## Compiles the passed string to its AST representation.
   ## Expects one or more statements.
 
-proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst".}
+proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst", noSideEffect.}
   ## Obtains the AST nodes returned from a macro or template invocation.
   ## Example:
   ##
@@ -263,7 +266,7 @@ proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst".}
   ##   macro FooMacro() =
   ##     var ast = getAst(BarTemplate())
 
-proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst".}
+proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst", noSideEffect.}
   ## Quasi-quoting operator.
   ## Accepts an expression or a block and returns the AST that represents it.
   ## Within the quoted AST, you are able to interpolate PNimrodNode expressions
diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim
index c507f5e1c..80fbf3a51 100644
--- a/lib/packages/docutils/highlite.nim
+++ b/lib/packages/docutils/highlite.nim
@@ -52,7 +52,7 @@ const
     "finally", "for", "from", "generic", "if", "import", "in", "include",
     "interface", "is", "isnot", "iterator", "lambda", "let", "macro", "method",
     "mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc",
-    "ptr", "raise", "ref", "return", "shared", "shl", "shr", "static",
+    "ptr", "raise", "ref", "return", "shl", "shr", "static",
     "template", "try", "tuple", "type", "using", "var", "when", "while", "with",
     "without", "xor", "yield"]
 
@@ -61,6 +61,7 @@ proc getSourceLanguage*(name: string): TSourceLanguage =
     if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0: 
       return i
   result = langNone
+  
 proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: cstring) =
   g.buf = buf
   g.kind = low(TTokenClass)
@@ -70,6 +71,7 @@ proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: cstring) =
   var pos = 0                     # skip initial whitespace:
   while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
   g.pos = pos
+
 proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) = 
   initGeneralTokenizer(g, cstring(buf))
 
@@ -554,7 +556,7 @@ when isMainModule:
     let input = string(readFile(filename))
     keywords = input.split()
     break
-  doAssert (not keywords.isNil, "Couldn't read any keywords.txt file!")
+  doAssert(not keywords.isNil, "Couldn't read any keywords.txt file!")
   doAssert keywords.len == nimrodKeywords.len, "No matching lengths"
   for i in 0..keywords.len-1:
     #echo keywords[i], " == ", nimrodKeywords[i]
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index 30cc9026b..23459ade6 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -311,8 +311,8 @@ proc newSharedState(options: TRstParseOptions,
   result.subs = @[]
   result.refs = @[]
   result.options = options
-  result.msgHandler = if isNil(msgHandler): defaultMsgHandler else: msgHandler
-  result.findFile = if isNil(findFile): defaultFindFile else: findFile
+  result.msgHandler = if not isNil(msgHandler): msgHandler else: defaultMsgHandler
+  result.findFile = if not isNil(findFile): findFile else: defaultFindFile
   
 proc rstMessage(p: TRstParser, msgKind: TMsgKind, arg: string) = 
   p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line, 
diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim
index 57a2f001f..ee04348e8 100644
--- a/lib/posix/epoll.nim
+++ b/lib/posix/epoll.nim
@@ -7,6 +7,8 @@
 #    distribution, for details about the copyright.
 #
 
+{.deadCodeElim:on.}
+
 from posix import TSocketHandle
 
 const
diff --git a/lib/posix/inotify.nim b/lib/posix/inotify.nim
index 28dcd652f..852eb12fa 100644
--- a/lib/posix/inotify.nim
+++ b/lib/posix/inotify.nim
@@ -7,6 +7,8 @@
 #    distribution, for details about the copyright.
 #
 
+{.deadCodeElim:on.}
+
 # Get the platform-dependent flags.  
 # Structure describing an inotify event.  
 type 
diff --git a/lib/posix/linux.nim b/lib/posix/linux.nim
index 1ed1af3b6..be591e29a 100644
--- a/lib/posix/linux.nim
+++ b/lib/posix/linux.nim
@@ -1,3 +1,5 @@
+{.deadCodeElim:on.}
+
 import posix
 
 const
@@ -22,4 +24,5 @@ const
 
 # fn should be of type proc (a2: pointer): void {.cdecl.}
 proc clone*(fn: pointer; child_stack: pointer; flags: cint;
-            arg: pointer; ptid: ptr TPid; tls: pointer; ctid: ptr TPid): cint {.importc, header: "<sched.h>".}
+            arg: pointer; ptid: ptr TPid; tls: pointer; 
+            ctid: ptr TPid): cint {.importc, header: "<sched.h>".}
diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim
index 131f23fdd..e206447cc 100644
--- a/lib/posix/posix.nim
+++ b/lib/posix/posix.nim
@@ -27,6 +27,8 @@
 ## resulting C code will just ``#include <XYZ.h>`` and *not* define the
 ## symbols declared here.
 
+{.deadCodeElim:on.}
+
 from times import TTime
 
 const
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 5e638dc74..f50383038 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -7,6 +7,8 @@
 #    distribution, for details about the copyright.
 #
 
+include "system/inclrtl"
+
 import os, oids, tables, strutils, macros
 
 import rawsockets
@@ -31,7 +33,7 @@ export TPort
 
 type
   PFutureBase* = ref object of PObject
-    cb: proc () {.closure.}
+    cb: proc () {.closure,gcsafe.}
     finished: bool
 
   PFuture*[T] = ref object of PFutureBase
@@ -68,7 +70,7 @@ proc fail*[T](future: PFuture[T], error: ref EBase) =
   if future.cb != nil:
     future.cb()
 
-proc `callback=`*(future: PFutureBase, cb: proc () {.closure.}) =
+proc `callback=`*(future: PFutureBase, cb: proc () {.closure,gcsafe.}) =
   ## Sets the callback proc to be called when the future completes.
   ##
   ## If future has already completed then ``cb`` will be called immediately.
@@ -80,7 +82,7 @@ proc `callback=`*(future: PFutureBase, cb: proc () {.closure.}) =
     future.cb()
 
 proc `callback=`*[T](future: PFuture[T],
-    cb: proc (future: PFuture[T]) {.closure.}) =
+    cb: proc (future: PFuture[T]) {.closure,gcsafe.}) =
   ## Sets the callback proc to be called when the future completes.
   ##
   ## If future has already completed then ``cb`` will be called immediately.
@@ -122,7 +124,7 @@ when defined(windows) or defined(nimdoc):
     TCompletionData* = object
       sock: TAsyncFD
       cb: proc (sock: TAsyncFD, bytesTransferred: DWORD,
-                errcode: TOSErrorCode) {.closure.}
+                errcode: TOSErrorCode) {.closure,gcsafe.}
 
     PDispatcher* = ref object
       ioPort: THandle
@@ -237,7 +239,7 @@ when defined(windows) or defined(nimdoc):
     let func =
       cast[proc (s: TSocketHandle, name: ptr TSockAddr, namelen: cint, 
          lpSendBuffer: pointer, dwSendDataLength: dword,
-         lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall.}](connectExPtr)
+         lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall,gcsafe.}](connectExPtr)
 
     result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent,
          lpOverlapped)
@@ -251,7 +253,7 @@ when defined(windows) or defined(nimdoc):
       cast[proc (listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
                  dwReceiveDataLength, dwLocalAddressLength,
                  dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
-                 lpOverlapped: POverlapped): bool {.stdcall.}](acceptExPtr)
+                 lpOverlapped: POverlapped): bool {.stdcall,gcsafe.}](acceptExPtr)
     result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength,
         dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived,
         lpOverlapped)
@@ -268,7 +270,7 @@ when defined(windows) or defined(nimdoc):
                  dwReceiveDataLength, dwLocalAddressLength,
                  dwRemoteAddressLength: DWORD, LocalSockaddr: ptr ptr TSockAddr,
                  LocalSockaddrLength: lpint, RemoteSockaddr: ptr ptr TSockAddr,
-                RemoteSockaddrLength: lpint) {.stdcall.}](getAcceptExSockAddrsPtr)
+                RemoteSockaddrLength: lpint) {.stdcall,gcsafe.}](getAcceptExSockAddrsPtr)
     
     func(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength,
                   dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
@@ -537,7 +539,7 @@ else:
   from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK
   type
     TAsyncFD* = distinct cint
-    TCallback = proc (sock: TAsyncFD): bool {.closure.}
+    TCallback = proc (sock: TAsyncFD): bool {.closure,gcsafe.}
 
     PData* = ref object of PObject
       sock: TAsyncFD
@@ -757,7 +759,7 @@ proc accept*(socket: TAsyncFD): PFuture[TAsyncFD] =
 
 template createCb*(retFutureSym, iteratorNameSym: expr): stmt {.immediate.} =
   var nameIterVar = iteratorNameSym
-  proc cb {.closure.} =
+  proc cb {.closure,gcsafe.} =
     if not nameIterVar.finished:
       var next = nameIterVar()
       if next == nil:
diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim
index ab09dc860..c68ca4350 100644
--- a/lib/pure/asyncio.nim
+++ b/lib/pure/asyncio.nim
@@ -6,6 +6,8 @@
 #    distribution, for details about the copyright.
 #
 
+include "system/inclrtl"
+
 import sockets, os
 
 ## This module implements an asynchronous event loop together with asynchronous sockets
@@ -98,13 +100,13 @@ type
     fd*: TSocketHandle
     deleVal*: PObject
 
-    handleRead*: proc (h: PObject) {.nimcall.}
-    handleWrite*: proc (h: PObject) {.nimcall.}
-    handleError*: proc (h: PObject) {.nimcall.}
-    hasDataBuffered*: proc (h: PObject): bool {.nimcall.}
+    handleRead*: proc (h: PObject) {.nimcall, gcsafe.}
+    handleWrite*: proc (h: PObject) {.nimcall, gcsafe.}
+    handleError*: proc (h: PObject) {.nimcall, gcsafe.}
+    hasDataBuffered*: proc (h: PObject): bool {.nimcall, gcsafe.}
     
     open*: bool
-    task*: proc (h: PObject) {.nimcall.}
+    task*: proc (h: PObject) {.nimcall, gcsafe.}
     mode*: TFileMode
     
   PDelegate* = ref TDelegate
@@ -118,13 +120,13 @@ type
     socket: TSocket
     info: TInfo
 
-    handleRead*: proc (s: PAsyncSocket) {.closure.}
-    handleWrite: proc (s: PAsyncSocket) {.closure.}
-    handleConnect*: proc (s:  PAsyncSocket) {.closure.}
+    handleRead*: proc (s: PAsyncSocket) {.closure, gcsafe.}
+    handleWrite: proc (s: PAsyncSocket) {.closure, gcsafe.}
+    handleConnect*: proc (s:  PAsyncSocket) {.closure, gcsafe.}
 
-    handleAccept*: proc (s:  PAsyncSocket) {.closure.}
+    handleAccept*: proc (s:  PAsyncSocket) {.closure, gcsafe.}
 
-    handleTask*: proc (s: PAsyncSocket) {.closure.}
+    handleTask*: proc (s: PAsyncSocket) {.closure, gcsafe.}
 
     lineBuffer: TaintedString ## Temporary storage for ``readLine``
     sendBuffer: string ## Temporary storage for ``send``
@@ -213,7 +215,7 @@ proc asyncSockHandleRead(h: PObject) =
   else:
     PAsyncSocket(h).handleAccept(PAsyncSocket(h))
 
-proc close*(sock: PAsyncSocket)
+proc close*(sock: PAsyncSocket) {.gcsafe.}
 proc asyncSockHandleWrite(h: PObject) =
   when defined(ssl):
     if PAsyncSocket(h).socket.isSSL and not
@@ -254,7 +256,7 @@ proc asyncSockHandleWrite(h: PObject) =
         PAsyncSocket(h).deleg.mode = fmRead
 
 when defined(ssl):
-  proc asyncSockDoHandshake(h: PObject) =
+  proc asyncSockDoHandshake(h: PObject) {.gcsafe.} =
     if PAsyncSocket(h).socket.isSSL and not
          PAsyncSocket(h).socket.gotHandshake:
       if PAsyncSocket(h).sslNeedAccept:
@@ -437,7 +439,7 @@ proc isSendDataBuffered*(s: PAsyncSocket): bool =
   return s.sendBuffer.len != 0
 
 proc setHandleWrite*(s: PAsyncSocket,
-    handleWrite: proc (s: PAsyncSocket) {.closure.}) =
+    handleWrite: proc (s: PAsyncSocket) {.closure, gcsafe.}) =
   ## Setter for the ``handleWrite`` event.
   ##
   ## To remove this event you should use the ``delHandleWrite`` function.
diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim
index 4e2b6f5f8..31fb24eef 100644
--- a/lib/pure/cgi.nim
+++ b/lib/pure/cgi.nim
@@ -377,7 +377,7 @@ proc setCookie*(name, value: string) =
   write(stdout, "Set-Cookie: ", name, "=", value, "\n")
 
 var
-  gcookies: PStringTable = nil
+  gcookies {.threadvar.}: PStringTable
 
 proc getCookie*(name: string): TaintedString =
   ## Gets a cookie. If no cookie of `name` exists, "" is returned.
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index 412bebeee..cd28f9af0 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -196,7 +196,7 @@ proc `==`*[A, B](s, t: TTable[A, B]): bool =
     # to use the slow route here:
     for key, val in s:
       if not hasKey(t, key): return false
-      if mget(t, key) != val: return false
+      if t[key] != val: return false
     return true
   
 proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): TTable[C, B] =
diff --git a/lib/pure/ftpclient.nim b/lib/pure/ftpclient.nim
index 3bb55239b..53f6688b9 100644
--- a/lib/pure/ftpclient.nim
+++ b/lib/pure/ftpclient.nim
@@ -6,6 +6,8 @@
 #    distribution, for details about the copyright.
 #
 
+include "system/inclrtl"
+
 import sockets, strutils, parseutils, times, os, asyncio
 
 ## This module **partially** implements an FTP client as specified
@@ -41,7 +43,7 @@ type
       dummyA, dummyB: pointer # workaround a Nimrod API issue
       asyncCSock: PAsyncSocket
       asyncDSock: PAsyncSocket
-      handleEvent*: proc (ftp: PAsyncFTPClient, ev: TFTPEvent) {.closure.}
+      handleEvent*: proc (ftp: PAsyncFTPClient, ev: TFTPEvent){.closure,gcsafe.}
       disp: PDispatcher
       asyncDSockID: PDelegate
     user, pass: string
@@ -59,7 +61,7 @@ type
     JRetrText, JRetr, JStore
 
   TFTPJob = object
-    prc: proc (ftp: PFTPClient, async: bool): bool {.nimcall.}
+    prc: proc (ftp: PFTPClient, async: bool): bool {.nimcall, gcsafe.}
     case typ*: FTPJobType
     of JRetrText:
       lines: string
@@ -148,7 +150,8 @@ proc assertReply(received: TaintedString, expected: varargs[string]) =
                      [expected.join("' or '"), received.string])
 
 proc createJob(ftp: PFTPClient,
-               prc: proc (ftp: PFTPClient, async: bool): bool {.nimcall.},
+               prc: proc (ftp: PFTPClient, async: bool): bool {.
+                          nimcall,gcsafe.},
                cmd: FTPJobType) =
   if ftp.jobInProgress:
     raise newException(EFTP, "Unable to do two jobs at once.")
@@ -558,7 +561,7 @@ proc csockHandleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) =
 
 proc asyncFTPClient*(address: string, port = TPort(21),
                      user, pass = "",
-    handleEvent: proc (ftp: PAsyncFTPClient, ev: TFTPEvent) {.closure.} = 
+    handleEvent: proc (ftp: PAsyncFTPClient, ev: TFTPEvent) {.closure,gcsafe.} = 
       (proc (ftp: PAsyncFTPClient, ev: TFTPEvent) = discard)): PAsyncFTPClient =
   ## Create a ``PAsyncFTPClient`` object.
   ##
diff --git a/lib/pure/future.nim b/lib/pure/future.nim
new file mode 100644
index 000000000..73c20e708
--- /dev/null
+++ b/lib/pure/future.nim
@@ -0,0 +1,118 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2014 Dominik Picheta
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements experimental features which may soon be moved to
+## the system module (or other more appropriate modules).
+
+import macros
+
+proc createProcType(p, b: PNimrodNode): PNimrodNode {.compileTime.} =
+  #echo treeRepr(p)
+  #echo treeRepr(b)
+  result = newNimNode(nnkProcTy)
+  var formalParams = newNimNode(nnkFormalParams)
+
+  expectKind(b, nnkIdent)
+  formalParams.add b
+
+  case p.kind
+  of nnkPar:
+    for i in 0 .. <p.len:
+      let ident = p[i]
+      var identDefs = newNimNode(nnkIdentDefs)
+      case ident.kind
+      of nnkExprColonExpr:
+        identDefs.add ident[0]
+        identDefs.add ident[1]
+      of nnkIdent:
+        identDefs.add newIdentNode("i" & $i)
+        identDefs.add(ident)
+      else:
+        error("Incorrect type list in proc type declaration.")
+      identDefs.add newEmptyNode()
+      formalParams.add identDefs
+  of nnkIdent:
+    var identDefs = newNimNode(nnkIdentDefs)
+    identDefs.add newIdentNode("i0")
+    identDefs.add(p)
+    identDefs.add newEmptyNode()
+    formalParams.add identDefs
+  else:
+    error("Incorrect type list in proc type declaration.")
+  
+  result.add formalParams
+  result.add newEmptyNode()
+  #echo(treeRepr(result))
+  #echo(result.toStrLit())
+
+macro `=>`*(p, b: expr): expr {.immediate.} =
+  ## Syntax sugar for anonymous procedures.
+  ##
+  ## .. code-block:: nimrod
+  ##
+  ##   proc passTwoAndTwo(f: (int, int) -> int): int =
+  ##     f(2, 2)
+  ##
+  ##   passTwoAndTwo((x, y) => x + y) # 4
+  
+  #echo treeRepr(p)
+  #echo(treeRepr(b))
+  var params: seq[PNimrodNode] = @[newIdentNode("auto")]
+
+  case p.kind
+  of nnkPar:
+    for c in children(p):
+      var identDefs = newNimNode(nnkIdentDefs)
+      case c.kind
+      of nnkExprColonExpr:
+        identDefs.add(c[0])
+        identDefs.add(c[1])
+        identDefs.add(newEmptyNode())
+      of nnkIdent:
+        identDefs.add(c)
+        identDefs.add(newEmptyNode())
+        identDefs.add(newEmptyNode())
+      else:
+        error("Incorrect procedure parameter list.")
+      params.add(identDefs)
+  of nnkIdent:
+    var identDefs = newNimNode(nnkIdentDefs)
+    identDefs.add(p)
+    identDefs.add(newEmptyNode())
+    identDefs.add(newEmptyNode())
+    params.add(identDefs)
+  of nnkInfix:
+    if p[0].kind == nnkIdent and p[0].ident == !"->":
+      var procTy = createProcType(p[1], p[2])
+      params[0] = procTy[0][0]
+      for i in 1 .. <procTy[0].len:
+        params.add(procTy[0][i])
+    else:
+      error("Expected proc type (->) got (" & $p[0].ident & ").")
+  else:
+    error("Incorrect procedure parameter list.")
+  result = newProc(params = params, body = b, procType = nnkLambda)
+  #echo(result.treeRepr)
+  #echo(result.toStrLit())
+  #return result # TODO: Bug?
+
+macro `->`*(p, b: expr): expr {.immediate.} =
+  ## Syntax sugar for procedure types.
+  ##
+  ## .. code-block:: nimrod
+  ##
+  ##   proc pass2(f: (float, float) -> float): float =
+  ##     f(2, 2)
+  ##   
+  ##   # is the same as:
+  ##
+  ##   proc pass2(f: proc (x, y: float): float): float =
+  ##     f(2, 2)
+
+  createProcType(p, b)
diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim
index 901fdc779..8de708c5d 100644
--- a/lib/pure/httpserver.nim
+++ b/lib/pure/httpserver.nim
@@ -477,7 +477,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
     s.path = data.substr(i, last-1)
 
 proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: TSocket, 
-                        path, query: string): bool {.closure.},
+                        path, query: string): bool {.closure, gcsafe.},
                      port = TPort(80), address = "",
                      reuseAddr = false): PAsyncHTTPServer =
   ## Creates an Asynchronous HTTP server at ``port``.
diff --git a/lib/pure/irc.nim b/lib/pure/irc.nim
index 31a673210..49d9a9a34 100644
--- a/lib/pure/irc.nim
+++ b/lib/pure/irc.nim
@@ -32,6 +32,8 @@
 ## **Warning:** The API of this module is unstable, and therefore is subject
 ## to change.
 
+include "system/inclrtl"
+
 import sockets, strutils, parseutils, times, asyncio, os
 
 type
@@ -41,7 +43,7 @@ type
     nick, user, realname, serverPass: string
     case isAsync: bool
     of true:
-      handleEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure.}
+      handleEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure, gcsafe.}
       asyncSock: PAsyncSocket
       myDispatcher: PDispatcher
     of false:
@@ -445,7 +447,7 @@ proc asyncIRC*(address: string, port: TPort = 6667.TPort,
               realname = "NimrodBot", serverPass = "",
               joinChans: seq[string] = @[],
               msgLimit: bool = true,
-              ircEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure.}
+              ircEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure,gcsafe.}
               ): PAsyncIRC =
   ## Use this function if you want to use asyncio's dispatcher.
   ## 
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index 3997b059f..e4aecd272 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -12,6 +12,8 @@
 ## Basic math routines for Nimrod.
 ## This module is available for the JavaScript target.
 
+include "system/inclrtl"
+
 {.push debugger:off .} # the user does not want to trace a part
                        # of the standard library!
 
@@ -127,25 +129,25 @@ proc variance*(x: openArray[float]): float {.noSideEffect.} =
     result = result + diff*diff
   result = result / toFloat(len(x))
 
-proc random*(max: int): int
+proc random*(max: int): int {.gcsafe.}
   ## returns a random number in the range 0..max-1. The sequence of
   ## random number is always the same, unless `randomize` is called
   ## which initializes the random number generator with a "random"
   ## number, i.e. a tickcount.
 
 when not defined(windows):
-  proc random*(max: float): float
+  proc random*(max: float): float {.gcsafe.}
     ## returns a random number in the range 0..<max. The sequence of
     ## random number is always the same, unless `randomize` is called
     ## which initializes the random number generator with a "random"
     ## number, i.e. a tickcount. This is currently not supported for windows.
 
-proc randomize*()
+proc randomize*() {.gcsafe.}
   ## initializes the random number generator with a "random"
   ## number, i.e. a tickcount. Note: Does nothing for the JavaScript target,
   ## as JavaScript does not support this.
   
-proc randomize*(seed: int)
+proc randomize*(seed: int) {.gcsafe.}
   ## initializes the random number generator with a specific seed.
   ## Note: Does nothing for the JavaScript target,
   ## as JavaScript does not support this.
@@ -227,8 +229,8 @@ else:
     result = int(floor(mathrandom() * float(max)))
   proc random(max: float): float =
     result = float(mathrandom() * float(max))
-  proc randomize() = nil
-  proc randomize(seed: int) = nil
+  proc randomize() = discard
+  proc randomize(seed: int) = discard
   
   proc sqrt*(x: float): float {.importc: "Math.sqrt", nodecl.}
   proc ln*(x: float): float {.importc: "Math.log", nodecl.}
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 30a5ae949..00a33db75 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -1567,7 +1567,7 @@ when defined(windows):
   # ourselves. This has the additional benefit that the program's behaviour
   # is always the same -- independent of the used C compiler.
   var
-    ownArgv: seq[string]
+    ownArgv {.threadvar.}: seq[string]
 
   proc paramCount*(): int {.rtl, extern: "nos$1", tags: [FReadIO].} =
     ## Returns the number of `command line arguments`:idx: given to the
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 5d6848565..6e250f9d5 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -137,6 +137,14 @@ proc startProcess*(command: string,
   ## to `startProcess`. See the documentation of ``TProcessOption`` for the
   ## meaning of these flags. You need to `close` the process when done.
   ##
+  ## Note that you can't pass any `args` if you use the option
+  ## ``poEvalCommand``, which invokes the system shell to run the specified
+  ## `command`. In this situation you have to concatenate manually the contents
+  ## of `args` to `command` carefully escaping/quoting any special characters,
+  ## since it will be passed *as is* to the system shell. Each system/shell may
+  ## feature different escaping rules, so try to avoid this kind of shell
+  ## invokation if possible as it leads to non portable software.
+  ##
   ## Return value: The newly created process object. Nil is never returned,
   ## but ``EOS`` is raised in case of an error.
 
@@ -607,11 +615,13 @@ elif not defined(useNimRtl):
     optionPoStdErrToStdOut: bool
 
   when not defined(useFork):
-    proc startProcessAuxSpawn(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].}
-  proc startProcessAuxFork(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].}
+    proc startProcessAuxSpawn(data: TStartProcessData): TPid {.
+      tags: [FExecIO, FReadEnv], gcsafe.}
+  proc startProcessAuxFork(data: TStartProcessData): TPid {.
+    tags: [FExecIO, FReadEnv], gcsafe.}
   {.push stacktrace: off, profiler: off.}
   proc startProcessAfterFork(data: ptr TStartProcessData) {.
-    tags: [FExecIO, FReadEnv], cdecl.}
+    tags: [FExecIO, FReadEnv], cdecl, gcsafe.}
   {.pop.}
 
   proc startProcess(command: string,
@@ -633,7 +643,7 @@ elif not defined(useNimRtl):
     if poEvalCommand in options:
       sysCommand = "/bin/sh"
       sysArgsRaw = @[sysCommand, "-c", command]
-      assert args.len == 0
+      assert args.len == 0, "`args` has to be empty when using poEvalCommand."
     else:
       sysCommand = command
       sysArgsRaw = @[command]
@@ -938,7 +948,7 @@ elif not defined(useNimRtl):
 proc execCmdEx*(command: string, options: set[TProcessOption] = {
                 poStdErrToStdOut, poUsePath}): tuple[
                 output: TaintedString,
-                exitCode: int] {.tags: [FExecIO, FReadIO].} =
+                exitCode: int] {.tags: [FExecIO, FReadIO], gcsafe.} =
   ## a convenience proc that runs the `command`, grabs all its output and
   ## exit code and returns both.
   var p = startCmd(command, options)
diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim
index f3249b107..727a8efd8 100644
--- a/lib/pure/parsecfg.nim
+++ b/lib/pure/parsecfg.nim
@@ -70,7 +70,7 @@ const
   SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.',
                         '/', '\\'} 
   
-proc rawGetTok(c: var TCfgParser, tok: var TToken)
+proc rawGetTok(c: var TCfgParser, tok: var TToken) {.gcsafe.}
 
 proc open*(c: var TCfgParser, input: PStream, filename: string, 
            lineOffset = 0) {.
diff --git a/lib/pure/redis.nim b/lib/pure/redis.nim
index f4c45b99c..959f5c6ef 100644
--- a/lib/pure/redis.nim
+++ b/lib/pure/redis.nim
@@ -19,10 +19,21 @@ import sockets, os, strutils, parseutils
 const
   redisNil* = "\0\0"
 
+type 
+  PPipeline = ref object
+    enabled: bool
+    buffer: string
+    expected: int ## number of replies expected if pipelined
+
+type
+  TSendMode = enum
+    normal, pipelined, multiple
+
 type
   TRedis* {.pure, final.} = object
     socket: TSocket
     connected: bool
+    pipeline: PPipeline
   
   TRedisStatus* = string
   TRedisInteger* = biggestInt
@@ -32,25 +43,42 @@ type
   EInvalidReply* = object of ESynch ## Invalid reply from redis
   ERedis* = object of ESynch        ## Error in redis
 
+proc newPipeline(): PPipeline =
+  new(result)
+  result.buffer = ""
+  result.enabled = false
+  result.expected = 0
+
 proc open*(host = "localhost", port = 6379.TPort): TRedis =
   ## Opens a connection to the redis server.
   result.socket = socket(buffered = false)
   if result.socket == InvalidSocket:
     OSError(OSLastError())
   result.socket.connect(host, port)
+  result.pipeline = newPipeline()  
 
 proc raiseInvalidReply(expected, got: char) =
   raise newException(EInvalidReply, 
           "Expected '$1' at the beginning of a status reply got '$2'" %
           [$expected, $got])
 
-proc raiseNoOK(status: string) =
-  if status != "QUEUED" and status != "OK":
+proc raiseNoOK(status: string, pipelineEnabled:bool) =
+  if pipelineEnabled and not (status == "QUEUED" or status == "PIPELINED"):
+    raise newException(EInvalidReply, "Expected \"QUEUED\" or \"PIPELINED\" got \"$1\"" % status)
+  elif not pipelineEnabled and status != "OK":
     raise newException(EInvalidReply, "Expected \"OK\" got \"$1\"" % status)
 
-proc parseStatus(r: TRedis): TRedisStatus =
-  var line = ""
-  r.socket.readLine(line)
+template readSocket(r: TRedis, dummyVal:expr): stmt =
+  var line {.inject.} :TaintedString = ""
+  if r.pipeline.enabled:
+    return dummyVal
+  else:
+    readLine(r.socket, line)
+
+proc parseStatus(r: TRedis, line: string = ""): TRedisStatus =
+  if r.pipeline.enabled:
+    return "PIPELINED"
+
   if line == "":
     raise newException(ERedis, "Server closed connection prematurely")
 
@@ -60,13 +88,16 @@ proc parseStatus(r: TRedis): TRedisStatus =
     raiseInvalidReply('+', line[0])
   
   return line.substr(1) # Strip '+'
-  
-proc parseInteger(r: TRedis): TRedisInteger =
-  var line = ""
-  r.socket.readLine(line)
 
-  if line == "+QUEUED":  # inside of multi
-    return -1
+proc readStatus(r:TRedis): TRedisStatus =
+  r.readSocket("PIPELINED")
+  return r.parseStatus(line)
+ 
+proc parseInteger(r: TRedis, line: string = ""): TRedisInteger =
+  if r.pipeline.enabled: return -1
+  
+  #if line == "+QUEUED":  # inside of multi
+  #  return -1
 
   if line == "":
     raise newException(ERedis, "Server closed connection prematurely")
@@ -80,12 +111,18 @@ proc parseInteger(r: TRedis): TRedisInteger =
   if parseBiggestInt(line, result, 1) == 0:
     raise newException(EInvalidReply, "Unable to parse integer.") 
 
+proc readInteger(r: TRedis): TRedisInteger =
+  r.readSocket(-1)
+  return r.parseInteger(line)
+
 proc recv(sock: TSocket, size: int): TaintedString =
   result = newString(size).TaintedString
   if sock.recv(cstring(result), size) != size:
     raise newException(EInvalidReply, "recv failed")
 
-proc parseSingle(r: TRedis, line:string, allowMBNil = False): TRedisString =
+proc parseSingleString(r: TRedis, line:string, allowMBNil = False): TRedisString =
+  if r.pipeline.enabled: return ""
+  
   # Error.
   if line[0] == '-':
     raise newException(ERedis, strip(line))
@@ -95,9 +132,6 @@ proc parseSingle(r: TRedis, line:string, allowMBNil = False): TRedisString =
     if line == "*-1":
        return RedisNil
   
-  if line == "+QUEUED" or line == "+OK" : # inside of a transaction (multi)
-    return nil
-
   if line[0] != '$':
     raiseInvalidReply('$', line[0])
   
@@ -108,41 +142,84 @@ proc parseSingle(r: TRedis, line:string, allowMBNil = False): TRedisString =
   var s = r.socket.recv(numBytes+2)
   result = strip(s.string)
 
-proc parseMultiLines(r: TRedis, countLine:string): TRedisList =
+proc readSingleString(r: TRedis): TRedisString =
+  r.readSocket("")
+  return r.parseSingleString(line)
+
+proc readNext(r: TRedis): TRedisList
+
+proc parseArrayLines(r: TRedis, countLine:string): TRedisList =
   if countLine.string[0] != '*':
     raiseInvalidReply('*', countLine.string[0])
 
   var numElems = parseInt(countLine.string.substr(1))
   if numElems == -1: return nil
   result = @[]
+
   for i in 1..numElems:
-    var line = ""
-    r.socket.readLine(line.TaintedString)
-    if line[0] == '*':  # after exec() may contain more multi-bulk replies
-      var parsed = r.parseMultiLines(line)
+    var parsed = r.readNext()
+    if not isNil(parsed):
       for item in parsed:
         result.add(item)
-    else:
-     result.add(r.parseSingle(line))
-
-proc parseBulk(r: TRedis, allowMBNil = False): TRedisString =
-  var line = ""
-  r.socket.readLine(line.TaintedString)
 
-  if line == "+QUEUED" or line == "+OK": # inside of a transaction (multi)
-    return nil
-
-  return r.parseSingle(line, allowMBNil)
+proc readArrayLines(r: TRedis): TRedisList =
+  r.readSocket(nil)
+  return r.parseArrayLines(line)  
+
+proc parseBulkString(r: TRedis, allowMBNil = False, line:string = ""): TRedisString =
+  if r.pipeline.enabled: return ""
+
+  return r.parseSingleString(line, allowMBNil)
+
+proc readBulkString(r: TRedis, allowMBNil = false): TRedisString =
+  r.readSocket("")
+  return r.parseBulkString(allowMBNil, line)
+
+proc readArray(r: TRedis): TRedisList =
+  r.readSocket(@[])
+  return r.parseArrayLines(line)
+
+proc readNext(r: TRedis): TRedisList =
+  r.readSocket(@[])
+
+  var res = case line[0]
+    of '+', '-': @[r.parseStatus(line)]
+    of ':': @[$(r.parseInteger(line))]
+    of '$': @[r.parseBulkString(true,line)]
+    of '*': r.parseArrayLines(line)
+    else: 
+      raise newException(EInvalidReply, "readNext failed on line: " & line)
+      nil
+  r.pipeline.expected -= 1
+  return res
+
+proc flushPipeline*(r: TRedis, wasMulti = false): TRedisList =
+  ## Send buffered commands, clear buffer, return results
+  if r.pipeline.buffer.len > 0:
+    r.socket.send(r.pipeline.buffer)
+  r.pipeline.buffer = ""
+  
+  r.pipeline.enabled = false
+  result = @[]
+  
+  var tot = r.pipeline.expected
 
-proc parseMultiBulk(r: TRedis): TRedisList =
-  var line = TaintedString""
-  r.socket.readLine(line)
+  for i in 0..tot-1:
+    var ret = r.readNext()
+    for item in ret:
+     if not (item.contains("OK") or item.contains("QUEUED")):
+       result.add(item)
 
-  if line == "+QUEUED": # inside of a transaction (multi)
-    return nil
-    
-  return r.parseMultiLines(line)
+  r.pipeline.expected = 0
 
+proc startPipelining*(r: TRedis) =
+  ## Enable command pipelining (reduces network roundtrips).
+  ## Note that when enabled, you must call flushPipeline to actually send commands, except
+  ## for multi/exec() which enable and flush the pipeline automatically.
+  ## Commands return immediately with dummy values; actual results returned from
+  ## flushPipeline() or exec()
+  r.pipeline.expected = 0
+  r.pipeline.enabled = true
 
 proc sendCommand(r: TRedis, cmd: string, args: varargs[string]) =
   var request = "*" & $(1 + args.len()) & "\c\L"
@@ -151,7 +228,12 @@ proc sendCommand(r: TRedis, cmd: string, args: varargs[string]) =
   for i in items(args):
     request.add("$" & $i.len() & "\c\L")
     request.add(i & "\c\L")
-  r.socket.send(request)
+  
+  if r.pipeline.enabled:
+    r.pipeline.buffer.add(request)
+    r.pipeline.expected += 1
+  else:
+    r.socket.send(request)
 
 proc sendCommand(r: TRedis, cmd: string, arg1: string,
                  args: varargs[string]) =
@@ -163,75 +245,80 @@ proc sendCommand(r: TRedis, cmd: string, arg1: string,
   for i in items(args):
     request.add("$" & $i.len() & "\c\L")
     request.add(i & "\c\L")
-  r.socket.send(request)
+    
+  if r.pipeline.enabled:
+    r.pipeline.expected += 1
+    r.pipeline.buffer.add(request)
+  else:
+    r.socket.send(request)
 
 # Keys
 
 proc del*(r: TRedis, keys: varargs[string]): TRedisInteger =
   ## Delete a key or multiple keys
   r.sendCommand("DEL", keys)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc exists*(r: TRedis, key: string): bool =
   ## Determine if a key exists
   r.sendCommand("EXISTS", key)
-  return r.parseInteger() == 1
+  return r.readInteger() == 1
 
 proc expire*(r: TRedis, key: string, seconds: int): bool =
   ## Set a key's time to live in seconds. Returns `false` if the key could
   ## not be found or the timeout could not be set.
   r.sendCommand("EXPIRE", key, $seconds)
-  return r.parseInteger() == 1
+  return r.readInteger() == 1
 
 proc expireAt*(r: TRedis, key: string, timestamp: int): bool =
   ## Set the expiration for a key as a UNIX timestamp. Returns `false` 
   ## if the key could not be found or the timeout could not be set.
   r.sendCommand("EXPIREAT", key, $timestamp)
-  return r.parseInteger() == 1
+  return r.readInteger() == 1
 
 proc keys*(r: TRedis, pattern: string): TRedisList =
   ## Find all keys matching the given pattern
   r.sendCommand("KEYS", pattern)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc move*(r: TRedis, key: string, db: int): bool =
   ## Move a key to another database. Returns `true` on a successful move.
   r.sendCommand("MOVE", key, $db)
-  return r.parseInteger() == 1
+  return r.readInteger() == 1
 
 proc persist*(r: TRedis, key: string): bool =
   ## Remove the expiration from a key. 
   ## Returns `true` when the timeout was removed.
   r.sendCommand("PERSIST", key)
-  return r.parseInteger() == 1
+  return r.readInteger() == 1
   
 proc randomKey*(r: TRedis): TRedisString =
   ## Return a random key from the keyspace
   r.sendCommand("RANDOMKEY")
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc rename*(r: TRedis, key, newkey: string): TRedisStatus =
   ## Rename a key.
   ## 
   ## **WARNING:** Overwrites `newkey` if it exists!
   r.sendCommand("RENAME", key, newkey)
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
   
 proc renameNX*(r: TRedis, key, newkey: string): bool =
   ## Same as ``rename`` but doesn't continue if `newkey` exists.
   ## Returns `true` if key was renamed.
   r.sendCommand("RENAMENX", key, newkey)
-  return r.parseInteger() == 1
+  return r.readInteger() == 1
 
 proc ttl*(r: TRedis, key: string): TRedisInteger =
   ## Get the time to live for a key
   r.sendCommand("TTL", key)
-  return r.parseInteger()
+  return r.readInteger()
   
 proc keyType*(r: TRedis, key: string): TRedisStatus =
   ## Determine the type stored at key
   r.sendCommand("TYPE", key)
-  return r.parseStatus()
+  return r.readStatus()
   
 
 # Strings
@@ -239,125 +326,125 @@ proc keyType*(r: TRedis, key: string): TRedisStatus =
 proc append*(r: TRedis, key, value: string): TRedisInteger =
   ## Append a value to a key
   r.sendCommand("APPEND", key, value)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc decr*(r: TRedis, key: string): TRedisInteger =
   ## Decrement the integer value of a key by one
   r.sendCommand("DECR", key)
-  return r.parseInteger()
+  return r.readInteger()
   
 proc decrBy*(r: TRedis, key: string, decrement: int): TRedisInteger =
   ## Decrement the integer value of a key by the given number
   r.sendCommand("DECRBY", key, $decrement)
-  return r.parseInteger()
+  return r.readInteger()
   
 proc get*(r: TRedis, key: string): TRedisString =
   ## Get the value of a key. Returns `redisNil` when `key` doesn't exist.
   r.sendCommand("GET", key)
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc getBit*(r: TRedis, key: string, offset: int): TRedisInteger =
   ## Returns the bit value at offset in the string value stored at key
   r.sendCommand("GETBIT", key, $offset)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc getRange*(r: TRedis, key: string, start, stop: int): TRedisString =
   ## Get a substring of the string stored at a key
   r.sendCommand("GETRANGE", key, $start, $stop)
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc getSet*(r: TRedis, key: string, value: string): TRedisString =
   ## Set the string value of a key and return its old value. Returns `redisNil`
   ## when key doesn't exist.
   r.sendCommand("GETSET", key, value)
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc incr*(r: TRedis, key: string): TRedisInteger =
   ## Increment the integer value of a key by one.
   r.sendCommand("INCR", key)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc incrBy*(r: TRedis, key: string, increment: int): TRedisInteger =
   ## Increment the integer value of a key by the given number
   r.sendCommand("INCRBY", key, $increment)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc setk*(r: TRedis, key, value: string) = 
   ## Set the string value of a key.
   ##
   ## NOTE: This function had to be renamed due to a clash with the `set` type.
   r.sendCommand("SET", key, value)
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc setNX*(r: TRedis, key, value: string): bool =
   ## Set the value of a key, only if the key does not exist. Returns `true`
   ## if the key was set.
   r.sendCommand("SETNX", key, value)
-  return r.parseInteger() == 1
+  return r.readInteger() == 1
 
 proc setBit*(r: TRedis, key: string, offset: int, 
              value: string): TRedisInteger =
   ## Sets or clears the bit at offset in the string value stored at key
   r.sendCommand("SETBIT", key, $offset, value)
-  return r.parseInteger()
+  return r.readInteger()
   
 proc setEx*(r: TRedis, key: string, seconds: int, value: string): TRedisStatus =
   ## Set the value and expiration of a key
   r.sendCommand("SETEX", key, $seconds, value)
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc setRange*(r: TRedis, key: string, offset: int, 
                value: string): TRedisInteger =
   ## Overwrite part of a string at key starting at the specified offset
   r.sendCommand("SETRANGE", key, $offset, value)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc strlen*(r: TRedis, key: string): TRedisInteger =
   ## Get the length of the value stored in a key. Returns 0 when key doesn't
   ## exist.
   r.sendCommand("STRLEN", key)
-  return r.parseInteger()
+  return r.readInteger()
 
 # Hashes
 proc hDel*(r: TRedis, key, field: string): bool =
   ## Delete a hash field at `key`. Returns `true` if the field was removed.
   r.sendCommand("HDEL", key, field)
-  return r.parseInteger() == 1
+  return r.readInteger() == 1
 
 proc hExists*(r: TRedis, key, field: string): bool =
   ## Determine if a hash field exists.
   r.sendCommand("HEXISTS", key, field)
-  return r.parseInteger() == 1
+  return r.readInteger() == 1
 
 proc hGet*(r: TRedis, key, field: string): TRedisString =
   ## Get the value of a hash field
   r.sendCommand("HGET", key, field)
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc hGetAll*(r: TRedis, key: string): TRedisList =
   ## Get all the fields and values in a hash
   r.sendCommand("HGETALL", key)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc hIncrBy*(r: TRedis, key, field: string, incr: int): TRedisInteger =
   ## Increment the integer value of a hash field by the given number
   r.sendCommand("HINCRBY", key, field, $incr)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc hKeys*(r: TRedis, key: string): TRedisList =
   ## Get all the fields in a hash
   r.sendCommand("HKEYS", key)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc hLen*(r: TRedis, key: string): TRedisInteger =
   ## Get the number of fields in a hash
   r.sendCommand("HLEN", key)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc hMGet*(r: TRedis, key: string, fields: varargs[string]): TRedisList =
   ## Get the values of all the given hash fields
   r.sendCommand("HMGET", key, fields)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc hMSet*(r: TRedis, key: string, 
             fieldValues: openarray[tuple[field, value: string]]) =
@@ -367,22 +454,22 @@ proc hMSet*(r: TRedis, key: string,
     args.add(field)
     args.add(value)
   r.sendCommand("HMSET", args)
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc hSet*(r: TRedis, key, field, value: string): TRedisInteger =
   ## Set the string value of a hash field
   r.sendCommand("HSET", key, field, value)
-  return r.parseInteger()
+  return r.readInteger()
   
 proc hSetNX*(r: TRedis, key, field, value: string): TRedisInteger =
   ## Set the value of a hash field, only if the field does **not** exist
   r.sendCommand("HSETNX", key, field, value)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc hVals*(r: TRedis, key: string): TRedisList =
   ## Get all the values in a hash
   r.sendCommand("HVALS", key)
-  return r.parseMultiBulk()
+  return r.readArray()
   
 # Lists
 
@@ -393,7 +480,7 @@ proc bLPop*(r: TRedis, keys: varargs[string], timeout: int): TRedisList =
   for i in items(keys): args.add(i)
   args.add($timeout)
   r.sendCommand("BLPOP", args)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc bRPop*(r: TRedis, keys: varargs[string], timeout: int): TRedisList =
   ## Remove and get the *last* element in a list, or block until one 
@@ -402,7 +489,7 @@ proc bRPop*(r: TRedis, keys: varargs[string], timeout: int): TRedisList =
   for i in items(keys): args.add(i)
   args.add($timeout)
   r.sendCommand("BRPOP", args)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc bRPopLPush*(r: TRedis, source, destination: string,
                  timeout: int): TRedisString =
@@ -411,29 +498,29 @@ proc bRPopLPush*(r: TRedis, source, destination: string,
   ##
   ## http://redis.io/commands/brpoplpush
   r.sendCommand("BRPOPLPUSH", source, destination, $timeout)
-  return r.parseBulk(true) # Multi-Bulk nil allowed.
+  return r.readBulkString(true) # Multi-Bulk nil allowed.
 
 proc lIndex*(r: TRedis, key: string, index: int): TRedisString =
   ## Get an element from a list by its index
   r.sendCommand("LINDEX", key, $index)
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc lInsert*(r: TRedis, key: string, before: bool, pivot, value: string):
               TRedisInteger =
   ## Insert an element before or after another element in a list
   var pos = if before: "BEFORE" else: "AFTER"
   r.sendCommand("LINSERT", key, pos, pivot, value)
-  return r.parseInteger()
+  return r.readInteger()
   
 proc lLen*(r: TRedis, key: string): TRedisInteger =
   ## Get the length of a list
   r.sendCommand("LLEN", key)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc lPop*(r: TRedis, key: string): TRedisString =
   ## Remove and get the first element in a list
   r.sendCommand("LPOP", key)
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc lPush*(r: TRedis, key, value: string, create: bool = True): TRedisInteger =
   ## Prepend a value to a list. Returns the length of the list after the push.
@@ -444,39 +531,39 @@ proc lPush*(r: TRedis, key, value: string, create: bool = True): TRedisInteger =
     r.sendCommand("LPUSH", key, value)
   else:
     r.sendCommand("LPUSHX", key, value)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc lRange*(r: TRedis, key: string, start, stop: int): TRedisList =
   ## Get a range of elements from a list. Returns `nil` when `key` 
   ## doesn't exist.
   r.sendCommand("LRANGE", key, $start, $stop)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc lRem*(r: TRedis, key: string, value: string, count: int = 0): TRedisInteger =
   ## Remove elements from a list. Returns the number of elements that have been
   ## removed.
   r.sendCommand("LREM", key, $count, value)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc lSet*(r: TRedis, key: string, index: int, value: string) =
   ## Set the value of an element in a list by its index
   r.sendCommand("LSET", key, $index, value)
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
-proc lTrim*(r: TRedis, key: string, start, stop: int) =
+proc lTrim*(r: TRedis, key: string, start, stop: int)  =
   ## Trim a list to the specified range
   r.sendCommand("LTRIM", key, $start, $stop)
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc rPop*(r: TRedis, key: string): TRedisString =
   ## Remove and get the last element in a list
   r.sendCommand("RPOP", key)
-  return r.parseBulk()
+  return r.readBulkString()
   
 proc rPopLPush*(r: TRedis, source, destination: string): TRedisString =
   ## Remove the last element in a list, append it to another list and return it
   r.sendCommand("RPOPLPUSH", source, destination)
-  return r.parseBulk()
+  return r.readBulkString()
   
 proc rPush*(r: TRedis, key, value: string, create: bool = True): TRedisInteger =
   ## Append a value to a list. Returns the length of the list after the push.
@@ -487,106 +574,106 @@ proc rPush*(r: TRedis, key, value: string, create: bool = True): TRedisInteger =
     r.sendCommand("RPUSH", key, value)
   else:
     r.sendCommand("RPUSHX", key, value)
-  return r.parseInteger()
+  return r.readInteger()
 
 # Sets
 
 proc sadd*(r: TRedis, key: string, member: string): TRedisInteger =
   ## Add a member to a set
   r.sendCommand("SADD", key, member)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc scard*(r: TRedis, key: string): TRedisInteger =
   ## Get the number of members in a set
   r.sendCommand("SCARD", key)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc sdiff*(r: TRedis, keys: varargs[string]): TRedisList =
   ## Subtract multiple sets
   r.sendCommand("SDIFF", keys)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc sdiffstore*(r: TRedis, destination: string,
                 keys: varargs[string]): TRedisInteger =
   ## Subtract multiple sets and store the resulting set in a key
   r.sendCommand("SDIFFSTORE", destination, keys)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc sinter*(r: TRedis, keys: varargs[string]): TRedisList =
   ## Intersect multiple sets
   r.sendCommand("SINTER", keys)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc sinterstore*(r: TRedis, destination: string,
                  keys: varargs[string]): TRedisInteger =
   ## Intersect multiple sets and store the resulting set in a key
   r.sendCommand("SINTERSTORE", destination, keys)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc sismember*(r: TRedis, key: string, member: string): TRedisInteger =
   ## Determine if a given value is a member of a set
   r.sendCommand("SISMEMBER", key, member)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc smembers*(r: TRedis, key: string): TRedisList =
   ## Get all the members in a set
   r.sendCommand("SMEMBERS", key)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc smove*(r: TRedis, source: string, destination: string,
            member: string): TRedisInteger =
   ## Move a member from one set to another
   r.sendCommand("SMOVE", source, destination, member)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc spop*(r: TRedis, key: string): TRedisString =
   ## Remove and return a random member from a set
   r.sendCommand("SPOP", key)
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc srandmember*(r: TRedis, key: string): TRedisString =
   ## Get a random member from a set
   r.sendCommand("SRANDMEMBER", key)
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc srem*(r: TRedis, key: string, member: string): TRedisInteger =
   ## Remove a member from a set
   r.sendCommand("SREM", key, member)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc sunion*(r: TRedis, keys: varargs[string]): TRedisList =
   ## Add multiple sets
   r.sendCommand("SUNION", keys)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc sunionstore*(r: TRedis, destination: string,
                  key: varargs[string]): TRedisInteger =
   ## Add multiple sets and store the resulting set in a key 
   r.sendCommand("SUNIONSTORE", destination, key)
-  return r.parseInteger()
+  return r.readInteger()
 
 # Sorted sets
 
 proc zadd*(r: TRedis, key: string, score: int, member: string): TRedisInteger =
   ## Add a member to a sorted set, or update its score if it already exists
   r.sendCommand("ZADD", key, $score, member)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc zcard*(r: TRedis, key: string): TRedisInteger =
   ## Get the number of members in a sorted set
   r.sendCommand("ZCARD", key)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc zcount*(r: TRedis, key: string, min: string, max: string): TRedisInteger =
   ## Count the members in a sorted set with scores within the given values
   r.sendCommand("ZCOUNT", key, min, max)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc zincrby*(r: TRedis, key: string, increment: string,
              member: string): TRedisString =
   ## Increment the score of a member in a sorted set
   r.sendCommand("ZINCRBY", key, increment, member)
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc zinterstore*(r: TRedis, destination: string, numkeys: string,
                  keys: openarray[string], weights: openarray[string] = [],
@@ -605,7 +692,7 @@ proc zinterstore*(r: TRedis, destination: string, numkeys: string,
     
   r.sendCommand("ZINTERSTORE", args)
   
-  return r.parseInteger()
+  return r.readInteger()
 
 proc zrange*(r: TRedis, key: string, start: string, stop: string,
             withScores: bool): TRedisList =
@@ -614,7 +701,7 @@ proc zrange*(r: TRedis, key: string, start: string, stop: string,
     r.sendCommand("ZRANGE", key, start, stop)
   else:
     r.sendCommand("ZRANGE", "WITHSCORES", key, start, stop)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc zrangebyscore*(r: TRedis, key: string, min: string, max: string, 
                    withScore: bool = false, limit: bool = False,
@@ -629,29 +716,29 @@ proc zrangebyscore*(r: TRedis, key: string, min: string, max: string,
     args.add($limitCount)
     
   r.sendCommand("ZRANGEBYSCORE", args)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc zrank*(r: TRedis, key: string, member: string): TRedisString =
   ## Determine the index of a member in a sorted set
   r.sendCommand("ZRANK", key, member)
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc zrem*(r: TRedis, key: string, member: string): TRedisInteger =
   ## Remove a member from a sorted set
   r.sendCommand("ZREM", key, member)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc zremrangebyrank*(r: TRedis, key: string, start: string,
                      stop: string): TRedisInteger =
   ## Remove all members in a sorted set within the given indexes
   r.sendCommand("ZREMRANGEBYRANK", key, start, stop)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc zremrangebyscore*(r: TRedis, key: string, min: string,
                       max: string): TRedisInteger =
   ## Remove all members in a sorted set within the given scores
   r.sendCommand("ZREMRANGEBYSCORE", key, min, max)
-  return r.parseInteger()
+  return r.readInteger()
 
 proc zrevrange*(r: TRedis, key: string, start: string, stop: string,
                withScore: bool): TRedisList =
@@ -660,7 +747,7 @@ proc zrevrange*(r: TRedis, key: string, start: string, stop: string,
   if withScore:
     r.sendCommand("ZREVRANGE", "WITHSCORE", key, start, stop)
   else: r.sendCommand("ZREVRANGE", key, start, stop)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc zrevrangebyscore*(r: TRedis, key: string, min: string, max: string, 
                    withScore: bool = false, limit: bool = False,
@@ -676,18 +763,18 @@ proc zrevrangebyscore*(r: TRedis, key: string, min: string, max: string,
     args.add($limitCount)
   
   r.sendCommand("ZREVRANGEBYSCORE", args)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc zrevrank*(r: TRedis, key: string, member: string): TRedisString =
   ## Determine the index of a member in a sorted set, with
   ## scores ordered from high to low
   r.sendCommand("ZREVRANK", key, member)
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc zscore*(r: TRedis, key: string, member: string): TRedisString =
   ## Get the score associated with the given member in a sorted set
   r.sendCommand("ZSCORE", key, member)
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc zunionstore*(r: TRedis, destination: string, numkeys: string,
                  keys: openarray[string], weights: openarray[string] = [],
@@ -705,7 +792,7 @@ proc zunionstore*(r: TRedis, destination: string, numkeys: string,
     
   r.sendCommand("ZUNIONSTORE", args)
   
-  return r.parseInteger()
+  return r.readInteger()
 
 
 # Pub/Sub
@@ -720,7 +807,7 @@ proc psubscribe*(r: TRedis, pattern: openarray[string]): ???? =
 proc publish*(r: TRedis, channel: string, message: string): TRedisInteger =
   ## Post a message to a channel
   r.socket.send("PUBLISH $# $#\c\L" % [channel, message])
-  return r.parseInteger()
+  return r.readInteger()
 
 proc punsubscribe*(r: TRedis, [pattern: openarray[string], : string): ???? =
   ## Stop listening for messages posted to channels matching the given patterns
@@ -744,92 +831,96 @@ proc unsubscribe*(r: TRedis, [channel: openarray[string], : string): ???? =
 proc discardMulti*(r: TRedis) =
   ## Discard all commands issued after MULTI
   r.sendCommand("DISCARD")
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc exec*(r: TRedis): TRedisList =
   ## Execute all commands issued after MULTI
-  r.sendCommand("EXEC")
-
-  return r.parseMultiBulk()
+  r.sendCommand("EXEC")  
+  r.pipeline.enabled = false
+  # Will reply with +OK for MULTI/EXEC and +QUEUED for every command
+  # between, then with the results
+  return r.flushPipeline(true)
+  
 
 proc multi*(r: TRedis) =
   ## Mark the start of a transaction block
+  r.startPipelining()
   r.sendCommand("MULTI")
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc unwatch*(r: TRedis) =
   ## Forget about all watched keys
   r.sendCommand("UNWATCH")
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc watch*(r: TRedis, key: varargs[string]) =
   ## Watch the given keys to determine execution of the MULTI/EXEC block 
   r.sendCommand("WATCH", key)
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 # Connection
 
 proc auth*(r: TRedis, password: string) =
   ## Authenticate to the server
   r.sendCommand("AUTH", password)
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc echoServ*(r: TRedis, message: string): TRedisString =
   ## Echo the given string
   r.sendCommand("ECHO", message)
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc ping*(r: TRedis): TRedisStatus =
   ## Ping the server
   r.sendCommand("PING")
-  return r.parseStatus()
+  return r.readStatus()
 
 proc quit*(r: TRedis) =
   ## Close the connection
   r.sendCommand("QUIT")
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc select*(r: TRedis, index: int): TRedisStatus =
   ## Change the selected database for the current connection 
   r.sendCommand("SELECT", $index)
-  return r.parseStatus()
+  return r.readStatus()
 
 # Server
 
 proc bgrewriteaof*(r: TRedis) =
   ## Asynchronously rewrite the append-only file
   r.sendCommand("BGREWRITEAOF")
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc bgsave*(r: TRedis) =
   ## Asynchronously save the dataset to disk
   r.sendCommand("BGSAVE")
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc configGet*(r: TRedis, parameter: string): TRedisList =
   ## Get the value of a configuration parameter
   r.sendCommand("CONFIG", "GET", parameter)
-  return r.parseMultiBulk()
+  return r.readArray()
 
 proc configSet*(r: TRedis, parameter: string, value: string) =
   ## Set a configuration parameter to the given value
   r.sendCommand("CONFIG", "SET", parameter, value)
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc configResetStat*(r: TRedis) =
   ## Reset the stats returned by INFO
   r.sendCommand("CONFIG", "RESETSTAT")
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc dbsize*(r: TRedis): TRedisInteger =
   ## Return the number of keys in the selected database
   r.sendCommand("DBSIZE")
-  return r.parseInteger()
+  return r.readInteger()
 
 proc debugObject*(r: TRedis, key: string): TRedisStatus =
   ## Get debugging information about a key
   r.sendCommand("DEBUG", "OBJECT", key)
-  return r.parseStatus()
+  return r.readStatus()
 
 proc debugSegfault*(r: TRedis) =
   ## Make the server crash
@@ -838,34 +929,34 @@ proc debugSegfault*(r: TRedis) =
 proc flushall*(r: TRedis): TRedisStatus =
   ## Remove all keys from all databases
   r.sendCommand("FLUSHALL")
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc flushdb*(r: TRedis): TRedisStatus =
   ## Remove all keys from the current database
   r.sendCommand("FLUSHDB")
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc info*(r: TRedis): TRedisString =
   ## Get information and statistics about the server
   r.sendCommand("INFO")
-  return r.parseBulk()
+  return r.readBulkString()
 
 proc lastsave*(r: TRedis): TRedisInteger =
   ## Get the UNIX time stamp of the last successful save to disk
   r.sendCommand("LASTSAVE")
-  return r.parseInteger()
+  return r.readInteger()
 
 discard """
 proc monitor*(r: TRedis) =
   ## Listen for all requests received by the server in real time
   r.socket.send("MONITOR\c\L")
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 """
 
 proc save*(r: TRedis) =
   ## Synchronously save the dataset to disk
   r.sendCommand("SAVE")
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 proc shutdown*(r: TRedis) =
   ## Synchronously save the dataset to disk and then shut down the server
@@ -877,7 +968,7 @@ proc shutdown*(r: TRedis) =
 proc slaveof*(r: TRedis, host: string, port: string) =
   ## Make the server a slave of another instance, or promote it as master
   r.sendCommand("SLAVEOF", host, port)
-  raiseNoOK(r.parseStatus())
+  raiseNoOK(r.readStatus(), r.pipeline.enabled)
 
 iterator hPairs*(r: TRedis, key: string): tuple[key, value: string] =
   ## Iterator for keys and values in a hash.
@@ -890,33 +981,72 @@ iterator hPairs*(r: TRedis, key: string): tuple[key, value: string] =
     else:
       yield (k, i)
       k = ""
-      
 
-when false:
-  # sorry, deactivated for the test suite
-  var r = open()
-  r.auth("pass")
+proc someTests(r: TRedis, how: TSendMode):seq[string] =
+  var list:seq[string] = @[]
 
+  if how == pipelined:
+    r.startPipelining()
+  elif how ==  multiple: 
+    r.multi()
+    
   r.setk("nim:test", "Testing something.")
   r.setk("nim:utf8", "こんにちは")
   r.setk("nim:esc", "\\ths ągt\\")
-  
-  echo r.get("nim:esc")
-  echo r.incr("nim:int")
-  echo r.incr("nim:int")
-  echo r.get("nim:int")
-  echo r.get("nim:utf8")
-  echo repr(r.get("blahasha"))
-  echo r.randomKey()
-  
+  r.setk("nim:int", "1")
+  list.add(r.get("nim:esc"))
+  list.add($(r.incr("nim:int")))
+  list.add(r.get("nim:int"))
+  list.add(r.get("nim:utf8"))
+  list.add($(r.hSet("test1", "name", "A Test")))
+  var res = r.hGetAll("test1")
+  for r in res:
+    list.add(r)
+  list.add(r.get("invalid_key"))
+  list.add($(r.lpush("mylist","itema")))
+  list.add($(r.lpush("mylist","itemb")))
+  r.ltrim("mylist",0,1)
   var p = r.lrange("mylist", 0, -1)
+
   for i in items(p):
-    echo("  ", i)
+    if not isNil(i):
+      list.add(i) 
 
-  echo(r.debugObject("test"))
+  list.add(r.debugObject("mylist"))
 
   r.configSet("timeout", "299")
-  for i in items(r.configGet("timeout")): echo ">> ", i
+  var g = r.configGet("timeout")
+  for i in items(g):
+    list.add(i)
+
+  list.add(r.echoServ("BLAH"))
+
+  case how
+  of normal:
+    return list
+  of pipelined:
+    return r.flushPipeline()
+  of multiple:
+    return r.exec()
+
+proc assertListsIdentical(listA, listB: seq[string]) =
+  assert(listA.len == listB.len)
+  var i = 0
+  for item in listA:
+    assert(item == listB[i])
+    i = i + 1
+  
+when isMainModule:
+  when false:
+    var r = open()
+
+    # Test with no pipelining
+    var listNormal = r.someTests(normal)
 
-  echo r.echoServ("BLAH")
+    # Test with pipelining enabled
+    var listPipelined = r.someTests(pipelined)
+    assertListsIdentical(listNormal, listPipelined)
 
+    # Test with multi/exec() (automatic pipelining)
+    var listMulti = r.someTests(multiple)
+    assertListsIdentical(listNormal, listMulti)
diff --git a/lib/pure/scgi.nim b/lib/pure/scgi.nim
index a6a0faabc..45f837833 100644
--- a/lib/pure/scgi.nim
+++ b/lib/pure/scgi.nim
@@ -26,6 +26,8 @@
 ## **Warning:** The API of this module is unstable, and therefore is subject
 ## to change.
 
+include "system/inclrtl"
+
 import sockets, strutils, os, strtabs, asyncio
 
 type
@@ -82,7 +84,7 @@ type
   
   TAsyncScgiState = object
     handleRequest: proc (client: PAsyncSocket, 
-                         input: string, headers: PStringTable) {.closure.}
+                         input: string, headers: PStringTable) {.closure,gcsafe.}
     asyncServer: PAsyncSocket
     disp: PDispatcher
   PAsyncScgiState* = ref TAsyncScgiState
@@ -150,7 +152,7 @@ proc writeStatusOkTextContent*(c: TSocket, contentType = "text/html") =
          "Content-Type: $1\r\L\r\L" % contentType)
 
 proc run*(handleRequest: proc (client: TSocket, input: string, 
-                               headers: PStringTable): bool {.nimcall.},
+                               headers: PStringTable): bool {.nimcall,gcsafe.},
           port = TPort(4000)) = 
   ## encapsulates the SCGI object and main loop.
   var s: TScgiState
@@ -246,7 +248,8 @@ proc handleAccept(sock: PAsyncSocket, s: PAsyncScgiState) =
   s.disp.register(client)
 
 proc open*(handleRequest: proc (client: PAsyncSocket, 
-                                input: string, headers: PStringTable) {.closure.},
+                                input: string, headers: PStringTable) {.
+                                closure, gcsafe.},
            port = TPort(4000), address = "127.0.0.1",
            reuseAddr = false): PAsyncScgiState =
   ## Creates an ``PAsyncScgiState`` object which serves as a SCGI server.
diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim
index b6acc329f..8d96cbaaf 100644
--- a/lib/pure/sockets.nim
+++ b/lib/pure/sockets.nim
@@ -24,6 +24,8 @@
 ## Asynchronous sockets are supported, however a better alternative is to use
 ## the `asyncio <asyncio.html>`_ module.
 
+include "system/inclrtl"
+
 {.deadCodeElim: on.}
 
 when hostOS == "solaris":
@@ -544,7 +546,7 @@ proc acceptAddr*(server: TSocket, client: var TSocket, address: var string) {.
             else:
               SSLError("Unknown error")
 
-proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].}
+proc setBlocking*(s: TSocket, blocking: bool) {.tags: [], gcsafe.}
   ## Sets blocking mode on socket
 
 when defined(ssl):
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index 3b6dc87a5..63622a26c 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -12,6 +12,8 @@
 ## interface for Nimrod file objects (`TFile`) and strings. Other modules
 ## may provide other implementations for this standard stream interface.
 
+include "system/inclrtl"
+
 proc newEIO(msg: string): ref EIO =
   new(result)
   result.msg = msg
@@ -23,15 +25,15 @@ type
                                ## here shouldn't be used directly. They are
                                ## accessible so that a stream implementation
                                ## can override them.
-    closeImpl*: proc (s: PStream) {.nimcall, tags: [].}
-    atEndImpl*: proc (s: PStream): bool {.nimcall, tags: [].}
-    setPositionImpl*: proc (s: PStream, pos: int) {.nimcall, tags: [].}
-    getPositionImpl*: proc (s: PStream): int {.nimcall, tags: [].}
+    closeImpl*: proc (s: PStream) {.nimcall, tags: [], gcsafe.}
+    atEndImpl*: proc (s: PStream): bool {.nimcall, tags: [], gcsafe.}
+    setPositionImpl*: proc (s: PStream, pos: int) {.nimcall, tags: [], gcsafe.}
+    getPositionImpl*: proc (s: PStream): int {.nimcall, tags: [], gcsafe.}
     readDataImpl*: proc (s: PStream, buffer: pointer,
-                         bufLen: int): int {.nimcall, tags: [FReadIO].}
+                         bufLen: int): int {.nimcall, tags: [FReadIO], gcsafe.}
     writeDataImpl*: proc (s: PStream, buffer: pointer, bufLen: int) {.nimcall,
-      tags: [FWriteIO].}
-    flushImpl*: proc (s: PStream) {.nimcall, tags: [FWriteIO].}
+      tags: [FWriteIO], gcsafe.}
+    flushImpl*: proc (s: PStream) {.nimcall, tags: [FWriteIO], gcsafe.}
 
 proc flush*(s: PStream) =
   ## flushes the buffers that the stream `s` might use.
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 2fce235e2..fdff06b2a 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -135,38 +135,38 @@ type
     months*: int      ## The number of months
     years*: int       ## The number of years
 
-proc getTime*(): TTime {.tags: [FTime].}
+proc getTime*(): TTime {.tags: [FTime], gcsafe.}
   ## gets the current calendar time as a UNIX epoch value (number of seconds
   ## elapsed since 1970) with integer precission. Use epochTime for higher
   ## resolution.
-proc getLocalTime*(t: TTime): TTimeInfo {.tags: [FTime], raises: [].}
+proc getLocalTime*(t: TTime): TTimeInfo {.tags: [FTime], raises: [], gcsafe.}
   ## converts the calendar time `t` to broken-time representation,
   ## expressed relative to the user's specified time zone.
-proc getGMTime*(t: TTime): TTimeInfo {.tags: [FTime], raises: [].}
+proc getGMTime*(t: TTime): TTimeInfo {.tags: [FTime], raises: [], gcsafe.}
   ## converts the calendar time `t` to broken-down time representation,
   ## expressed in Coordinated Universal Time (UTC).
 
-proc timeInfoToTime*(timeInfo: TTimeInfo): TTime {.tags: [].}
+proc timeInfoToTime*(timeInfo: TTimeInfo): TTime {.tags: [], gcsafe.}
   ## converts a broken-down time structure to
   ## calendar time representation. The function ignores the specified
   ## contents of the structure members `weekday` and `yearday` and recomputes
   ## them from the other information in the broken-down time structure.
 
-proc fromSeconds*(since1970: float): TTime {.tags: [], raises: [].}
+proc fromSeconds*(since1970: float): TTime {.tags: [], raises: [], gcsafe.}
   ## Takes a float which contains the number of seconds since the unix epoch and
   ## returns a time object.
 
-proc fromSeconds*(since1970: int64): TTime {.tags: [], raises: [].} = 
+proc fromSeconds*(since1970: int64): TTime {.tags: [], raises: [], gcsafe.} = 
   ## Takes an int which contains the number of seconds since the unix epoch and
   ## returns a time object.
   fromSeconds(float(since1970))
 
-proc toSeconds*(time: TTime): float {.tags: [], raises: [].}
+proc toSeconds*(time: TTime): float {.tags: [], raises: [], gcsafe.}
   ## Returns the time in seconds since the unix epoch.
 
-proc `$` *(timeInfo: TTimeInfo): string {.tags: [], raises: [].}
+proc `$` *(timeInfo: TTimeInfo): string {.tags: [], raises: [], gcsafe.}
   ## converts a `TTimeInfo` object to a string representation.
-proc `$` *(time: TTime): string {.tags: [], raises: [].}
+proc `$` *(time: TTime): string {.tags: [], raises: [], gcsafe.}
   ## converts a calendar time to a string representation.
 
 proc `-`*(a, b: TTime): int64 {.
@@ -189,14 +189,15 @@ proc `==`*(a, b: TTime): bool {.
   result = a - b == 0
 
 when not defined(JS):
-  proc getTzname*(): tuple[nonDST, DST: string] {.tags: [FTime], raises: [].}
+  proc getTzname*(): tuple[nonDST, DST: string] {.tags: [FTime], raises: [],
+    gcsafe.}
     ## returns the local timezone; ``nonDST`` is the name of the local non-DST
     ## timezone, ``DST`` is the name of the local DST timezone.
 
-proc getTimezone*(): int {.tags: [FTime], raises: [].}
+proc getTimezone*(): int {.tags: [FTime], raises: [], gcsafe.}
   ## returns the offset of the local (non-DST) timezone in seconds west of UTC.
 
-proc getStartMilsecs*(): int {.deprecated, tags: [FTime].}
+proc getStartMilsecs*(): int {.deprecated, tags: [FTime], gcsafe.}
   ## get the miliseconds from the start of the program. **Deprecated since
   ## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
 
diff --git a/lib/system.nim b/lib/system.nim
index 3cb6b08d5..273820b82 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -77,7 +77,7 @@ type
 
   TNumber* = TInteger|TReal
     ## type class matching all number types
-  
+
 proc defined*(x: expr): bool {.magic: "Defined", noSideEffect.}
   ## Special compile-time procedure that checks whether `x` is
   ## defined. `x` has to be an identifier or a qualified identifier.
@@ -188,6 +188,13 @@ when not defined(niminheritable):
 when not defined(nimunion):
   {.pragma: unchecked.}
 
+when defined(nimNewShared):
+  type
+    `shared`* {.magic: "Shared".}
+    guarded* {.magic: "Guarded".}
+
+include "system/inclrtl"
+
 const NoFakeVars* = defined(NimrodVM) ## true if the backend doesn't support \
   ## "fake variables" like 'var EBADF {.importc.}: cint'.
 
@@ -784,7 +791,8 @@ proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
   ##   assert(test[int](3) == 3)
   ##   assert(test[string]("xyz") == 0)
 template `isnot` *(x, y: expr): expr {.immediate.} = not (x is y)
-  ## Negated version of `is`. Equivalent to `not(is(x,y))`
+  ## Negated version of `is`. Equivalent to ``not(x is y)``.
+
 proc `of` *[T, S](x: T, y: S): bool {.magic: "Of", noSideEffect.}
   ## Checks if `x` has a type of `y`
   ##
@@ -1021,8 +1029,6 @@ template sysAssert(cond: bool, msg: string) =
       echo "[SYSASSERT] ", msg
       quit 1
 
-include "system/inclrtl"
-
 when not defined(JS) and not defined(nimrodVm) and hostOS != "standalone":
   include "system/cgprocs"
 
@@ -1201,20 +1207,20 @@ proc substr*(s: string, first, last: int): string {.
   ## or `limit`:idx: a string's length.
 
 when not defined(nimrodVM):
-  proc zeroMem*(p: pointer, size: int) {.importc, noDecl.}
+  proc zeroMem*(p: pointer, size: int) {.importc, noDecl, gcsafe.}
     ## overwrites the contents of the memory at ``p`` with the value 0.
     ## Exactly ``size`` bytes will be overwritten. Like any procedure
     ## dealing with raw memory this is *unsafe*.
 
   proc copyMem*(dest, source: pointer, size: int) {.
-    importc: "memcpy", header: "<string.h>".}
+    importc: "memcpy", header: "<string.h>", gcsafe.}
     ## copies the contents from the memory at ``source`` to the memory
     ## at ``dest``. Exactly ``size`` bytes will be copied. The memory
     ## regions may not overlap. Like any procedure dealing with raw
     ## memory this is *unsafe*.
 
   proc moveMem*(dest, source: pointer, size: int) {.
-    importc: "memmove", header: "<string.h>".}
+    importc: "memmove", header: "<string.h>", gcsafe.}
     ## copies the contents from the memory at ``source`` to the memory
     ## at ``dest``. Exactly ``size`` bytes will be copied. The memory
     ## regions may overlap, ``moveMem`` handles this case appropriately
@@ -1229,14 +1235,14 @@ when not defined(nimrodVM):
     ## *unsafe*.
 
   when hostOS != "standalone":
-    proc alloc*(size: int): pointer {.noconv, rtl, tags: [].}
+    proc alloc*(size: int): pointer {.noconv, rtl, tags: [], gcsafe.}
       ## allocates a new memory block with at least ``size`` bytes. The
       ## block has to be freed with ``realloc(block, 0)`` or
       ## ``dealloc(block)``. The block is not initialized, so reading
       ## from it before writing to it is undefined behaviour!
       ## The allocated memory belongs to its allocating thread!
       ## Use `allocShared` to allocate from a shared heap.
-    proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
+    proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline, gcsafe.} =
       ## allocates a new memory block with at least ``T.sizeof * size``
       ## bytes. The block has to be freed with ``resize(block, 0)`` or
       ## ``free(block)``. The block is not initialized, so reading
@@ -1244,14 +1250,14 @@ when not defined(nimrodVM):
       ## The allocated memory belongs to its allocating thread!
       ## Use `createSharedU` to allocate from a shared heap.
       cast[ptr T](alloc(T.sizeof * size))
-    proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].}
+    proc alloc0*(size: int): pointer {.noconv, rtl, tags: [], gcsafe.}
       ## allocates a new memory block with at least ``size`` bytes. The
       ## block has to be freed with ``realloc(block, 0)`` or
       ## ``dealloc(block)``. The block is initialized with all bytes
       ## containing zero, so it is somewhat safer than ``alloc``.
       ## The allocated memory belongs to its allocating thread!
       ## Use `allocShared0` to allocate from a shared heap.
-    proc create*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
+    proc create*(T: typedesc, size = 1.Positive): ptr T {.inline, gcsafe.} =
       ## allocates a new memory block with at least ``T.sizeof * size``
       ## bytes. The block has to be freed with ``resize(block, 0)`` or
       ## ``free(block)``. The block is initialized with all bytes
@@ -1259,7 +1265,8 @@ when not defined(nimrodVM):
       ## The allocated memory belongs to its allocating thread!
       ## Use `createShared` to allocate from a shared heap.
       cast[ptr T](alloc0(T.sizeof * size))
-    proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [].}
+    proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [], 
+                                                       gcsafe.}
       ## grows or shrinks a given memory block. If p is **nil** then a new
       ## memory block is returned. In either way the block has at least
       ## ``newSize`` bytes. If ``newSize == 0`` and p is not **nil**
@@ -1267,7 +1274,7 @@ when not defined(nimrodVM):
       ## be freed with ``dealloc``.
       ## The allocated memory belongs to its allocating thread!
       ## Use `reallocShared` to reallocate from a shared heap.
-    proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline.} =
+    proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline, gcsafe.} =
       ## grows or shrinks a given memory block. If p is **nil** then a new
       ## memory block is returned. In either way the block has at least
       ## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not
@@ -1276,7 +1283,7 @@ when not defined(nimrodVM):
       ## its allocating thread!
       ## Use `resizeShared` to reallocate from a shared heap.
       cast[ptr T](realloc(p, T.sizeof * newSize))
-    proc dealloc*(p: pointer) {.noconv, rtl, tags: [].}
+    proc dealloc*(p: pointer) {.noconv, rtl, tags: [], gcsafe.}
       ## frees the memory allocated with ``alloc``, ``alloc0`` or
       ## ``realloc``. This procedure is dangerous! If one forgets to
       ## free the memory a leak occurs; if one tries to access freed
@@ -1284,22 +1291,23 @@ when not defined(nimrodVM):
       ## or other memory may be corrupted. 
       ## The freed memory must belong to its allocating thread!
       ## Use `deallocShared` to deallocate from a shared heap.
-    proc free*[T](p: ptr T) {.inline.} =
+    proc free*[T](p: ptr T) {.inline, gcsafe.} =
       dealloc(p)
-    proc allocShared*(size: int): pointer {.noconv, rtl.}
+    proc allocShared*(size: int): pointer {.noconv, rtl, gcsafe.}
       ## allocates a new memory block on the shared heap with at
       ## least ``size`` bytes. The block has to be freed with
       ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
       ## is not initialized, so reading from it before writing to it is 
       ## undefined behaviour!
-    proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
+    proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline, 
+                                                                 gcsafe.} =
       ## allocates a new memory block on the shared heap with at
       ## least ``T.sizeof * size`` bytes. The block has to be freed with
       ## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block
       ## is not initialized, so reading from it before writing to it is 
       ## undefined behaviour!
       cast[ptr T](allocShared(T.sizeof * size))
-    proc allocShared0*(size: int): pointer {.noconv, rtl.}
+    proc allocShared0*(size: int): pointer {.noconv, rtl, gcsafe.}
       ## allocates a new memory block on the shared heap with at 
       ## least ``size`` bytes. The block has to be freed with
       ## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
@@ -1312,7 +1320,8 @@ when not defined(nimrodVM):
       ## The block is initialized with all bytes
       ## containing zero, so it is somewhat safer than ``createSharedU``.
       cast[ptr T](allocShared0(T.sizeof * size))
-    proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl.}
+    proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl, 
+                                                             gcsafe.}
       ## grows or shrinks a given memory block on the heap. If p is **nil**
       ## then a new memory block is returned. In either way the block has at
       ## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil**
@@ -1325,13 +1334,13 @@ when not defined(nimrodVM):
       ## not **nil** ``resizeShared`` calls ``freeShared(p)``. In other
       ## cases the block has to be freed with ``freeShared``.
       cast[ptr T](reallocShared(p, T.sizeof * newSize))
-    proc deallocShared*(p: pointer) {.noconv, rtl.}
+    proc deallocShared*(p: pointer) {.noconv, rtl, gcsafe.}
       ## frees the memory allocated with ``allocShared``, ``allocShared0`` or
       ## ``reallocShared``. This procedure is dangerous! If one forgets to
       ## free the memory a leak occurs; if one tries to access freed
       ## memory (or just freeing it twice!) a core dump may happen
       ## or other memory may be corrupted.
-    proc freeShared*[T](p: ptr T) {.inline.} =
+    proc freeShared*[T](p: ptr T) {.inline, gcsafe.} =
       ## frees the memory allocated with ``createShared``, ``createSharedU`` or
       ## ``resizeShared``. This procedure is dangerous! If one forgets to
       ## free the memory a leak occurs; if one tries to access freed
@@ -1892,7 +1901,7 @@ const nimrodStackTrace = compileOption("stacktrace")
 # of the code
 
 var
-  globalRaiseHook*: proc (e: ref E_Base): bool {.nimcall.}
+  globalRaiseHook*: proc (e: ref E_Base): bool {.nimcall, gcsafe.}
     ## with this hook you can influence exception handling on a global level.
     ## If not nil, every 'raise' statement ends up calling this hook. Ordinary
     ## application code should never set this hook! You better know what you
@@ -1900,7 +1909,7 @@ var
     ## exception is caught and does not propagate further through the call
     ## stack.
 
-  localRaiseHook* {.threadvar.}: proc (e: ref E_Base): bool {.nimcall.}
+  localRaiseHook* {.threadvar.}: proc (e: ref E_Base): bool {.nimcall, gcsafe.}
     ## with this hook you can influence exception handling on a
     ## thread local level.
     ## If not nil, every 'raise' statement ends up calling this hook. Ordinary
@@ -1908,7 +1917,7 @@ var
     ## do when setting this. If ``localRaiseHook`` returns false, the exception
     ## is caught and does not propagate further through the call stack.
     
-  outOfMemHook*: proc () {.nimcall, tags: [].}
+  outOfMemHook*: proc () {.nimcall, tags: [], gcsafe.}
     ## set this variable to provide a procedure that should be called 
     ## in case of an `out of memory`:idx: event. The standard handler
     ## writes an error message and terminates the program. `outOfMemHook` can
@@ -1959,7 +1968,7 @@ elif hostOS != "standalone":
       inc(i)
   {.pop.}
 
-proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo", tags: [FWriteIO].}
+proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo", tags: [FWriteIO], gcsafe.}
   ## special built-in that takes a variable number of arguments. Each argument
   ## is converted to a string via ``$``, so it works for user-defined
   ## types that have an overloaded ``$`` operator.
@@ -2006,7 +2015,7 @@ when not defined(sysFatal):
       e.msg = message & arg
       raise e
 
-proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo".}
+proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", gcsafe.}
   ## get type information for `x`. Ordinary code should not use this, but
   ## the `typeinfo` module instead.
 
@@ -2113,14 +2122,15 @@ when not defined(JS): #and not defined(NimrodVM):
         ## `useStdoutAsStdmsg` compile-time switch.
 
     proc open*(f: var TFile, filename: string,
-               mode: TFileMode = fmRead, bufSize: int = -1): bool {.tags: [].}
+               mode: TFileMode = fmRead, bufSize: int = -1): bool {.tags: [],
+               gcsafe.}
       ## Opens a file named `filename` with given `mode`.
       ##
       ## Default mode is readonly. Returns true iff the file could be opened.
       ## This throws no exception if the file could not be opened.
 
     proc open*(f: var TFile, filehandle: TFileHandle,
-               mode: TFileMode = fmRead): bool {.tags: [].}
+               mode: TFileMode = fmRead): bool {.tags: [], gcsafe.}
       ## Creates a ``TFile`` from a `filehandle` with given `mode`.
       ##
       ## Default mode is readonly. Returns true iff the file could be opened.
@@ -2135,7 +2145,7 @@ when not defined(JS): #and not defined(NimrodVM):
         sysFatal(EIO, "cannot open: ", filename)
 
     proc reopen*(f: TFile, filename: string, mode: TFileMode = fmRead): bool {.
-      tags: [].}
+      tags: [], gcsafe.}
       ## reopens the file `f` with given `filename` and `mode`. This 
       ## is often used to redirect the `stdin`, `stdout` or `stderr`
       ## file variables.
@@ -2145,7 +2155,7 @@ when not defined(JS): #and not defined(NimrodVM):
     proc close*(f: TFile) {.importc: "fclose", header: "<stdio.h>", tags: [].}
       ## Closes the file.
 
-    proc endOfFile*(f: TFile): bool {.tags: [].}
+    proc endOfFile*(f: TFile): bool {.tags: [], gcsafe.}
       ## Returns true iff `f` is at the end.
       
     proc readChar*(f: TFile): char {.
@@ -2155,39 +2165,40 @@ when not defined(JS): #and not defined(NimrodVM):
       importc: "fflush", header: "<stdio.h>", tags: [FWriteIO].}
       ## Flushes `f`'s buffer.
 
-    proc readAll*(file: TFile): TaintedString {.tags: [FReadIO].}
+    proc readAll*(file: TFile): TaintedString {.tags: [FReadIO], gcsafe.}
       ## Reads all data from the stream `file`.
       ##
       ## Raises an IO exception in case of an error. It is an error if the
       ## current file position is not at the beginning of the file.
     
-    proc readFile*(filename: string): TaintedString {.tags: [FReadIO].}
+    proc readFile*(filename: string): TaintedString {.tags: [FReadIO], gcsafe.}
       ## Opens a file named `filename` for reading. Then calls `readAll`
       ## and closes the file afterwards. Returns the string. 
       ## Raises an IO exception in case of an error.
 
-    proc writeFile*(filename, content: string) {.tags: [FWriteIO].}
+    proc writeFile*(filename, content: string) {.tags: [FWriteIO], gcsafe.}
       ## Opens a file named `filename` for writing. Then writes the
       ## `content` completely to the file and closes the file afterwards.
       ## Raises an IO exception in case of an error.
 
-    proc write*(f: TFile, r: float32) {.tags: [FWriteIO].}
-    proc write*(f: TFile, i: int) {.tags: [FWriteIO].}
-    proc write*(f: TFile, i: BiggestInt) {.tags: [FWriteIO].}
-    proc write*(f: TFile, r: BiggestFloat) {.tags: [FWriteIO].}
-    proc write*(f: TFile, s: string) {.tags: [FWriteIO].}
-    proc write*(f: TFile, b: bool) {.tags: [FWriteIO].}
-    proc write*(f: TFile, c: char) {.tags: [FWriteIO].}
-    proc write*(f: TFile, c: cstring) {.tags: [FWriteIO].}
-    proc write*(f: TFile, a: varargs[string, `$`]) {.tags: [FWriteIO].}
+    proc write*(f: TFile, r: float32) {.tags: [FWriteIO], gcsafe.}
+    proc write*(f: TFile, i: int) {.tags: [FWriteIO], gcsafe.}
+    proc write*(f: TFile, i: BiggestInt) {.tags: [FWriteIO], gcsafe.}
+    proc write*(f: TFile, r: BiggestFloat) {.tags: [FWriteIO], gcsafe.}
+    proc write*(f: TFile, s: string) {.tags: [FWriteIO], gcsafe.}
+    proc write*(f: TFile, b: bool) {.tags: [FWriteIO], gcsafe.}
+    proc write*(f: TFile, c: char) {.tags: [FWriteIO], gcsafe.}
+    proc write*(f: TFile, c: cstring) {.tags: [FWriteIO], gcsafe.}
+    proc write*(f: TFile, a: varargs[string, `$`]) {.tags: [FWriteIO], gcsafe.}
       ## Writes a value to the file `f`. May throw an IO exception.
 
-    proc readLine*(f: TFile): TaintedString  {.tags: [FReadIO].}
+    proc readLine*(f: TFile): TaintedString  {.tags: [FReadIO], gcsafe.}
       ## reads a line of text from the file `f`. May throw an IO exception.
       ## A line of text may be delimited by ``CR``, ``LF`` or
       ## ``CRLF``. The newline character(s) are not part of the returned string.
     
-    proc readLine*(f: TFile, line: var TaintedString): bool {.tags: [FReadIO].}
+    proc readLine*(f: TFile, line: var TaintedString): bool {.tags: [FReadIO], 
+                  gcsafe.}
       ## reads a line of text from the file `f` into `line`. `line` must not be
       ## ``nil``! May throw an IO exception.
       ## A line of text may be delimited by ``CR``, ``LF`` or
@@ -2195,53 +2206,59 @@ when not defined(JS): #and not defined(NimrodVM):
       ## Returns ``false`` if the end of the file has been reached, ``true``
       ## otherwise. If ``false`` is returned `line` contains no new data.
 
-    proc writeln*[Ty](f: TFile, x: varargs[Ty, `$`]) {.inline, tags: [FWriteIO].}
-      ## writes the values `x` to `f` and then writes "\n".
-      ## May throw an IO exception.
+    when not defined(booting):
+      proc writeln*[Ty](f: TFile, x: varargs[Ty, `$`]) {.inline, 
+                               tags: [FWriteIO], gcsafe.}
+        ## writes the values `x` to `f` and then writes "\n".
+        ## May throw an IO exception.
+    else:
+      proc writeln*[Ty](f: TFile, x: varargs[Ty, `$`]) {.inline, 
+                               tags: [FWriteIO].}
 
-    proc getFileSize*(f: TFile): int64 {.tags: [FReadIO].}
+    proc getFileSize*(f: TFile): int64 {.tags: [FReadIO], gcsafe.}
       ## retrieves the file size (in bytes) of `f`.
 
     proc readBytes*(f: TFile, a: var openArray[int8], start, len: int): int {.
-      tags: [FReadIO].}
+      tags: [FReadIO], gcsafe.}
       ## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns
       ## the actual number of bytes that have been read which may be less than
       ## `len` (if not as many bytes are remaining), but not greater.
 
     proc readChars*(f: TFile, a: var openArray[char], start, len: int): int {.
-      tags: [FReadIO].}
+      tags: [FReadIO], gcsafe.}
       ## reads `len` bytes into the buffer `a` starting at ``a[start]``. Returns
       ## the actual number of bytes that have been read which may be less than
       ## `len` (if not as many bytes are remaining), but not greater.
 
-    proc readBuffer*(f: TFile, buffer: pointer, len: int): int {.tags: [FReadIO].}
+    proc readBuffer*(f: TFile, buffer: pointer, len: int): int {.
+      tags: [FReadIO], gcsafe.}
       ## reads `len` bytes into the buffer pointed to by `buffer`. Returns
       ## the actual number of bytes that have been read which may be less than
       ## `len` (if not as many bytes are remaining), but not greater.
 
     proc writeBytes*(f: TFile, a: openArray[int8], start, len: int): int {.
-      tags: [FWriteIO].}
+      tags: [FWriteIO], gcsafe.}
       ## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns
       ## the number of actual written bytes, which may be less than `len` in case
       ## of an error.
 
     proc writeChars*(f: TFile, a: openArray[char], start, len: int): int {.
-      tags: [FWriteIO].}
+      tags: [FWriteIO], gcsafe.}
       ## writes the bytes of ``a[start..start+len-1]`` to the file `f`. Returns
       ## the number of actual written bytes, which may be less than `len` in case
       ## of an error.
 
     proc writeBuffer*(f: TFile, buffer: pointer, len: int): int {.
-      tags: [FWriteIO].}
+      tags: [FWriteIO], gcsafe.}
       ## writes the bytes of buffer pointed to by the parameter `buffer` to the
       ## file `f`. Returns the number of actual written bytes, which may be less
       ## than `len` in case of an error.
 
-    proc setFilePos*(f: TFile, pos: int64)
+    proc setFilePos*(f: TFile, pos: int64) {.gcsafe.}
       ## sets the position of the file pointer that is used for read/write
       ## operations. The file's first byte has the index zero.
 
-    proc getFilePos*(f: TFile): int64
+    proc getFilePos*(f: TFile): int64 {.gcsafe.}
       ## retrieves the current position of the file pointer that is used to
       ## read from the file `f`. The file's first byte has the index zero.
 
@@ -2284,10 +2301,12 @@ when not defined(JS): #and not defined(NimrodVM):
       dealloc(a)
 
   when not defined(NimrodVM):
-    proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable.}
+    proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, 
+      discardable, gcsafe.}
       ## atomic increment of `memLoc`. Returns the value after the operation.
     
-    proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable.}
+    proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, 
+      discardable, gcsafe.}
       ## atomic decrement of `memLoc`. Returns the value after the operation.
 
     include "system/atomics"
@@ -2917,3 +2936,6 @@ when not defined(booting):
 
   template isStatic*(x): expr = compiles(static(x))
     # checks whether `x` is a value known at compile-time
+
+when hasThreadSupport:
+  when hostOS != "standalone": include "system/sysspawn"
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 954485eb4..eaef6cd95 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -722,10 +722,13 @@ proc alloc0(allocator: var TMemRegion, size: int): pointer =
   zeroMem(result, size)
 
 proc dealloc(allocator: var TMemRegion, p: pointer) =
+  sysAssert(p != nil, "dealloc 0")
   var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
-  sysAssert(cast[ptr TFreeCell](x).zeroField == 1, "dealloc 1")
+  sysAssert(x != nil, "dealloc 1")
+  sysAssert(isAccessible(allocator, x), "is not accessible")
+  sysAssert(cast[ptr TFreeCell](x).zeroField == 1, "dealloc 2")
   rawDealloc(allocator, x)
-  sysAssert(not isAllocatedPtr(allocator, x), "dealloc 2")
+  sysAssert(not isAllocatedPtr(allocator, x), "dealloc 3")
 
 proc realloc(allocator: var TMemRegion, p: pointer, newsize: int): pointer =
   if newsize > 0:
diff --git a/lib/system/assign.nim b/lib/system/assign.nim
index bed8820be..75c749633 100644
--- a/lib/system/assign.nim
+++ b/lib/system/assign.nim
@@ -7,10 +7,11 @@
 #    distribution, for details about the copyright.
 #
 
-proc genericResetAux(dest: pointer, n: ptr TNimNode)
+proc genericResetAux(dest: pointer, n: ptr TNimNode) {.gcsafe.}
 
-proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool)
-proc genericAssignAux(dest, src: pointer, n: ptr TNimNode, shallow: bool) =
+proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) {.gcsafe.}
+proc genericAssignAux(dest, src: pointer, n: ptr TNimNode,
+                      shallow: bool) {.gcsafe.} =
   var
     d = cast[TAddress](dest)
     s = cast[TAddress](src)
@@ -139,8 +140,8 @@ proc genericAssignOpenArray(dest, src: pointer, len: int,
     genericAssign(cast[pointer](d +% i*% mt.base.size),
                   cast[pointer](s +% i*% mt.base.size), mt.base)
 
-proc objectInit(dest: pointer, typ: PNimType) {.compilerProc.}
-proc objectInitAux(dest: pointer, n: ptr TNimNode) =
+proc objectInit(dest: pointer, typ: PNimType) {.compilerProc, gcsafe.}
+proc objectInitAux(dest: pointer, n: ptr TNimNode) {.gcsafe.} =
   var d = cast[TAddress](dest)
   case n.kind
   of nkNone: sysAssert(false, "objectInitAux")
@@ -184,7 +185,7 @@ else:
     mixin destroy
     for i in countup(0, r.len - 1): destroy(r[i])
 
-proc genericReset(dest: pointer, mt: PNimType) {.compilerProc.}
+proc genericReset(dest: pointer, mt: PNimType) {.compilerProc, gcsafe.}
 proc genericResetAux(dest: pointer, n: ptr TNimNode) =
   var d = cast[TAddress](dest)
   case n.kind
diff --git a/lib/system/avltree.nim b/lib/system/avltree.nim
index fc965d6aa..bced15d6a 100644
--- a/lib/system/avltree.nim
+++ b/lib/system/avltree.nim
@@ -51,7 +51,7 @@ proc split(t: var PAvlNode) =
     t.link[0] = temp
     inc t.level
 
-proc add(a: var TMemRegion, t: var PAvlNode, key, upperBound: int) =
+proc add(a: var TMemRegion, t: var PAvlNode, key, upperBound: int) {.gcsafe.} =
   if t == bottom:
     t = allocAvlNode(a, key, upperBound)
   else:
@@ -64,7 +64,7 @@ proc add(a: var TMemRegion, t: var PAvlNode, key, upperBound: int) =
     skew(t)
     split(t)
 
-proc del(a: var TMemRegion, t: var PAvlNode, x: int) =
+proc del(a: var TMemRegion, t: var PAvlNode, x: int) {.gcsafe.} =
   if t == bottom: return
   a.last = t
   if x <% t.key:
diff --git a/lib/system/cgprocs.nim b/lib/system/cgprocs.nim
index e30cfa469..d483c61bd 100644
--- a/lib/system/cgprocs.nim
+++ b/lib/system/cgprocs.nim
@@ -9,7 +9,7 @@
 
 # Headers for procs that the code generator depends on ("compilerprocs")
 
-proc addChar(s: NimString, c: char): NimString {.compilerProc.}
+proc addChar(s: NimString, c: char): NimString {.compilerProc, gcsafe.}
 
 type
   TLibHandle = pointer       # private type
@@ -21,5 +21,5 @@ proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr {.compilerproc.}
 
 proc nimLoadLibraryError(path: string) {.compilerproc, noinline.}
 
-proc setStackBottom(theStackBottom: pointer) {.compilerRtl, noinline.}
+proc setStackBottom(theStackBottom: pointer) {.compilerRtl, noinline, gcsafe.}
 
diff --git a/lib/system/channels.nim b/lib/system/channels.nim
index bf949529b..e5535dbdc 100644
--- a/lib/system/channels.nim
+++ b/lib/system/channels.nim
@@ -1,7 +1,7 @@
 #

 #

 #            Nimrod's Runtime Library

-#        (c) Copyright 2012 Andreas Rumpf

+#        (c) Copyright 2014 Andreas Rumpf

 #

 #    See the file "copying.txt", included in this

 #    distribution, for details about the copyright.

@@ -29,7 +29,7 @@ type
     region: TMemRegion

   PRawChannel = ptr TRawChannel

   TLoadStoreMode = enum mStore, mLoad

-  TChannel*[TMsg] = TRawChannel ## a channel for thread communication

+  TChannel* {.gcsafe.}[TMsg] = TRawChannel ## a channel for thread communication

 

 const ChannelDeadMask = -2

 

@@ -49,9 +49,9 @@ proc deinitRawChannel(p: pointer) =
   deinitSysCond(c.cond)

 

 proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, 

-              mode: TLoadStoreMode)

+              mode: TLoadStoreMode) {.gcsafe.}

 proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,

-              mode: TLoadStoreMode) =

+              mode: TLoadStoreMode) {.gcsafe.} =

   var

     d = cast[TAddress](dest)

     s = cast[TAddress](src)

@@ -209,7 +209,6 @@ proc send*[TMsg](c: var TChannel[TMsg], msg: TMsg) =
 

 proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =

   # to save space, the generic is as small as possible

-  acquireSys(q.lock)

   q.ready = true

   while q.count <= 0:

     waitSysCond(q.cond, q.lock)

@@ -218,17 +217,29 @@ proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
     releaseSys(q.lock)

     sysFatal(EInvalidValue, "cannot receive message of wrong type")

   rawRecv(q, res, typ)

-  releaseSys(q.lock)

 

 proc recv*[TMsg](c: var TChannel[TMsg]): TMsg =

   ## receives a message from the channel `c`. This blocks until

   ## a message has arrived! You may use ``peek`` to avoid the blocking.

   var q = cast[PRawChannel](addr(c))

+  acquireSys(q.lock)

   llRecv(q, addr(result), cast[PNimType](getTypeInfo(result)))

+  releaseSys(q.lock)

+
+proc tryRecv*[TMsg](c: var TChannel[TMsg]): tuple[dataAvaliable: bool,
+                                                  msg: TMsg] =
+  ## try to receives a message from the channel `c` if available. Otherwise
+  ## it returns ``(false, default(msg))``.
+  var q = cast[PRawChannel](addr(c))

+  if q.mask != ChannelDeadMask:

+    lockChannel(q):

+      llRecv(q, addr(result.msg), cast[PNimType](getTypeInfo(result.msg)))
+      result.dataAvaliable = true

 

 proc peek*[TMsg](c: var TChannel[TMsg]): int =

   ## returns the current number of messages in the channel `c`. Returns -1

-  ## if the channel has been closed.

+  ## if the channel has been closed. **Note**: This is dangerous to use
+  ## as it encourages races. It's much better to use ``tryRecv`` instead.

   var q = cast[PRawChannel](addr(c))

   if q.mask != ChannelDeadMask:

     lockChannel(q):

diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index e50ba7b9f..612a9e729 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -11,7 +11,7 @@
 # use the heap (and nor exceptions) do not include the GC or memory allocator.
 
 var
-  errorMessageWriter*: (proc(msg: string) {.tags: [FWriteIO].})
+  errorMessageWriter*: (proc(msg: string) {.tags: [FWriteIO], gcsafe.})
     ## Function that will be called
     ## instead of stdmsg.write when printing stacktrace.
     ## Unstable API.
@@ -32,10 +32,10 @@ proc showErrorMessage(data: cstring) =
   else:
     writeToStdErr(data)
 
-proc chckIndx(i, a, b: int): int {.inline, compilerproc.}
-proc chckRange(i, a, b: int): int {.inline, compilerproc.}
-proc chckRangeF(x, a, b: float): float {.inline, compilerproc.}
-proc chckNil(p: pointer) {.noinline, compilerproc.}
+proc chckIndx(i, a, b: int): int {.inline, compilerproc, gcsafe.}
+proc chckRange(i, a, b: int): int {.inline, compilerproc, gcsafe.}
+proc chckRangeF(x, a, b: float): float {.inline, compilerproc, gcsafe.}
+proc chckNil(p: pointer) {.noinline, compilerproc, gcsafe.}
 
 var
   framePtr {.rtlThreadVar.}: PFrame
@@ -322,5 +322,5 @@ when not defined(noSignalHandler):
 
 proc setControlCHook(hook: proc () {.noconv.}) =
   # ugly cast, but should work on all architectures:
-  type TSignalHandler = proc (sig: cint) {.noconv.}
+  type TSignalHandler = proc (sig: cint) {.noconv, gcsafe.}
   c_signal(SIGINT, cast[TSignalHandler](hook))
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index ec1760914..3b85fe600 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -51,7 +51,7 @@ type
     waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack, 
     waCollectWhite,
 
-  TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall.}
+  TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, gcsafe.}
     # A ref type can have a finalizer that is called before the object's
     # storage is freed.
 
@@ -152,11 +152,11 @@ template gcTrace(cell, state: expr): stmt {.immediate.} =
   when traceGC: traceCell(cell, state)
 
 # forward declarations:
-proc collectCT(gch: var TGcHeap)
-proc isOnStack*(p: pointer): bool {.noinline.}
-proc forAllChildren(cell: PCell, op: TWalkOp)
-proc doOperation(p: pointer, op: TWalkOp)
-proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp)
+proc collectCT(gch: var TGcHeap) {.gcsafe.}
+proc isOnStack*(p: pointer): bool {.noinline, gcsafe.}
+proc forAllChildren(cell: PCell, op: TWalkOp) {.gcsafe.}
+proc doOperation(p: pointer, op: TWalkOp) {.gcsafe.}
+proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) {.gcsafe.}
 # we need the prototype here for debugging purposes
 
 when hasThreadSupport and hasSharedHeap:
@@ -294,7 +294,7 @@ proc initGC() =
 
 when useMarkForDebug or useBackupGc:
   type
-    TGlobalMarkerProc = proc () {.nimcall.}
+    TGlobalMarkerProc = proc () {.nimcall, gcsafe.}
   var
     globalMarkersLen: int
     globalMarkers: array[0.. 7_000, TGlobalMarkerProc]
@@ -311,7 +311,7 @@ proc cellsetReset(s: var TCellSet) =
   deinit(s)
   init(s)
 
-proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
+proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.gcsafe.} =
   var d = cast[TAddress](dest)
   case n.kind
   of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
@@ -680,10 +680,11 @@ proc doOperation(p: pointer, op: TWalkOp) =
 proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
   doOperation(d, TWalkOp(op))
 
-proc collectZCT(gch: var TGcHeap): bool
+proc collectZCT(gch: var TGcHeap): bool {.gcsafe.}
 
 when useMarkForDebug or useBackupGc:
-  proc markStackAndRegistersForSweep(gch: var TGcHeap) {.noinline, cdecl.}
+  proc markStackAndRegistersForSweep(gch: var TGcHeap) {.noinline, cdecl,
+                                                         gcsafe.}
 
 proc collectRoots(gch: var TGcHeap) =
   for s in elements(gch.cycleRoots):
diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim
index e78a4e5cd..3c99a57e1 100644
--- a/lib/system/gc_ms.nim
+++ b/lib/system/gc_ms.nim
@@ -32,11 +32,11 @@ type
                    # local 
     waMarkPrecise  # fast precise marking
 
-  TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall.}
+  TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, gcsafe.}
     # A ref type can have a finalizer that is called before the object's
     # storage is freed.
   
-  TGlobalMarkerProc = proc () {.nimcall.}
+  TGlobalMarkerProc = proc () {.nimcall, gcsafe.}
 
   TGcStat = object
     collections: int         # number of performed full collections
@@ -113,11 +113,11 @@ when BitsPerPage mod (sizeof(int)*8) != 0:
   {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
 
 # forward declarations:
-proc collectCT(gch: var TGcHeap)
-proc isOnStack*(p: pointer): bool {.noinline.}
-proc forAllChildren(cell: PCell, op: TWalkOp)
-proc doOperation(p: pointer, op: TWalkOp)
-proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp)
+proc collectCT(gch: var TGcHeap) {.gcsafe.}
+proc isOnStack*(p: pointer): bool {.noinline, gcsafe.}
+proc forAllChildren(cell: PCell, op: TWalkOp) {.gcsafe.}
+proc doOperation(p: pointer, op: TWalkOp) {.gcsafe.}
+proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) {.gcsafe.}
 # we need the prototype here for debugging purposes
 
 proc prepareDealloc(cell: PCell) =
@@ -150,7 +150,7 @@ proc initGC() =
       Init(gch.allocated)
       init(gch.marked)
 
-proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
+proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.gcsafe.} =
   var d = cast[TAddress](dest)
   case n.kind
   of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
diff --git a/lib/system/hti.nim b/lib/system/hti.nim
index 9d8ece7df..64174e60f 100644
--- a/lib/system/hti.nim
+++ b/lib/system/hti.nim
@@ -85,7 +85,7 @@ type
     base: ptr TNimType
     node: ptr TNimNode # valid for tyRecord, tyObject, tyTuple, tyEnum
     finalizer: pointer # the finalizer for the type
-    marker: proc (p: pointer, op: int) {.nimcall.} # marker proc for GC
+    marker: proc (p: pointer, op: int) {.nimcall, gcsafe.} # marker proc for GC
   PNimType = ptr TNimType
   
 # node.len may be the ``first`` element of a set
diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim
index 9831130e2..475a09686 100644
--- a/lib/system/inclrtl.nim
+++ b/lib/system/inclrtl.nim
@@ -16,6 +16,8 @@
 #    -> defined(useNimRtl) or appType == "lib" and not defined(createNimRtl)
 # 3) Exported into nimrtl.
 #    -> appType == "lib" and defined(createNimRtl)
+when not defined(nimNewShared):
+  {.pragma: gcsafe.}
 
 when defined(createNimRtl):
   when defined(useNimRtl): 
@@ -24,7 +26,7 @@ when defined(createNimRtl):
     {.error: "nimrtl must be built as a library!".}
 
 when defined(createNimRtl): 
-  {.pragma: rtl, exportc: "nimrtl_$1", dynlib.}
+  {.pragma: rtl, exportc: "nimrtl_$1", dynlib, gcsafe.}
   {.pragma: inl.}
   {.pragma: compilerRtl, compilerproc, exportc: "nimrtl_$1", dynlib.}
 elif defined(useNimRtl):
@@ -34,11 +36,11 @@ elif defined(useNimRtl):
     const nimrtl* = "nimrtl.dylib"
   else: 
     const nimrtl* = "libnimrtl.so"
-  {.pragma: rtl, importc: "nimrtl_$1", dynlib: nimrtl.}
+  {.pragma: rtl, importc: "nimrtl_$1", dynlib: nimrtl, gcsafe.}
   {.pragma: inl.}
   {.pragma: compilerRtl, compilerproc, importc: "nimrtl_$1", dynlib: nimrtl.}
 else:
-  {.pragma: rtl.}
+  {.pragma: rtl, gcsafe.}
   {.pragma: inl, inline.}
   {.pragma: compilerRtl, compilerproc.}
 
diff --git a/lib/system/repr.nim b/lib/system/repr.nim
index 7c1a68bc7..487bac052 100644
--- a/lib/system/repr.nim
+++ b/lib/system/repr.nim
@@ -10,7 +10,7 @@
 # The generic ``repr`` procedure. It is an invaluable debugging tool.
 
 when not defined(useNimRtl):
-  proc reprAny(p: pointer, typ: PNimType): string {.compilerRtl.}
+  proc reprAny(p: pointer, typ: PNimType): string {.compilerRtl, gcsafe.}
 
 proc reprInt(x: int64): string {.compilerproc.} = return $x
 proc reprFloat(x: float): string {.compilerproc.} = return $x
@@ -78,7 +78,7 @@ proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
 type
   PByteArray = ptr array[0.. 0xffff, int8]
 
-proc addSetElem(result: var string, elem: int, typ: PNimType) =
+proc addSetElem(result: var string, elem: int, typ: PNimType) {.gcsafe.} =
   case typ.kind
   of tyEnum: add result, reprEnum(elem, typ)
   of tyBool: add result, reprBool(bool(elem))
@@ -147,7 +147,7 @@ when not defined(useNimRtl):
     for i in 0..cl.indent-1: add result, ' '
 
   proc reprAux(result: var string, p: pointer, typ: PNimType,
-               cl: var TReprClosure)
+               cl: var TReprClosure) {.gcsafe.}
 
   proc reprArray(result: var string, p: pointer, typ: PNimType,
                  cl: var TReprClosure) =
@@ -172,7 +172,7 @@ when not defined(useNimRtl):
     add result, "]"
 
   proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode,
-                     cl: var TReprClosure) =
+                     cl: var TReprClosure) {.gcsafe.} =
     case n.kind
     of nkNone: sysAssert(false, "reprRecordAux")
     of nkSlot:
diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim
index 5e3b04b7f..b8ed29cfc 100644
--- a/lib/system/syslocks.nim
+++ b/lib/system/syslocks.nim
@@ -52,7 +52,7 @@ when defined(Windows):
   proc closeHandle(hObject: THandle) {.stdcall, noSideEffect,
     dynlib: "kernel32", importc: "CloseHandle".}
   proc waitForSingleObject(hHandle: THandle, dwMilliseconds: int32): int32 {.
-    stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
+    stdcall, dynlib: "kernel32", importc: "WaitForSingleObject", noSideEffect.}
 
   proc signalSysCond(hEvent: TSysCond) {.stdcall, noSideEffect,
     dynlib: "kernel32", importc: "SetEvent".}
@@ -89,16 +89,16 @@ else:
 
   proc releaseSys(L: var TSysLock) {.noSideEffect,
     importc: "pthread_mutex_unlock", header: "<pthread.h>".}
-  proc deinitSys(L: var TSysLock) {.
+  proc deinitSys(L: var TSysLock) {.noSideEffect,
     importc: "pthread_mutex_destroy", header: "<pthread.h>".}
 
   proc initSysCond(cond: var TSysCond, cond_attr: pointer = nil) {.
-    importc: "pthread_cond_init", header: "<pthread.h>".}
+    importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
   proc waitSysCond(cond: var TSysCond, lock: var TSysLock) {.
-    importc: "pthread_cond_wait", header: "<pthread.h>".}
+    importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
   proc signalSysCond(cond: var TSysCond) {.
-    importc: "pthread_cond_signal", header: "<pthread.h>".}
+    importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
   
-  proc deinitSysCond(cond: var TSysCond) {.
+  proc deinitSysCond(cond: var TSysCond) {.noSideEffect,
     importc: "pthread_cond_destroy", header: "<pthread.h>".}
   
diff --git a/lib/system/sysspawn.nim b/lib/system/sysspawn.nim
new file mode 100644
index 000000000..dabf35a3e
--- /dev/null
+++ b/lib/system/sysspawn.nim
@@ -0,0 +1,195 @@
+#
+#
+#            Nimrod's Runtime Library
+#        (c) Copyright 2014 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Implements Nimrod's 'spawn'.
+
+when not defined(NimString): 
+  {.error: "You must not import this module explicitly".}
+
+{.push stackTrace:off.}
+
+when (defined(x86) or defined(amd64)) and defined(gcc):
+  proc cpuRelax {.inline.} =
+    {.emit: """asm volatile("pause" ::: "memory");""".}
+elif (defined(x86) or defined(amd64)) and defined(vcc):
+  proc cpuRelax {.importc: "YieldProcessor", header: "<windows.h>".}
+elif defined(intelc):
+  proc cpuRelax {.importc: "_mm_pause", header: "xmmintrin.h".}
+elif false:
+  from os import sleep
+
+  proc cpuRelax {.inline.} = os.sleep(1)
+
+when defined(windows) and not defined(gcc):
+  proc interlockedCompareExchange(p: pointer; exchange, comparand: int32): int32
+    {.importc: "InterlockedCompareExchange", header: "<windows.h>", cdecl.}
+
+  proc cas(p: ptr bool; oldValue, newValue: bool): bool =
+    interlockedCompareExchange(p, newValue.int32, oldValue.int32) != 0
+
+else:
+  # this is valid for GCC and Intel C++
+  proc cas(p: ptr bool; oldValue, newValue: bool): bool
+    {.importc: "__sync_bool_compare_and_swap", nodecl.}
+
+# We declare our own condition variables here to get rid of the dummy lock
+# on Windows:
+
+type
+  CondVar = object
+    c: TSysCond
+    when defined(posix):
+      stupidLock: TSysLock
+      counter: int
+
+proc createCondVar(): CondVar =
+  initSysCond(result.c)
+  when defined(posix):
+    initSysLock(result.stupidLock)
+    #acquireSys(result.stupidLock)
+
+proc await(cv: var CondVar) =
+  when defined(posix):
+    acquireSys(cv.stupidLock)
+    while cv.counter <= 0:
+      waitSysCond(cv.c, cv.stupidLock)
+    dec cv.counter
+    releaseSys(cv.stupidLock)
+  else:
+    waitSysCondWindows(cv.c)
+
+proc signal(cv: var CondVar) =
+  when defined(posix):
+    acquireSys(cv.stupidLock)
+    inc cv.counter
+    releaseSys(cv.stupidLock)
+  signalSysCond(cv.c)
+
+type
+  FastCondVar = object
+    event, slowPath: bool
+    slow: CondVar
+
+proc createFastCondVar(): FastCondVar =
+  initSysCond(result.slow.c)
+  when defined(posix):
+    initSysLock(result.slow.stupidLock)
+    #acquireSys(result.slow.stupidLock)
+  result.event = false
+  result.slowPath = false
+
+proc await(cv: var FastCondVar) =
+  #for i in 0 .. 50:
+  #  if cas(addr cv.event, true, false):
+  #    # this is a HIT: Triggers > 95% in my tests.
+  #    return
+  #  cpuRelax()
+  #cv.slowPath = true
+  # XXX For some reason this crashes some test programs
+  await(cv.slow)
+  cv.event = false
+
+proc signal(cv: var FastCondVar) =
+  cv.event = true
+  #if cas(addr cv.slowPath, true, false):
+  signal(cv.slow)
+
+{.pop.}
+
+# ----------------------------------------------------------------------------
+
+type
+  WorkerProc = proc (thread, args: pointer) {.nimcall, gcsafe.}
+  Worker = object
+    taskArrived: CondVar
+    taskStarted: FastCondVar #\
+    # task data:
+    f: WorkerProc
+    data: pointer
+    ready: bool # put it here for correct alignment!
+
+proc nimArgsPassingDone(p: pointer) {.compilerProc.} =
+  let w = cast[ptr Worker](p)
+  signal(w.taskStarted)
+
+var gSomeReady = createFastCondVar()
+
+proc slave(w: ptr Worker) {.thread.} =
+  while true:
+    w.ready = true # If we instead signal "workerReady" we need the scheduler
+                   # to notice this. The scheduler could then optimize the
+                   # layout of the worker threads (e.g. keep the list sorted)
+                   # so that no search for a "ready" thread is necessary.
+                   # This might be implemented later, but is more tricky than
+                   # it looks because 'spawn' itself can run concurrently.
+    signal(gSomeReady)
+    await(w.taskArrived)
+    assert(not w.ready)
+    # shield against spurious wakeups:
+    if w.data != nil:
+      w.f(w, w.data)
+      w.data = nil
+
+const NumThreads = 4
+
+var
+  workers: array[NumThreads, TThread[ptr Worker]]
+  workersData: array[NumThreads, Worker]
+
+proc setup() =
+  for i in 0.. <NumThreads:
+    workersData[i].taskArrived = createCondVar()
+    workersData[i].taskStarted = createFastCondVar()
+    createThread(workers[i], slave, addr(workersData[i]))
+
+proc preferSpawn*(): bool =
+  ## Use this proc to determine quickly if a 'spawn' or a direct call is
+  ## preferable. If it returns 'true' a 'spawn' may make sense. In general
+  ## it is not necessary to call this directly; use 'spawnX' instead.
+  result = gSomeReady.event
+
+proc spawn*(call: stmt) {.magic: "Spawn".}
+  ## always spawns a new task, so that the 'call' is never executed on
+  ## the calling thread. 'call' has to be proc call 'p(...)' where 'p'
+  ## is gcsafe and has 'void' as the return type.
+
+template spawnX*(call: stmt) =
+  ## spawns a new task if a CPU core is ready, otherwise executes the
+  ## call in the calling thread. Usually it is advised to
+  ## use 'spawn' in order to not block the producer for an unknown
+  ## amount of time. 'call' has to be proc call 'p(...)' where 'p'
+  ## is gcsafe and has 'void' as the return type.
+  if preferSpawn(): spawn call
+  else: call
+
+proc nimSpawn(fn: WorkerProc; data: pointer) {.compilerProc.} =
+  # implementation of 'spawn' that is used by the code generator.
+  while true:
+    for i in 0.. high(workers):
+      let w = addr(workersData[i])
+      if cas(addr w.ready, true, false):
+        w.data = data
+        w.f = fn
+        signal(w.taskArrived)
+        await(w.taskStarted)
+        return
+    await(gSomeReady)
+
+proc sync*() =
+  ## a simple barrier to wait for all spawn'ed tasks. If you need more elaborate
+  ## waiting, you have to use an explicit barrier.
+  while true:
+    var allReady = true
+    for i in 0 .. high(workers):
+      if not allReady: break
+      allReady = allReady and workersData[i].ready
+    if allReady: break
+    await(gSomeReady)
+
+setup()
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index eb9d2000b..4244bae4c 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -252,10 +252,8 @@ proc nimIntToStr(x: int): string {.compilerRtl.} =
 
 proc nimFloatToStr(x: float): string {.compilerproc.} =
   var buf: array [0..59, char]
-  c_sprintf(buf, "%#.f", x)
-  result = $buf
-  if result[len(result)-1] == '.':
-    result.add("0")
+  c_sprintf(buf, "%#.16e", x)
+  return $buf
 
 proc nimInt64ToStr(x: int64): string {.compilerRtl.} =
   result = newString(sizeof(x)*4)
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index ff9ab6cc0..0d52e4d09 100644
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -256,9 +256,9 @@ type
                           ## that **must not** be part of a message! Use
                           ## a ``TThreadId`` for that.
     when TArg is void:
-      dataFn: proc () {.nimcall.}
+      dataFn: proc () {.nimcall, gcsafe.}
     else:
-      dataFn: proc (m: TArg) {.nimcall.}
+      dataFn: proc (m: TArg) {.nimcall, gcsafe.}
       data: TArg
   TThreadId*[TArg] = ptr TThread[TArg] ## the current implementation uses
                                        ## a pointer as a thread ID.
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index 3449a3eba..4ce2f11b4 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -522,7 +522,7 @@ proc inet_addr*(cp: cstring): int32 {.
   stdcall, importc: "inet_addr", dynlib: ws2dll.} 
 
 proc WSAFDIsSet(s: TSocketHandle, FDSet: var TFdSet): bool {.
-  stdcall, importc: "__WSAFDIsSet", dynlib: ws2dll.}
+  stdcall, importc: "__WSAFDIsSet", dynlib: ws2dll, noSideEffect.}
 
 proc FD_ISSET*(Socket: TSocketHandle, FDSet: var TFdSet): cint = 
   result = if WSAFDIsSet(Socket, FDSet): 1'i32 else: 0'i32
@@ -718,4 +718,4 @@ proc WSASend*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
   stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".}
 
 proc get_osfhandle*(fd:TFileHandle): THandle {.
-  importc:"_get_osfhandle", header:"<io.h>".}
\ No newline at end of file
+  importc:"_get_osfhandle", header:"<io.h>".}
diff --git a/lib/wrappers/sqlite3.nim b/lib/wrappers/sqlite3.nim
index 8ff1da1d1..586f763ae 100644
--- a/lib/wrappers/sqlite3.nim
+++ b/lib/wrappers/sqlite3.nim
@@ -90,6 +90,7 @@ const
   SQLITE_IGNORE* = 2          # Original from sqlite3.h: 
                               ##define SQLITE_STATIC      ((void(*)(void *))0)
                               ##define SQLITE_TRANSIENT   ((void(*)(void *))-1)
+  SQLITE_DETERMINISTIC* = 0x800
 
 const 
   SQLITE_STATIC* = nil