From bdf6f59c6d4864d6436f6c517ebab5572d2b937a Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Sat, 15 Jul 2017 09:50:41 +0300 Subject: [PATCH] Fixes #5738 (#6059) --- lib/pure/asyncmacro.nim | 10 ++++++--- tests/async/tasync_gcsafe.nim | 36 +++++++++++++++++++++++++++++++++ tests/async/tasync_gcunsafe.nim | 30 +++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 tests/async/tasync_gcsafe.nim create mode 100644 tests/async/tasync_gcunsafe.nim diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 89b216b25d1c..82226182adf3 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -28,7 +28,7 @@ template createCb(retFutureSym, iteratorNameSym, name, futureVarCompletions: untyped) = var nameIterVar = iteratorNameSym #{.push stackTrace: off.} - proc cb {.closure,gcsafe.} = + proc cb {.closure.} = try: if not nameIterVar.finished: var next = nameIterVar() @@ -38,7 +38,8 @@ template createCb(retFutureSym, iteratorNameSym, "`nil` Future?" raise newException(AssertionError, msg % name) else: - next.callback = cb + {.gcsafe.}: + next.callback = (proc() {.closure, gcsafe.})(cb) except: futureVarCompletions @@ -379,7 +380,10 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = procBody, nnkIteratorDef) closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body) closureIterator.addPragma(newIdentNode("closure")) - closureIterator.addPragma(newIdentNode("gcsafe")) + + # If proc has an explicit gcsafe pragma, we add it to iterator as well. + if prc.pragma.findChild(it.kind in {nnkSym, nnkIdent} and $it == "gcsafe") != nil: + closureIterator.addPragma(newIdentNode("gcsafe")) outerProcBody.add(closureIterator) # -> createCb(retFuture) diff --git a/tests/async/tasync_gcsafe.nim b/tests/async/tasync_gcsafe.nim new file mode 100644 index 000000000000..89df6456a7da --- /dev/null +++ b/tests/async/tasync_gcsafe.nim @@ -0,0 +1,36 @@ +discard """ + cmd: "nim c --threads:on $file" + output: ''' +1 +2 +3 +''' +""" + +assert compileOption("threads"), "this test will not do anything useful without --threads:on" + +import asyncdispatch + +var globalDummy: ref int +proc gcUnsafeProc() = + if not globalDummy.isNil: + echo globalDummy[] + echo "1" + +proc gcSafeAsyncProcWithNoAnnotation() {.async.} = + echo "2" + +proc gcSafeAsyncProcWithAnnotation() {.gcsafe, async.} = + echo "3" + +proc gcUnsafeAsyncProc() {.async.} = + # We should be able to call gcUnsafe + gcUnsafeProc() + + # We should be able to call async implicitly gcsafe + await gcSafeAsyncProcWithNoAnnotation() + + # We should be able to call async explicitly gcsafe + await gcSafeAsyncProcWithAnnotation() + +waitFor gcUnsafeAsyncProc() diff --git a/tests/async/tasync_gcunsafe.nim b/tests/async/tasync_gcunsafe.nim new file mode 100644 index 000000000000..f4e2cdcf24a9 --- /dev/null +++ b/tests/async/tasync_gcunsafe.nim @@ -0,0 +1,30 @@ +discard """ + cmd: "nim c --threads:on $file" + file: "asyncmacro.nim" + errormsg: "'anotherGCSafeAsyncProcIter' is not GC-safe as it calls 'asyncGCUnsafeProc'" +""" + +assert compileOption("threads"), "this test will not do anything useful without --threads:on" + +import asyncdispatch + +var globalDummy: ref int +proc gcUnsafeProc() = + if not globalDummy.isNil: + echo globalDummy[] + +proc asyncExplicitlyGCSafeProc() {.gcsafe, async.} = + echo "hi" + +proc asyncImplicitlyGCSafeProc() {.async.} = + echo "hi" + +proc asyncGCUnsafeProc() {.async.} = + gcUnsafeProc() + +proc anotherGCSafeAsyncProc() {.async, gcsafe.} = + # We should be able to call other gcsafe procs + await asyncExplicitlyGCSafeProc() + await asyncImplicitlyGCSafeProc() + # But we can't call gcunsafe procs + await asyncGCUnsafeProc()