From 4baaea5ad52f5a80b2ca5592d2950ed28f7c49dc Mon Sep 17 00:00:00 2001 From: Reimer Behrends Date: Sun, 6 Sep 2015 01:48:31 +0200 Subject: [PATCH 1/5] Properly initialize the Boehm GC on all platforms. --- lib/system/mmdisp.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 8a946716d6..bf006f68a7 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -157,7 +157,7 @@ when defined(boehmgc): proc setStackBottom(theStackBottom: pointer) = discard proc initGC() = - when defined(macosx): boehmGCinit() + boehmGCinit() proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = if ntfNoRefs in typ.flags: result = allocAtomic(size) From 639b5e006992779e8f984eb4a2b1509e4ad2b03a Mon Sep 17 00:00:00 2001 From: Reimer Behrends Date: Tue, 8 Sep 2015 19:01:06 +0200 Subject: [PATCH 2/5] Properly register threads with the Boehm GC. In order to be able to scan thread stacks, the Boehm GC needs to know about newly created threads. We establish the end of the stack by using GC_call_with_stack_base (this works properly also with the dual-stack Itanium architecture) and then GC_register_my_thread() to register a thrad and GC_unregister_my_thread() to unregister it again. This patch also includes a modification for the refc and markandsweep collectors to set the stack bottom for thread stacks correctly even if an optimizer aggressively inlines and optimizes procedures (this is already being done for the stack of the main thread). Finally, we use the {.noconv.} pragma for the Boehm GC, as the Boehm API uses no specific calling convention. --- lib/system.nim | 9 +++++++ lib/system/mmdisp.nim | 41 +++++++++++++--------------- lib/system/threads.nim | 61 +++++++++++++++++++++++++++++++----------- 3 files changed, 73 insertions(+), 38 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 3d7d4bd284..e0bfbe8eaa 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1200,6 +1200,15 @@ const hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own taintMode = compileOption("taintmode") +when defined(boehmgc): + when defined(windows): + const boehmLib = "boehmgc.dll" + elif defined(macosx): + const boehmLib = "libgc.dylib" + else: + const boehmLib = "libgc.so.1" + {.pragma: boehmGC, noconv, dynlib: boehmLib.} + when taintMode: type TaintedString* = distinct string ## a distinct string type that ## is `tainted`:idx:. It is an alias for diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index bf006f68a7..ecf25ceeeb 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -66,41 +66,34 @@ proc raiseOutOfMem() {.noinline.} = quit(1) when defined(boehmgc): - when defined(windows): - const boehmLib = "boehmgc.dll" - elif defined(macosx): - const boehmLib = "libgc.dylib" - else: - const boehmLib = "libgc.so.1" - - proc boehmGCinit {.importc: "GC_init", dynlib: boehmLib.} - proc boehmGC_disable {.importc: "GC_disable", dynlib: boehmLib.} - proc boehmGC_enable {.importc: "GC_enable", dynlib: boehmLib.} + proc boehmGCinit {.importc: "GC_init", boehmGC.} + proc boehmGC_disable {.importc: "GC_disable", boehmGC.} + proc boehmGC_enable {.importc: "GC_enable", boehmGC.} proc boehmGCincremental {. - importc: "GC_enable_incremental", dynlib: boehmLib.} - proc boehmGCfullCollect {.importc: "GC_gcollect", dynlib: boehmLib.} - proc boehmAlloc(size: int): pointer {. - importc: "GC_malloc", dynlib: boehmLib.} + importc: "GC_enable_incremental", boehmGC.} + proc boehmGCfullCollect {.importc: "GC_gcollect", boehmGC.} + proc boehmAlloc(size: int): pointer {.importc: "GC_malloc", boehmGC.} proc boehmAllocAtomic(size: int): pointer {. - importc: "GC_malloc_atomic", dynlib: boehmLib.} + importc: "GC_malloc_atomic", boehmGC.} proc boehmRealloc(p: pointer, size: int): pointer {. - importc: "GC_realloc", dynlib: boehmLib.} - proc boehmDealloc(p: pointer) {.importc: "GC_free", dynlib: boehmLib.} + importc: "GC_realloc", boehmGC.} + proc boehmDealloc(p: pointer) {.importc: "GC_free", boehmGC.} + when hasThreadSupport: + proc boehmGC_allow_register_threads {. + importc: "GC_allow_register_threads", boehmGC.} - proc boehmGetHeapSize: int {.importc: "GC_get_heap_size", dynlib: boehmLib.} + proc boehmGetHeapSize: int {.importc: "GC_get_heap_size", boehmGC.} ## Return the number of bytes in the heap. Excludes collector private ## data structures. Includes empty blocks and fragmentation loss. ## Includes some pages that were allocated but never written. - proc boehmGetFreeBytes: int {.importc: "GC_get_free_bytes", dynlib: boehmLib.} + proc boehmGetFreeBytes: int {.importc: "GC_get_free_bytes", boehmGC.} ## Return a lower bound on the number of free bytes in the heap. - proc boehmGetBytesSinceGC: int {.importc: "GC_get_bytes_since_gc", - dynlib: boehmLib.} + proc boehmGetBytesSinceGC: int {.importc: "GC_get_bytes_since_gc", boehmGC.} ## Return the number of bytes allocated since the last collection. - proc boehmGetTotalBytes: int {.importc: "GC_get_total_bytes", - dynlib: boehmLib.} + proc boehmGetTotalBytes: int {.importc: "GC_get_total_bytes", boehmGC.} ## Return the total number of bytes allocated in this process. ## Never decreases. @@ -158,6 +151,8 @@ when defined(boehmgc): proc initGC() = boehmGCinit() + when hasThreadSupport: + boehmGC_allow_register_threads() proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = if ntfNoRefs in typ.flags: result = allocAtomic(size) diff --git a/lib/system/threads.nim b/lib/system/threads.nim index c7cb8d9df8..c5de841f88 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -304,22 +304,53 @@ type when not defined(boehmgc) and not hasSharedHeap and not defined(gogc): proc deallocOsPages() +when defined(boehmgc): + type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.} + proc boehmGC_call_with_stack_base(sbp: GCStackBaseProc, p: pointer) + {.importc: "GC_call_with_stack_base", boehmGC.} + proc boehmGC_register_my_thread(sb: pointer) + {.importc: "GC_register_my_thread", boehmGC.} + proc boehmGC_unregister_my_thread() + {.importc: "GC_unregister_my_thread", boehmGC.} + + proc threadProcWrapDispatch[TArg](sb: pointer, thrd: pointer) {.noconv.} = + boehmGC_register_my_thread(sb) + let thrd = cast[ptr Thread[TArg]](thrd) + when TArg is void: + thrd.dataFn() + else: + thrd.dataFn(thrd.data) + boehmGC_unregister_my_thread() +else: + proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) = + when TArg is void: + thrd.dataFn() + else: + thrd.dataFn(thrd.data) + +proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) = + when defined(boehmgc): + boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd) + elif not defined(nogc) and not defined(gogc): + var p {.volatile.}: proc(a: ptr Thread[TArg]) {.nimcall.} = + threadProcWrapDispatch[TArg] + when not hasSharedHeap: + # init the GC for refc/markandsweep + setStackBottom(addr(p)) + initGC() + when declared(registerThread): + thrd.stackBottom = addr(thrd) + registerThread(thrd) + p(thrd) + when declared(registerThread): unregisterThread(thrd) + when declared(deallocOsPages): deallocOsPages() + else: + threadProcWrapDispatch(thrd) + template threadProcWrapperBody(closure: expr) {.immediate.} = when declared(globalsSlot): threadVarSetValue(globalsSlot, closure) - var t = cast[ptr Thread[TArg]](closure) - when useStackMaskHack: - var tls: ThreadLocalStorage - when not defined(boehmgc) and not defined(gogc) and not defined(nogc) and not hasSharedHeap: - # init the GC for this thread: - setStackBottom(addr(t)) - initGC() - when declared(registerThread): - t.stackBottom = addr(t) - registerThread(t) - when TArg is void: t.dataFn() - else: t.dataFn(t.data) - when declared(registerThread): unregisterThread(t) - when declared(deallocOsPages): deallocOsPages() + var thrd = cast[ptr Thread[TArg]](closure) + threadProcWrapStackFrame(thrd) # Since an unhandled exception terminates the whole process (!), there is # no need for a ``try finally`` here, nor would it be correct: The current # exception is tried to be re-raised by the code-gen after the ``finally``! @@ -327,7 +358,7 @@ template threadProcWrapperBody(closure: expr) {.immediate.} = # page! # mark as not running anymore: - t.dataFn = nil + thrd.dataFn = nil {.push stack_trace:off.} when defined(windows): From 9deab06c1ba4e7badec41031ac1af585c353c6f2 Mon Sep 17 00:00:00 2001 From: Reimer Behrends Date: Tue, 8 Sep 2015 19:29:29 +0200 Subject: [PATCH 3/5] Include Boehm GC in garbage collection tests. --- tests/testament/categories.nim | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 4de1edeee1..10e39b20a5 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -114,12 +114,18 @@ proc gcTests(r: var TResults, cat: Category, options: string) = testSpec r, makeTest("tests/gc" / filename, options & " -d:release -d:useRealtimeGC", cat, actionRun) - template test(filename: expr): stmt = + template testWithoutBoehm(filename: expr): stmt = testWithoutMs filename testSpec r, makeTest("tests/gc" / filename, options & " --gc:markAndSweep", cat, actionRun) testSpec r, makeTest("tests/gc" / filename, options & " -d:release --gc:markAndSweep", cat, actionRun) + template test(filename: expr): stmt = + testWithoutBoehm filename + testSpec r, makeTest("tests/gc" / filename, options & + " --gc:boehm", cat, actionRun) + testSpec r, makeTest("tests/gc" / filename, options & + " -d:release --gc:boehm", cat, actionRun) test "growobjcrash" test "gcbench" @@ -130,9 +136,9 @@ proc gcTests(r: var TResults, cat: Category, options: string) = test "gcleak4" # Disabled because it works and takes too long to run: #test "gcleak5" - test "weakrefs" + testWithoutBoehm "weakrefs" test "cycleleak" - test "closureleak" + testWithoutBoehm "closureleak" testWithoutMs "refarrayleak" test "stackrefleak" From 250375bdd22530d3a0f3b2b87cbbf0987c65fcaa Mon Sep 17 00:00:00 2001 From: Reimer Behrends Date: Tue, 8 Sep 2015 19:12:01 +0200 Subject: [PATCH 4/5] Remove spurious unsigned operations from system/threads.nim. These operations were included before the unsigned module was incorporated directly into system.nim and subsequently caused compilation errors with --gc:go due to duplicate definitions. --- lib/system/mmdisp.nim | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index ecf25ceeeb..1c13f3ff8a 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -199,9 +199,6 @@ elif defined(gogc): else: const goLib = "libgo.so" - proc `div`[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.} - proc `-`[T: SomeUnsignedInt](x, y: T): T {.magic: "SubU", noSideEffect.} - proc roundup(x, v: int): int {.inline.} = result = (x + (v-1)) and not (v-1) From 645cf70112495a0326371d8f81e2095da87f15bf Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 12 Oct 2015 19:58:43 +0200 Subject: [PATCH 5/5] disable new BoehmGC tests on Windows --- tests/testament/categories.nim | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 33ce086d35..3166942ecb 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -125,10 +125,13 @@ proc gcTests(r: var TResults, cat: Category, options: string) = " -d:release --gc:markAndSweep", cat, actionRun) template test(filename: expr): stmt = testWithoutBoehm filename - testSpec r, makeTest("tests/gc" / filename, options & - " --gc:boehm", cat, actionRun) - testSpec r, makeTest("tests/gc" / filename, options & - " -d:release --gc:boehm", cat, actionRun) + when not defined(windows): + # AR: cannot find any boehm.dll on the net, right now, so disabled + # for windows: + testSpec r, makeTest("tests/gc" / filename, options & + " --gc:boehm", cat, actionRun) + testSpec r, makeTest("tests/gc" / filename, options & + " -d:release --gc:boehm", cat, actionRun) test "gcemscripten" test "growobjcrash"