addQuitProc now works with closures, and c, js(node/browser) backend; fix some bugs in testament (#14342)
* make addQuitProc great again * fix bugs in testament * fix test * change 2016 => 2020 * addQuitProc => addExitProc + locks * move to std/exitprocs
This commit is contained in:
parent
45cac4afda
commit
dfe51d10a1
9 changed files with 117 additions and 44 deletions
|
|
@ -9,6 +9,10 @@
|
||||||
|
|
||||||
## This module contains Nim's support for locks and condition vars.
|
## This module contains Nim's support for locks and condition vars.
|
||||||
|
|
||||||
|
#[
|
||||||
|
for js, for now we treat locks as noop's to avoid pushing `when defined(js)`
|
||||||
|
in client code that uses locks.
|
||||||
|
]#
|
||||||
|
|
||||||
when not compileOption("threads") and not defined(nimdoc):
|
when not compileOption("threads") and not defined(nimdoc):
|
||||||
when false: # fix #12330
|
when false: # fix #12330
|
||||||
|
|
@ -26,7 +30,8 @@ type
|
||||||
|
|
||||||
proc initLock*(lock: var Lock) {.inline.} =
|
proc initLock*(lock: var Lock) {.inline.} =
|
||||||
## Initializes the given lock.
|
## Initializes the given lock.
|
||||||
initSysLock(lock)
|
when not defined(js):
|
||||||
|
initSysLock(lock)
|
||||||
|
|
||||||
proc deinitLock*(lock: var Lock) {.inline.} =
|
proc deinitLock*(lock: var Lock) {.inline.} =
|
||||||
## Frees the resources associated with the lock.
|
## Frees the resources associated with the lock.
|
||||||
|
|
@ -38,11 +43,13 @@ proc tryAcquire*(lock: var Lock): bool =
|
||||||
|
|
||||||
proc acquire*(lock: var Lock) =
|
proc acquire*(lock: var Lock) =
|
||||||
## Acquires the given lock.
|
## Acquires the given lock.
|
||||||
acquireSys(lock)
|
when not defined(js):
|
||||||
|
acquireSys(lock)
|
||||||
|
|
||||||
proc release*(lock: var Lock) =
|
proc release*(lock: var Lock) =
|
||||||
## Releases the given lock.
|
## Releases the given lock.
|
||||||
releaseSys(lock)
|
when not defined(js):
|
||||||
|
releaseSys(lock)
|
||||||
|
|
||||||
|
|
||||||
proc initCond*(cond: var Cond) {.inline.} =
|
proc initCond*(cond: var Cond) {.inline.} =
|
||||||
|
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nim's Runtime Library
|
|
||||||
# (c) Copyright 2016 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## ``system.addQuitProc`` is nice and very useful but due to its C-based
|
|
||||||
## implementation it doesn't support closures which limits its usefulness.
|
|
||||||
## This module fixes this. Later versions of this module will also
|
|
||||||
## support the JavaScript backend.
|
|
||||||
|
|
||||||
var
|
|
||||||
gClosures: seq[proc () {.closure.}]
|
|
||||||
|
|
||||||
proc callClosures() {.noconv.} =
|
|
||||||
for i in countdown(gClosures.len-1, 0):
|
|
||||||
gClosures[i]()
|
|
||||||
|
|
||||||
proc addQuitClosure*(cl: proc () {.closure.}) =
|
|
||||||
## Like ``system.addQuitProc`` but it supports closures.
|
|
||||||
if gClosures.len == 0:
|
|
||||||
addQuitProc(callClosures)
|
|
||||||
gClosures = @[cl]
|
|
||||||
else:
|
|
||||||
gClosures.add(cl)
|
|
||||||
65
lib/std/exitprocs.nim
Normal file
65
lib/std/exitprocs.nim
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nim's Runtime Library
|
||||||
|
# (c) Copyright 2020 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
import locks
|
||||||
|
|
||||||
|
type
|
||||||
|
FunKind = enum kClosure, kNoconv # extend as needed
|
||||||
|
Fun = object
|
||||||
|
case kind: FunKind
|
||||||
|
of kClosure: fun1: proc () {.closure.}
|
||||||
|
of kNoconv: fun2: proc () {.noconv.}
|
||||||
|
|
||||||
|
var
|
||||||
|
gFunsLock: Lock
|
||||||
|
gFuns: seq[Fun]
|
||||||
|
|
||||||
|
initLock(gFunsLock)
|
||||||
|
|
||||||
|
when defined(js):
|
||||||
|
proc addAtExit(quitProc: proc() {.noconv.}) =
|
||||||
|
when defined(nodejs):
|
||||||
|
asm """
|
||||||
|
process.on('exit', `quitProc`);
|
||||||
|
"""
|
||||||
|
elif defined(js):
|
||||||
|
asm """
|
||||||
|
window.onbeforeunload = `quitProc`;
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
proc addAtExit(quitProc: proc() {.noconv.}) {.
|
||||||
|
importc: "atexit", header: "<stdlib.h>".}
|
||||||
|
|
||||||
|
proc callClosures() {.noconv.} =
|
||||||
|
withLock gFunsLock:
|
||||||
|
for i in countdown(gFuns.len-1, 0):
|
||||||
|
let fun = gFuns[i]
|
||||||
|
case fun.kind
|
||||||
|
of kClosure: fun.fun1()
|
||||||
|
of kNoconv: fun.fun2()
|
||||||
|
|
||||||
|
template fun() =
|
||||||
|
if gFuns.len == 0:
|
||||||
|
addAtExit(callClosures)
|
||||||
|
|
||||||
|
proc addExitProc*(cl: proc () {.closure.}) =
|
||||||
|
## Adds/registers a quit procedure. Each call to `addExitProc` registers
|
||||||
|
## another quit procedure. They are executed on a last-in, first-out basis.
|
||||||
|
# Support for `addExitProc` is done by Ansi C's facilities here.
|
||||||
|
# In case of an unhandled exception the exit handlers should
|
||||||
|
# not be called explicitly! The user may decide to do this manually though.
|
||||||
|
withLock gFunsLock:
|
||||||
|
fun()
|
||||||
|
gFuns.add Fun(kind: kClosure, fun1: cl)
|
||||||
|
|
||||||
|
proc addExitProc*(cl: proc() {.noconv.}) =
|
||||||
|
## overload for `noconv` procs.
|
||||||
|
withLock gFunsLock:
|
||||||
|
fun()
|
||||||
|
gFuns.add Fun(kind: kNoconv, fun2: cl)
|
||||||
|
|
@ -1147,8 +1147,8 @@ when defined(nimdoc):
|
||||||
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}
|
proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}
|
||||||
## Stops the program immediately with an exit code.
|
## Stops the program immediately with an exit code.
|
||||||
##
|
##
|
||||||
## Before stopping the program the "quit procedures" are called in the
|
## Before stopping the program the "exit procedures" are called in the
|
||||||
## opposite order they were added with `addQuitProc <#addQuitProc,proc>`_.
|
## opposite order they were added with `addExitProc <exitprocs.html#addExitProc,proc>`_.
|
||||||
## ``quit`` never returns and ignores any exception that may have been raised
|
## ``quit`` never returns and ignores any exception that may have been raised
|
||||||
## by the quit procedures. It does *not* call the garbage collector to free
|
## by the quit procedures. It does *not* call the garbage collector to free
|
||||||
## all the memory, unless a quit procedure calls `GC_fullCollect
|
## all the memory, unless a quit procedure calls `GC_fullCollect
|
||||||
|
|
@ -1427,7 +1427,7 @@ proc toBiggestInt*(f: BiggestFloat): BiggestInt {.noSideEffect.} =
|
||||||
if f >= 0: BiggestInt(f+0.5) else: BiggestInt(f-0.5)
|
if f >= 0: BiggestInt(f+0.5) else: BiggestInt(f-0.5)
|
||||||
|
|
||||||
proc addQuitProc*(quitProc: proc() {.noconv.}) {.
|
proc addQuitProc*(quitProc: proc() {.noconv.}) {.
|
||||||
importc: "atexit", header: "<stdlib.h>".}
|
importc: "atexit", header: "<stdlib.h>", deprecated: "use exitprocs.addExitProc".}
|
||||||
## Adds/registers a quit procedure.
|
## Adds/registers a quit procedure.
|
||||||
##
|
##
|
||||||
## Each call to ``addQuitProc`` registers another quit procedure. Up to 30
|
## Each call to ``addQuitProc`` registers another quit procedure. Up to 30
|
||||||
|
|
@ -1440,7 +1440,6 @@ proc addQuitProc*(quitProc: proc() {.noconv.}) {.
|
||||||
# In case of an unhandled exception the exit handlers should
|
# In case of an unhandled exception the exit handlers should
|
||||||
# not be called explicitly! The user may decide to do this manually though.
|
# not be called explicitly! The user may decide to do this manually though.
|
||||||
|
|
||||||
|
|
||||||
proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
|
proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
|
||||||
## Swaps the values `a` and `b`.
|
## Swaps the values `a` and `b`.
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -269,8 +269,8 @@ proc debuggerTests(r: var TResults, cat: Category, options: string) =
|
||||||
|
|
||||||
proc jsTests(r: var TResults, cat: Category, options: string) =
|
proc jsTests(r: var TResults, cat: Category, options: string) =
|
||||||
template test(filename: untyped) =
|
template test(filename: untyped) =
|
||||||
testSpec r, makeTest(filename, options & " -d:nodejs", cat), {targetJS}
|
testSpec r, makeTest(filename, options, cat), {targetJS}
|
||||||
testSpec r, makeTest(filename, options & " -d:nodejs -d:release", cat), {targetJS}
|
testSpec r, makeTest(filename, options & " -d:release", cat), {targetJS}
|
||||||
|
|
||||||
for t in os.walkFiles("tests/js/t*.nim"):
|
for t in os.walkFiles("tests/js/t*.nim"):
|
||||||
test(t)
|
test(t)
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,13 @@ const
|
||||||
targetToExt*: array[TTarget, string] = ["nim.c", "nim.cpp", "nim.m", "js"]
|
targetToExt*: array[TTarget, string] = ["nim.c", "nim.cpp", "nim.m", "js"]
|
||||||
targetToCmd*: array[TTarget, string] = ["c", "cpp", "objc", "js"]
|
targetToCmd*: array[TTarget, string] = ["c", "cpp", "objc", "js"]
|
||||||
|
|
||||||
|
proc defaultOptions*(a: TTarget): string =
|
||||||
|
case a
|
||||||
|
of targetJS: "-d:nodejs"
|
||||||
|
# once we start testing for `nim js -d:nimbrowser` (eg selenium or similar),
|
||||||
|
# we can adapt this logic; or a given js test can override with `-u:nodejs`.
|
||||||
|
else: ""
|
||||||
|
|
||||||
when not declared(parseCfgBool):
|
when not declared(parseCfgBool):
|
||||||
# candidate for the stdlib:
|
# candidate for the stdlib:
|
||||||
proc parseCfgBool(s: string): bool =
|
proc parseCfgBool(s: string): bool =
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,10 @@ proc nimcacheDir(filename, options: string, target: TTarget): string =
|
||||||
|
|
||||||
proc prepareTestArgs(cmdTemplate, filename, options, nimcache: string,
|
proc prepareTestArgs(cmdTemplate, filename, options, nimcache: string,
|
||||||
target: TTarget, extraOptions = ""): seq[string] =
|
target: TTarget, extraOptions = ""): seq[string] =
|
||||||
let options = options & " " & quoteShell("--nimCache:" & nimcache) & " " & extraOptions
|
var options = target.defaultOptions & " " & options
|
||||||
|
# improve pending https://github.com/nim-lang/Nim/issues/14343
|
||||||
|
if nimcache.len > 0: options.add " " & ("--nimCache:" & nimcache).quoteShell
|
||||||
|
options.add " " & extraOptions
|
||||||
result = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
|
result = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
|
||||||
"options", options, "file", filename.quoteShell,
|
"options", options, "file", filename.quoteShell,
|
||||||
"filedir", filename.getFileDir()])
|
"filedir", filename.getFileDir()])
|
||||||
|
|
@ -192,9 +195,7 @@ proc callCompiler(cmdTemplate, filename, options, nimcache: string,
|
||||||
|
|
||||||
proc callCCompiler(cmdTemplate, filename, options: string,
|
proc callCCompiler(cmdTemplate, filename, options: string,
|
||||||
target: TTarget): TSpec =
|
target: TTarget): TSpec =
|
||||||
let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target],
|
let c = prepareTestArgs(cmdTemplate, filename, options, nimcache = "", target)
|
||||||
"options", options, "file", filename.quoteShell,
|
|
||||||
"filedir", filename.getFileDir()])
|
|
||||||
var p = startProcess(command="gcc", args=c[5 .. ^1],
|
var p = startProcess(command="gcc", args=c[5 .. ^1],
|
||||||
options={poStdErrToStdOut, poUsePath})
|
options={poStdErrToStdOut, poUsePath})
|
||||||
let outp = p.outputStream
|
let outp = p.outputStream
|
||||||
|
|
@ -254,7 +255,8 @@ proc addResult(r: var TResults, test: TTest, target: TTarget,
|
||||||
# test.name is easier to find than test.name.extractFilename
|
# test.name is easier to find than test.name.extractFilename
|
||||||
# A bit hacky but simple and works with tests/testament/tshould_not_work.nim
|
# A bit hacky but simple and works with tests/testament/tshould_not_work.nim
|
||||||
var name = test.name.replace(DirSep, '/')
|
var name = test.name.replace(DirSep, '/')
|
||||||
name.add " " & $target & test.options
|
name.add " " & $target
|
||||||
|
if test.options.len > 0: name.add " " & test.options
|
||||||
|
|
||||||
let duration = epochTime() - test.startTime
|
let duration = epochTime() - test.startTime
|
||||||
let success = if test.spec.timeout > 0.0 and duration > test.spec.timeout: reTimeout
|
let success = if test.spec.timeout > 0.0 and duration > test.spec.timeout: reTimeout
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ test'''
|
||||||
"""
|
"""
|
||||||
# bug #4537
|
# bug #4537
|
||||||
|
|
||||||
# nim js --d:nodejs
|
# nim js -d:nodejs
|
||||||
|
|
||||||
type
|
type
|
||||||
Str = distinct string
|
Str = distinct string
|
||||||
|
|
|
||||||
21
tests/stdlib/texitprocs.nim
Normal file
21
tests/stdlib/texitprocs.nim
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
discard """
|
||||||
|
targets: "c cpp js"
|
||||||
|
output: '''
|
||||||
|
ok4
|
||||||
|
ok3
|
||||||
|
ok2
|
||||||
|
ok1
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
import std/exitprocs
|
||||||
|
|
||||||
|
proc fun1() {.noconv.} = echo "ok1"
|
||||||
|
proc fun2() = echo "ok2"
|
||||||
|
proc fun3() {.noconv.} = echo "ok3"
|
||||||
|
proc fun4() = echo "ok4"
|
||||||
|
|
||||||
|
addExitProc(fun1)
|
||||||
|
addExitProc(fun2)
|
||||||
|
addExitProc(fun3)
|
||||||
|
addExitProc(fun4)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue