lib/system/g-w - Dropped 'T' from types

This commit is contained in:
pdw 2015-05-24 22:37:46 -05:00 • committed by Araq
commit 2ca90a20a1
13 changed files with 426 additions and 396 deletions

View file

@ -45,17 +45,17 @@ const
rcShift = 3 # shift by rcShift to get the reference counter rcShift = 3 # shift by rcShift to get the reference counter
colorMask = 0b011 colorMask = 0b011
type type
TWalkOp = enum WalkOp = enum
waMarkGlobal, # part of the backup/debug mark&sweep waMarkGlobal, # part of the backup/debug mark&sweep
waMarkPrecise, # part of the backup/debug mark&sweep waMarkPrecise, # part of the backup/debug mark&sweep
waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack, waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack,
waCollectWhite #, waDebug waCollectWhite #, waDebug
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
# A ref type can have a finalizer that is called before the object's # A ref type can have a finalizer that is called before the object's
# storage is freed. # storage is freed.
TGcStat {.final, pure.} = object GcStat {.final, pure.} = object
stackScans: int # number of performed stack scans (for statistics) stackScans: int # number of performed stack scans (for statistics)
cycleCollections: int # number of performed full collections cycleCollections: int # number of performed full collections
maxThreshold: int # max threshold that has been set maxThreshold: int # max threshold that has been set
@ -64,35 +64,36 @@ type
cycleTableSize: int # max entries in cycle table cycleTableSize: int # max entries in cycle table
maxPause: int64 # max measured GC pause in nanoseconds maxPause: int64 # max measured GC pause in nanoseconds
TGcHeap {.final, pure.} = object # this contains the zero count and GcHeap {.final, pure.} = object # this contains the zero count and
# non-zero count table # non-zero count table
stackBottom: pointer stackBottom: pointer
cycleThreshold: int cycleThreshold: int
when useCellIds: when useCellIds:
idGenerator: int idGenerator: int
zct: TCellSeq # the zero count table zct: CellSeq # the zero count table
decStack: TCellSeq # cells in the stack that are to decref again decStack: CellSeq # cells in the stack that are to decref again
cycleRoots: TCellSet cycleRoots: CellSet
tempStack: TCellSeq # temporary stack for recursion elimination tempStack: CellSeq # temporary stack for recursion elimination
recGcLock: int # prevent recursion via finalizers; no thread lock recGcLock: int # prevent recursion via finalizers; no thread lock
when withRealTime: when withRealTime:
maxPause: TNanos # max allowed pause in nanoseconds; active if > 0 maxPause: Nanos # max allowed pause in nanoseconds; active if > 0
region: TMemRegion # garbage collected region region: MemRegion # garbage collected region
stat: TGcStat stat: GcStat
when useMarkForDebug or useBackupGc: when useMarkForDebug or useBackupGc:
marked: TCellSet marked: CellSet
{.deprecated: [TWalkOp: WalkOp, TFinalizer: Finalizer, TGcHeap: GcHeap,
TGcStat: GcStat].}
var var
gch {.rtlThreadVar.}: TGcHeap gch {.rtlThreadVar.}: GcHeap
when not defined(useNimRtl): when not defined(useNimRtl):
instantiateForRegion(gch.region) instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) = template acquire(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
acquireSys(HeapLock) acquireSys(HeapLock)
template release(gch: TGcHeap) = template release(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
releaseSys(HeapLock) releaseSys(HeapLock)
@ -104,18 +105,18 @@ template gcAssert(cond: bool, msg: string) =
writeStackTrace() writeStackTrace()
quit 1 quit 1
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} = proc addZCT(s: var CellSeq, c: PCell) {.noinline.} =
if (c.refcount and ZctFlag) == 0: if (c.refcount and ZctFlag) == 0:
c.refcount = c.refcount or ZctFlag c.refcount = c.refcount or ZctFlag
add(s, c) add(s, c)
proc cellToUsr(cell: PCell): pointer {.inline.} = proc cellToUsr(cell: PCell): pointer {.inline.} =
# convert object (=pointer to refcount) to pointer to userdata # convert object (=pointer to refcount) to pointer to userdata
result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(TCell))) result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
proc usrToCell(usr: pointer): PCell {.inline.} = proc usrToCell(usr: pointer): PCell {.inline.} =
# convert pointer to userdata to object (=pointer to refcount) # convert pointer to userdata to object (=pointer to refcount)
result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell))) result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
proc canBeCycleRoot(c: PCell): bool {.inline.} = proc canBeCycleRoot(c: PCell): bool {.inline.} =
result = ntfAcyclic notin c.typ.flags result = ntfAcyclic notin c.typ.flags
@ -152,11 +153,11 @@ template gcTrace(cell, state: expr): stmt {.immediate.} =
when traceGC: traceCell(cell, state) when traceGC: traceCell(cell, state)
# forward declarations: # forward declarations:
proc collectCT(gch: var TGcHeap) {.benign.} proc collectCT(gch: var GcHeap) {.benign.}
proc isOnStack*(p: pointer): bool {.noinline, benign.} proc isOnStack*(p: pointer): bool {.noinline, benign.}
proc forAllChildren(cell: PCell, op: TWalkOp) {.benign.} proc forAllChildren(cell: PCell, op: WalkOp) {.benign.}
proc doOperation(p: pointer, op: TWalkOp) {.benign.} proc doOperation(p: pointer, op: WalkOp) {.benign.}
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) {.benign.} proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
# we need the prototype here for debugging purposes # we need the prototype here for debugging purposes
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
@ -178,7 +179,7 @@ proc prepareDealloc(cell: PCell) =
# prevend recursive entering here by a lock. # prevend recursive entering here by a lock.
# XXX: we should set the cell's children to nil! # XXX: we should set the cell's children to nil!
inc(gch.recGcLock) inc(gch.recGcLock)
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell)) (cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
dec(gch.recGcLock) dec(gch.recGcLock)
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
@ -276,7 +277,7 @@ proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} =
proc initGC() = proc initGC() =
when not defined(useNimRtl): when not defined(useNimRtl):
when traceGC: when traceGC:
for i in low(TCellState)..high(TCellState): init(states[i]) for i in low(CellState)..high(CellState): init(states[i])
gch.cycleThreshold = InitialCycleThreshold gch.cycleThreshold = InitialCycleThreshold
gch.stat.stackScans = 0 gch.stat.stackScans = 0
gch.stat.cycleCollections = 0 gch.stat.cycleCollections = 0
@ -308,12 +309,13 @@ proc setupForeignThreadGc*() =
when useMarkForDebug or useBackupGc: when useMarkForDebug or useBackupGc:
type type
TGlobalMarkerProc = proc () {.nimcall, benign.} GlobalMarkerProc = proc () {.nimcall, benign.}
{.deprecated: [TGlobalMarkerProc: GlobalMarkerProc].}
var var
globalMarkersLen: int globalMarkersLen: int
globalMarkers: array[0.. 7_000, TGlobalMarkerProc] globalMarkers: array[0.. 7_000, GlobalMarkerProc]
proc nimRegisterGlobalMarker(markerProc: TGlobalMarkerProc) {.compilerProc.} = proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerProc.} =
if globalMarkersLen <= high(globalMarkers): if globalMarkersLen <= high(globalMarkers):
globalMarkers[globalMarkersLen] = markerProc globalMarkers[globalMarkersLen] = markerProc
inc globalMarkersLen inc globalMarkersLen
@ -321,11 +323,11 @@ when useMarkForDebug or useBackupGc:
echo "[GC] cannot register global variable; too many global variables" echo "[GC] cannot register global variable; too many global variables"
quit 1 quit 1
proc cellsetReset(s: var TCellSet) = proc cellsetReset(s: var CellSet) =
deinit(s) deinit(s)
init(s) init(s)
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.benign.} = proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
case n.kind case n.kind
of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
@ -345,7 +347,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.benign.} =
if m != nil: forAllSlotsAux(dest, m, op) if m != nil: forAllSlotsAux(dest, m, op)
of nkNone: sysAssert(false, "forAllSlotsAux") of nkNone: sysAssert(false, "forAllSlotsAux")
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) = proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
if dest == nil: return # nothing to do if dest == nil: return # nothing to do
if ntfNoRefs notin mt.flags: if ntfNoRefs notin mt.flags:
@ -359,7 +361,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) =
forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op) forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
else: discard else: discard
proc forAllChildren(cell: PCell, op: TWalkOp) = proc forAllChildren(cell: PCell, op: WalkOp) =
gcAssert(cell != nil, "forAllChildren: 1") gcAssert(cell != nil, "forAllChildren: 1")
gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2") gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
gcAssert(cell.typ != nil, "forAllChildren: 3") gcAssert(cell.typ != nil, "forAllChildren: 3")
@ -380,7 +382,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
GenericSeqSize), cell.typ.base, op) GenericSeqSize), cell.typ.base, op)
else: discard else: discard
proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} = proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
# we check the last 8 entries (cache line) for a slot that could be reused. # we check the last 8 entries (cache line) for a slot that could be reused.
# In 63% of all cases we succeed here! But we have to optimize the heck # In 63% of all cases we succeed here! But we have to optimize the heck
# out of this small linear search so that ``newObj`` is not slowed down. # out of this small linear search so that ``newObj`` is not slowed down.
@ -431,13 +433,13 @@ proc gcInvariant*() =
markForDebug(gch) markForDebug(gch)
{.pop.} {.pop.}
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer = proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
# generates a new object and sets its reference counter to 0 # generates a new object and sets its reference counter to 0
sysAssert(allocInv(gch.region), "rawNewObj begin") sysAssert(allocInv(gch.region), "rawNewObj begin")
acquire(gch) acquire(gch)
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
collectCT(gch) collectCT(gch)
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT # now it is buffered in the ZCT
res.typ = typ res.typ = typ
@ -486,7 +488,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
collectCT(gch) collectCT(gch)
sysAssert(allocInv(gch.region), "newObjRC1 after collectCT") sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc") sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT # now it is buffered in the ZCT
@ -515,7 +517,7 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
cast[PGenericSeq](result).reserved = len cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size) when defined(memProfiler): nimProfile(size)
proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
acquire(gch) acquire(gch)
collectCT(gch) collectCT(gch)
var ol = usrToCell(old) var ol = usrToCell(old)
@ -523,13 +525,13 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
sysAssert(allocInv(gch.region), "growObj begin") sysAssert(allocInv(gch.region), "growObj begin")
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
var elemSize = 1 var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size if ol.typ.kind != tyString: elemSize = ol.typ.base.size
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
copyMem(res, ol, oldsize + sizeof(TCell)) copyMem(res, ol, oldsize + sizeof(Cell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
newsize-oldsize) newsize-oldsize)
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
# This can be wrong for intermediate temps that are nevertheless on the # This can be wrong for intermediate temps that are nevertheless on the
@ -564,7 +566,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
decRef(ol) decRef(ol)
else: else:
sysAssert(ol.typ != nil, "growObj: 5") sysAssert(ol.typ != nil, "growObj: 5")
zeroMem(ol, sizeof(TCell)) zeroMem(ol, sizeof(Cell))
release(gch) release(gch)
when useCellIds: when useCellIds:
inc gch.idGenerator inc gch.idGenerator
@ -580,7 +582,7 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
# ---------------- cycle collector ------------------------------------------- # ---------------- cycle collector -------------------------------------------
proc freeCyclicCell(gch: var TGcHeap, c: PCell) = proc freeCyclicCell(gch: var GcHeap, c: PCell) =
prepareDealloc(c) prepareDealloc(c)
gcTrace(c, csCycFreed) gcTrace(c, csCycFreed)
when logGC: writeCell("cycle collector dealloc cell", c) when logGC: writeCell("cycle collector dealloc cell", c)
@ -589,7 +591,7 @@ proc freeCyclicCell(gch: var TGcHeap, c: PCell) =
rawDealloc(gch.region, c) rawDealloc(gch.region, c)
else: else:
gcAssert(c.typ != nil, "freeCyclicCell") gcAssert(c.typ != nil, "freeCyclicCell")
zeroMem(c, sizeof(TCell)) zeroMem(c, sizeof(Cell))
proc markGray(s: PCell) = proc markGray(s: PCell) =
if s.color != rcGray: if s.color != rcGray:
@ -620,7 +622,7 @@ proc collectWhite(s: PCell) =
forAllChildren(s, waCollectWhite) forAllChildren(s, waCollectWhite)
freeCyclicCell(gch, s) freeCyclicCell(gch, s)
proc markRoots(gch: var TGcHeap) = proc markRoots(gch: var GcHeap) =
var tabSize = 0 var tabSize = 0
for s in elements(gch.cycleRoots): for s in elements(gch.cycleRoots):
#writeCell("markRoot", s) #writeCell("markRoot", s)
@ -635,7 +637,7 @@ proc markRoots(gch: var TGcHeap) =
gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, tabSize) gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, tabSize)
when useBackupGc: when useBackupGc:
proc sweep(gch: var TGcHeap) = proc sweep(gch: var GcHeap) =
for x in allObjects(gch.region): for x in allObjects(gch.region):
if isCell(x): if isCell(x):
# cast to PCell is correct here: # cast to PCell is correct here:
@ -643,7 +645,7 @@ when useBackupGc:
if c notin gch.marked: freeCyclicCell(gch, c) if c notin gch.marked: freeCyclicCell(gch, c)
when useMarkForDebug or useBackupGc: when useMarkForDebug or useBackupGc:
proc markS(gch: var TGcHeap, c: PCell) = proc markS(gch: var GcHeap, c: PCell) =
incl(gch.marked, c) incl(gch.marked, c)
gcAssert gch.tempStack.len == 0, "stack not empty!" gcAssert gch.tempStack.len == 0, "stack not empty!"
forAllChildren(c, waMarkPrecise) forAllChildren(c, waMarkPrecise)
@ -653,10 +655,10 @@ when useMarkForDebug or useBackupGc:
if not containsOrIncl(gch.marked, d): if not containsOrIncl(gch.marked, d):
forAllChildren(d, waMarkPrecise) forAllChildren(d, waMarkPrecise)
proc markGlobals(gch: var TGcHeap) = proc markGlobals(gch: var GcHeap) =
for i in 0 .. < globalMarkersLen: globalMarkers[i]() for i in 0 .. < globalMarkersLen: globalMarkers[i]()
proc stackMarkS(gch: var TGcHeap, p: pointer) {.inline.} = proc stackMarkS(gch: var GcHeap, p: pointer) {.inline.} =
# the addresses are not as cells on the stack, so turn them to cells: # the addresses are not as cells on the stack, so turn them to cells:
var cell = usrToCell(p) var cell = usrToCell(p)
var c = cast[TAddress](cell) var c = cast[TAddress](cell)
@ -688,7 +690,7 @@ when logGC:
forAllChildren(s, waDebug) forAllChildren(s, waDebug)
c_fprintf(c_stdout, "}\n") c_fprintf(c_stdout, "}\n")
proc doOperation(p: pointer, op: TWalkOp) = proc doOperation(p: pointer, op: WalkOp) =
if p == nil: return if p == nil: return
var c: PCell = usrToCell(p) var c: PCell = usrToCell(p)
gcAssert(c != nil, "doOperation: 1") gcAssert(c != nil, "doOperation: 1")
@ -733,19 +735,19 @@ proc doOperation(p: pointer, op: TWalkOp) =
#of waDebug: debugGraph(c) #of waDebug: debugGraph(c)
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
doOperation(d, TWalkOp(op)) doOperation(d, WalkOp(op))
proc collectZCT(gch: var TGcHeap): bool {.benign.} proc collectZCT(gch: var GcHeap): bool {.benign.}
when useMarkForDebug or useBackupGc: when useMarkForDebug or useBackupGc:
proc markStackAndRegistersForSweep(gch: var TGcHeap) {.noinline, cdecl, proc markStackAndRegistersForSweep(gch: var GcHeap) {.noinline, cdecl,
benign.} benign.}
proc collectRoots(gch: var TGcHeap) = proc collectRoots(gch: var GcHeap) =
for s in elements(gch.cycleRoots): for s in elements(gch.cycleRoots):
collectWhite(s) collectWhite(s)
proc collectCycles(gch: var TGcHeap) = proc collectCycles(gch: var GcHeap) =
# ensure the ZCT 'color' is not used: # ensure the ZCT 'color' is not used:
while gch.zct.len > 0: discard collectZCT(gch) while gch.zct.len > 0: discard collectZCT(gch)
when useBackupGc: when useBackupGc:
@ -778,7 +780,7 @@ proc collectCycles(gch: var TGcHeap) =
if cycleRootsLen != 0: if cycleRootsLen != 0:
cfprintf(cstdout, "cycle roots: %ld\n", cycleRootsLen) cfprintf(cstdout, "cycle roots: %ld\n", cycleRootsLen)
proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} = proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
# the addresses are not as cells on the stack, so turn them to cells: # the addresses are not as cells on the stack, so turn them to cells:
sysAssert(allocInv(gch.region), "gcMark begin") sysAssert(allocInv(gch.region), "gcMark begin")
var cell = usrToCell(p) var cell = usrToCell(p)
@ -798,7 +800,7 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
add(gch.decStack, cell) add(gch.decStack, cell)
sysAssert(allocInv(gch.region), "gcMark end") sysAssert(allocInv(gch.region), "gcMark end")
proc markThreadStacks(gch: var TGcHeap) = proc markThreadStacks(gch: var GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
{.error: "not fully implemented".} {.error: "not fully implemented".}
var it = threadList var it = threadList
@ -887,7 +889,7 @@ elif stackIncreases:
var var
jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
# a little hack to get the size of a TJmpBuf in the generated C code # a little hack to get the size of a JmpBuf in the generated C code
# in a platform independent way # in a platform independent way
template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} = template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
@ -947,18 +949,18 @@ else:
gcMark(gch, cast[PPointer](sp)[]) gcMark(gch, cast[PPointer](sp)[])
sp = sp +% sizeof(pointer) sp = sp +% sizeof(pointer)
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
forEachStackSlot(gch, gcMark) forEachStackSlot(gch, gcMark)
when useMarkForDebug or useBackupGc: when useMarkForDebug or useBackupGc:
proc markStackAndRegistersForSweep(gch: var TGcHeap) = proc markStackAndRegistersForSweep(gch: var GcHeap) =
forEachStackSlot(gch, stackMarkS) forEachStackSlot(gch, stackMarkS)
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
# end of non-portable code # end of non-portable code
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
proc collectZCT(gch: var TGcHeap): bool = proc collectZCT(gch: var GcHeap): bool =
# Note: Freeing may add child objects to the ZCT! So essentially we do # Note: Freeing may add child objects to the ZCT! So essentially we do
# deep freeing, which is bad for incremental operation. In order to # deep freeing, which is bad for incremental operation. In order to
# avoid a deep stack, we move objects to keep the ZCT small. # avoid a deep stack, we move objects to keep the ZCT small.
@ -968,7 +970,7 @@ proc collectZCT(gch: var TGcHeap): bool =
when withRealTime: when withRealTime:
var steps = workPackage var steps = workPackage
var t0: TTicks var t0: Ticks
if gch.maxPause > 0: t0 = getticks() if gch.maxPause > 0: t0 = getticks()
while L[] > 0: while L[] > 0:
var c = gch.zct.d[0] var c = gch.zct.d[0]
@ -1001,7 +1003,7 @@ proc collectZCT(gch: var TGcHeap): bool =
rawDealloc(gch.region, c) rawDealloc(gch.region, c)
else: else:
sysAssert(c.typ != nil, "collectZCT 2") sysAssert(c.typ != nil, "collectZCT 2")
zeroMem(c, sizeof(TCell)) zeroMem(c, sizeof(Cell))
when withRealTime: when withRealTime:
if steps == 0: if steps == 0:
steps = workPackage steps = workPackage
@ -1014,7 +1016,7 @@ proc collectZCT(gch: var TGcHeap): bool =
return false return false
result = true result = true
proc unmarkStackAndRegisters(gch: var TGcHeap) = proc unmarkStackAndRegisters(gch: var GcHeap) =
var d = gch.decStack.d var d = gch.decStack.d
for i in 0..gch.decStack.len-1: for i in 0..gch.decStack.len-1:
sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters" sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
@ -1026,7 +1028,7 @@ proc unmarkStackAndRegisters(gch: var TGcHeap) =
#sysAssert c.typ != nil, "unmarkStackAndRegisters 2" #sysAssert c.typ != nil, "unmarkStackAndRegisters 2"
gch.decStack.len = 0 gch.decStack.len = 0
proc collectCTBody(gch: var TGcHeap) = proc collectCTBody(gch: var GcHeap) =
when withRealTime: when withRealTime:
let t0 = getticks() let t0 = getticks()
sysAssert(allocInv(gch.region), "collectCT: begin") sysAssert(allocInv(gch.region), "collectCT: begin")
@ -1058,11 +1060,11 @@ proc collectCTBody(gch: var TGcHeap) =
c_fprintf(c_stdout, "[GC] missed deadline: %ld\n", duration) c_fprintf(c_stdout, "[GC] missed deadline: %ld\n", duration)
when useMarkForDebug or useBackupGc: when useMarkForDebug or useBackupGc:
proc markForDebug(gch: var TGcHeap) = proc markForDebug(gch: var GcHeap) =
markStackAndRegistersForSweep(gch) markStackAndRegistersForSweep(gch)
markGlobals(gch) markGlobals(gch)
proc collectCT(gch: var TGcHeap) = proc collectCT(gch: var GcHeap) =
# stackMarkCosts prevents some pathological behaviour: Stack marking # stackMarkCosts prevents some pathological behaviour: Stack marking
# becomes more expensive with large stacks and large stacks mean that # becomes more expensive with large stacks and large stacks mean that
# cells with RC=0 are more likely to be kept alive by the stack. # cells with RC=0 are more likely to be kept alive by the stack.
@ -1077,13 +1079,13 @@ proc collectCT(gch: var TGcHeap) =
collectCTBody(gch) collectCTBody(gch)
when withRealTime: when withRealTime:
proc toNano(x: int): TNanos {.inline.} = proc toNano(x: int): Nanos {.inline.} =
result = x * 1000 result = x * 1000
proc GC_setMaxPause*(MaxPauseInUs: int) = proc GC_setMaxPause*(MaxPauseInUs: int) =
gch.maxPause = MaxPauseInUs.toNano gch.maxPause = MaxPauseInUs.toNano
proc GC_step(gch: var TGcHeap, us: int, strongAdvice: bool) = proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
acquire(gch) acquire(gch)
gch.maxPause = us.toNano gch.maxPause = us.toNano
if (gch.zct.len >= ZctThreshold or (cycleGC and if (gch.zct.len >= ZctThreshold or (cycleGC and

View file

@ -54,7 +54,7 @@ const
# was replaced by a resize operation. # was replaced by a resize operation.
# see growObj for details # see growObj for details
rcColorMask = TRefCount(0b00111) rcColorMask = RefCount(0b00111)
rcZct = 0b01000 # already added to ZCT rcZct = 0b01000 # already added to ZCT
rcInCycleRoots = 0b10000 # already buffered as cycle candidate rcInCycleRoots = 0b10000 # already buffered as cycle candidate
@ -97,14 +97,14 @@ const
CollectCyclesStats = false CollectCyclesStats = false
type type
TWalkOp = enum WalkOp = enum
waPush waPush
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall.} Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall.}
# A ref type can have a finalizer that is called before the object's # A ref type can have a finalizer that is called before the object's
# storage is freed. # storage is freed.
TGcStat {.final, pure.} = object GcStat {.final, pure.} = object
stackScans: int # number of performed stack scans (for statistics) stackScans: int # number of performed stack scans (for statistics)
cycleCollections: int # number of performed full collections cycleCollections: int # number of performed full collections
maxThreshold: int # max threshold that has been set maxThreshold: int # max threshold that has been set
@ -113,16 +113,16 @@ type
cycleTableSize: int # max entries in cycle table cycleTableSize: int # max entries in cycle table
maxPause: int64 # max measured GC pause in nanoseconds maxPause: int64 # max measured GC pause in nanoseconds
TGcHeap {.final, pure.} = object # this contains the zero count and GcHeap {.final, pure.} = object # this contains the zero count and
# non-zero count table # non-zero count table
stackBottom: pointer stackBottom: pointer
stackTop: pointer stackTop: pointer
cycleThreshold: int cycleThreshold: int
zct: TCellSeq # the zero count table zct: CellSeq # the zero count table
decStack: TCellSeq # cells in the stack that are to decref again decStack: CellSeq # cells in the stack that are to decref again
cycleRoots: TCellSeq cycleRoots: CellSeq
tempStack: TCellSeq # temporary stack for recursion elimination tempStack: CellSeq # temporary stack for recursion elimination
freeStack: TCellSeq # objects ready to be freed freeStack: CellSeq # objects ready to be freed
recGcLock: int # prevent recursion via finalizers; no thread lock recGcLock: int # prevent recursion via finalizers; no thread lock
cycleRootsTrimIdx: int # Trimming is a light-weight collection of the cycleRootsTrimIdx: int # Trimming is a light-weight collection of the
# cycle roots table that uses a cheap linear scan # cycle roots table that uses a cheap linear scan
@ -132,21 +132,22 @@ type
# This index indicates the start of the range of # This index indicates the start of the range of
# such new objects within the table. # such new objects within the table.
when withRealTime: when withRealTime:
maxPause: TNanos # max allowed pause in nanoseconds; active if > 0 maxPause: Nanos # max allowed pause in nanoseconds; active if > 0
region: TMemRegion # garbage collected region region: MemRegion # garbage collected region
stat: TGcStat stat: GcStat
{.deprecated: [TWalkOp: WalkOp, TFinalizer: Finalizer, TGcStat: GcStat,
TGcHeap: GcHeap].}
var var
gch* {.rtlThreadVar.}: TGcHeap gch* {.rtlThreadVar.}: GcHeap
when not defined(useNimRtl): when not defined(useNimRtl):
instantiateForRegion(gch.region) instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) = template acquire(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
AcquireSys(HeapLock) AcquireSys(HeapLock)
template release(gch: TGcHeap) = template release(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
releaseSys(HeapLock) releaseSys(HeapLock)
@ -169,7 +170,7 @@ template isDead(c: Pcell): expr =
c.isBitUp(rcReallyDead) # also covers rcRetiredBuffer c.isBitUp(rcReallyDead) # also covers rcRetiredBuffer
template clearBit(c: PCell, bit): expr = template clearBit(c: PCell, bit): expr =
c.refcount = c.refcount and (not TRefCount(bit)) c.refcount = c.refcount and (not RefCount(bit))
when debugGC: when debugGC:
var gcCollectionIdx = 0 var gcCollectionIdx = 0
@ -206,7 +207,7 @@ when debugGC:
c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld\n", c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld\n",
msg, c, kind, c.refcount shr rcShift) msg, c, kind, c.refcount shr rcShift)
proc addZCT(zct: var TCellSeq, c: PCell) {.noinline.} = proc addZCT(zct: var CellSeq, c: PCell) {.noinline.} =
if c.isBitDown(rcZct): if c.isBitDown(rcZct):
c.setBit rcZct c.setBit rcZct
zct.add c zct.add c
@ -221,7 +222,7 @@ template setStackTop(gch) =
var stackTop {.volatile.}: pointer var stackTop {.volatile.}: pointer
gch.stackTop = addr(stackTop) gch.stackTop = addr(stackTop)
template addCycleRoot(cycleRoots: var TCellSeq, c: PCell) = template addCycleRoot(cycleRoots: var CellSeq, c: PCell) =
if c.color != rcCycleCandidate: if c.color != rcCycleCandidate:
c.setColor rcCycleCandidate c.setColor rcCycleCandidate
@ -233,11 +234,11 @@ template addCycleRoot(cycleRoots: var TCellSeq, c: PCell) =
proc cellToUsr(cell: PCell): pointer {.inline.} = proc cellToUsr(cell: PCell): pointer {.inline.} =
# convert object (=pointer to refcount) to pointer to userdata # convert object (=pointer to refcount) to pointer to userdata
result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(TCell))) result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
proc usrToCell*(usr: pointer): PCell {.inline.} = proc usrToCell*(usr: pointer): PCell {.inline.} =
# convert pointer to userdata to object (=pointer to refcount) # convert pointer to userdata to object (=pointer to refcount)
result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell))) result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
proc canbeCycleRoot(c: PCell): bool {.inline.} = proc canbeCycleRoot(c: PCell): bool {.inline.} =
result = ntfAcyclic notin c.typ.flags result = ntfAcyclic notin c.typ.flags
@ -254,11 +255,11 @@ when BitsPerPage mod (sizeof(int)*8) != 0:
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".} {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
# forward declarations: # forward declarations:
proc collectCT(gch: var TGcHeap) proc collectCT(gch: var GcHeap)
proc isOnStack*(p: pointer): bool {.noinline.} proc isOnStack*(p: pointer): bool {.noinline.}
proc forAllChildren(cell: PCell, op: TWalkOp) proc forAllChildren(cell: PCell, op: WalkOp)
proc doOperation(p: pointer, op: TWalkOp) proc doOperation(p: pointer, op: WalkOp)
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp)
# we need the prototype here for debugging purposes # we need the prototype here for debugging purposes
proc prepareDealloc(cell: PCell) = proc prepareDealloc(cell: PCell) =
@ -269,18 +270,19 @@ proc prepareDealloc(cell: PCell) =
# prevend recursive entering here by a lock. # prevend recursive entering here by a lock.
# XXX: we should set the cell's children to nil! # XXX: we should set the cell's children to nil!
inc(gch.recGcLock) inc(gch.recGcLock)
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell)) (cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
dec(gch.recGcLock) dec(gch.recGcLock)
when traceGC: when traceGC:
# traceGC is a special switch to enable extensive debugging # traceGC is a special switch to enable extensive debugging
type type
TCellState = enum CellState = enum
csAllocated, csFreed csAllocated, csFreed
{.deprecated: [TCellState: CellState].}
var var
states: array[TCellState, TCellSet] states: array[CellState, CellSet]
proc traceCell(c: PCell, state: TCellState) = proc traceCell(c: PCell, state: CellState) =
case state case state
of csAllocated: of csAllocated:
if c in states[csAllocated]: if c in states[csAllocated]:
@ -300,7 +302,7 @@ when traceGC:
incl(states[state], c) incl(states[state], c)
proc computeCellWeight(c: PCell): int = proc computeCellWeight(c: PCell): int =
var x: TCellSet var x: CellSet
x.init x.init
let startLen = gch.tempStack.len let startLen = gch.tempStack.len
@ -363,30 +365,32 @@ proc rtlAddZCT(c: PCell) {.rtl, inl.} =
WithHeapLock: addZCT(gch.zct, c) WithHeapLock: addZCT(gch.zct, c)
type type
TCyclicMode = enum CyclicMode = enum
Cyclic, Cyclic,
Acyclic, Acyclic,
MaybeCyclic MaybeCyclic
TReleaseType = enum ReleaseType = enum
AddToZTC AddToZTC
FreeImmediately FreeImmediately
THeapType = enum HeapType = enum
LocalHeap LocalHeap
SharedHeap SharedHeap
{.deprecated: [TCyclicMode: CyclicMode, TReleaseType: ReleaseType,
THeapType: HeapType].}
template `++` (rc: TRefCount, heapType: THeapType): stmt = template `++` (rc: RefCount, heapType: HeapType): stmt =
when heapType == SharedHeap: when heapType == SharedHeap:
discard atomicInc(rc, rcIncrement) discard atomicInc(rc, rcIncrement)
else: else:
inc rc, rcIncrement inc rc, rcIncrement
template `--`(rc: TRefCount): expr = template `--`(rc: RefCount): expr =
dec rc, rcIncrement dec rc, rcIncrement
rc <% rcIncrement rc <% rcIncrement
template `--` (rc: TRefCount, heapType: THeapType): expr = template `--` (rc: RefCount, heapType: HeapType): expr =
(when heapType == SharedHeap: atomicDec(rc, rcIncrement) <% rcIncrement else: --rc) (when heapType == SharedHeap: atomicDec(rc, rcIncrement) <% rcIncrement else: --rc)
template doDecRef(cc: PCell, template doDecRef(cc: PCell,
@ -479,7 +483,7 @@ when hasThreadSupport and hasSharedHeap:
proc initGC() = proc initGC() =
when not defined(useNimRtl): when not defined(useNimRtl):
when traceGC: when traceGC:
for i in low(TCellState)..high(TCellState): init(states[i]) for i in low(CellState)..high(CellState): init(states[i])
gch.cycleThreshold = InitialCycleThreshold gch.cycleThreshold = InitialCycleThreshold
gch.stat.stackScans = 0 gch.stat.stackScans = 0
gch.stat.cycleCollections = 0 gch.stat.cycleCollections = 0
@ -494,7 +498,7 @@ proc initGC() =
init(gch.cycleRoots) init(gch.cycleRoots)
init(gch.decStack) init(gch.decStack)
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) = proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
case n.kind case n.kind
of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
@ -514,7 +518,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
if m != nil: forAllSlotsAux(dest, m, op) if m != nil: forAllSlotsAux(dest, m, op)
of nkNone: sysAssert(false, "forAllSlotsAux") of nkNone: sysAssert(false, "forAllSlotsAux")
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) = proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
if dest == nil: return # nothing to do if dest == nil: return # nothing to do
if ntfNoRefs notin mt.flags: if ntfNoRefs notin mt.flags:
@ -528,7 +532,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) =
forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op) forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
else: discard else: discard
proc forAllChildren(cell: PCell, op: TWalkOp) = proc forAllChildren(cell: PCell, op: WalkOp) =
sysAssert(cell != nil, "forAllChildren: 1") sysAssert(cell != nil, "forAllChildren: 1")
sysAssert(cell.typ != nil, "forAllChildren: 2") sysAssert(cell.typ != nil, "forAllChildren: 2")
sysAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3" sysAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3"
@ -549,7 +553,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
cell.typ.base, op) cell.typ.base, op)
else: discard else: discard
proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} = proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
# we check the last 8 entries (cache line) for a slot that could be reused. # we check the last 8 entries (cache line) for a slot that could be reused.
# In 63% of all cases we succeed here! But we have to optimize the heck # In 63% of all cases we succeed here! But we have to optimize the heck
# out of this small linear search so that ``newObj`` is not slowed down. # out of this small linear search so that ``newObj`` is not slowed down.
@ -593,7 +597,7 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
return return
add(gch.zct, res) add(gch.zct, res)
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1 = false): pointer = proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap, rc1 = false): pointer =
# generates a new object and sets its reference counter to 0 # generates a new object and sets its reference counter to 0
acquire(gch) acquire(gch)
sysAssert(allocInv(gch.region), "rawNewObj begin") sysAssert(allocInv(gch.region), "rawNewObj begin")
@ -602,7 +606,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1 = false): pointer
collectCT(gch) collectCT(gch)
sysAssert(allocInv(gch.region), "rawNewObj after collect") sysAssert(allocInv(gch.region), "rawNewObj after collect")
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
sysAssert(allocInv(gch.region), "rawNewObj after rawAlloc") sysAssert(allocInv(gch.region), "rawNewObj after rawAlloc")
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
@ -638,20 +642,20 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1 = false): pointer
{.pop.} {.pop.}
proc freeCell(gch: var TGcHeap, c: PCell) = proc freeCell(gch: var GcHeap, c: PCell) =
# prepareDealloc(c) # prepareDealloc(c)
gcTrace(c, csFreed) gcTrace(c, csFreed)
when reallyDealloc: rawDealloc(gch.region, c) when reallyDealloc: rawDealloc(gch.region, c)
else: else:
sysAssert(c.typ != nil, "collectCycles") sysAssert(c.typ != nil, "collectCycles")
zeroMem(c, sizeof(TCell)) zeroMem(c, sizeof(Cell))
template eraseAt(cells: var TCellSeq, at: int): stmt = template eraseAt(cells: var CellSeq, at: int): stmt =
cells.d[at] = cells.d[cells.len - 1] cells.d[at] = cells.d[cells.len - 1]
dec cells.len dec cells.len
template trimAt(roots: var TCellSeq, at: int): stmt = template trimAt(roots: var CellSeq, at: int): stmt =
# This will remove a cycle root candidate during trimming. # This will remove a cycle root candidate during trimming.
# a candidate is removed either because it received a refup and # a candidate is removed either because it received a refup and
# it's no longer a candidate or because it received further refdowns # it's no longer a candidate or because it received further refdowns
@ -696,7 +700,7 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
cast[PGenericSeq](result).len = len cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len cast[PGenericSeq](result).reserved = len
proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
acquire(gch) acquire(gch)
collectCT(gch) collectCT(gch)
var ol = usrToCell(old) var ol = usrToCell(old)
@ -704,7 +708,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
sysAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") sysAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
sysAssert(allocInv(gch.region), "growObj begin") sysAssert(allocInv(gch.region), "growObj begin")
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
var elemSize = if ol.typ.kind != tyString: ol.typ.base.size var elemSize = if ol.typ.kind != tyString: ol.typ.base.size
else: 1 else: 1
@ -713,8 +717,8 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
# XXX: This should happen outside # XXX: This should happen outside
# call user-defined move code # call user-defined move code
# call user-defined default constructor # call user-defined default constructor
copyMem(res, ol, oldsize + sizeof(TCell)) copyMem(res, ol, oldsize + sizeof(Cell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
newsize-oldsize) newsize-oldsize)
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
@ -778,26 +782,27 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
# ---------------- cycle collector ------------------------------------------- # ---------------- cycle collector -------------------------------------------
proc doOperation(p: pointer, op: TWalkOp) = proc doOperation(p: pointer, op: WalkOp) =
if p == nil: return if p == nil: return
var c: PCell = usrToCell(p) var c: PCell = usrToCell(p)
sysAssert(c != nil, "doOperation: 1") sysAssert(c != nil, "doOperation: 1")
gch.tempStack.add c gch.tempStack.add c
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
doOperation(d, TWalkOp(op)) doOperation(d, WalkOp(op))
type type
TRecursionType = enum RecursionType = enum
FromChildren, FromChildren,
FromRoot FromRoot
{.deprecated: [TRecursionType: RecursionType].}
proc collectZCT(gch: var TGcHeap): bool proc collectZCT(gch: var GcHeap): bool
template pseudoRecursion(typ: TRecursionType, body: stmt): stmt = template pseudoRecursion(typ: RecursionType, body: stmt): stmt =
discard discard
proc trimCycleRoots(gch: var TGcHeap, startIdx = gch.cycleRootsTrimIdx) = proc trimCycleRoots(gch: var GcHeap, startIdx = gch.cycleRootsTrimIdx) =
var i = startIdx var i = startIdx
while i < gch.cycleRoots.len: while i < gch.cycleRoots.len:
if gch.cycleRoots.d[i].color != rcCycleCandidate: if gch.cycleRoots.d[i].color != rcCycleCandidate:
@ -808,7 +813,7 @@ proc trimCycleRoots(gch: var TGcHeap, startIdx = gch.cycleRootsTrimIdx) =
gch.cycleRootsTrimIdx = gch.cycleRoots.len gch.cycleRootsTrimIdx = gch.cycleRoots.len
# we now use a much simpler and non-recursive algorithm for cycle removal # we now use a much simpler and non-recursive algorithm for cycle removal
proc collectCycles(gch: var TGcHeap) = proc collectCycles(gch: var GcHeap) =
if gch.cycleRoots.len == 0: return if gch.cycleRoots.len == 0: return
gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, gch.cycleRoots.len) gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, gch.cycleRoots.len)
@ -990,7 +995,7 @@ var gcDebugging* = false
var seqdbg* : proc (s: PGenericSeq) {.cdecl.} var seqdbg* : proc (s: PGenericSeq) {.cdecl.}
proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} = proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
# the addresses are not as cells on the stack, so turn them to cells: # the addresses are not as cells on the stack, so turn them to cells:
sysAssert(allocInv(gch.region), "gcMark begin") sysAssert(allocInv(gch.region), "gcMark begin")
var cell = usrToCell(p) var cell = usrToCell(p)
@ -1025,7 +1030,7 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
add(gch.decStack, cell) add(gch.decStack, cell)
sysAssert(allocInv(gch.region), "gcMark end") sysAssert(allocInv(gch.region), "gcMark end")
proc markThreadStacks(gch: var TGcHeap) = proc markThreadStacks(gch: var GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
{.error: "not fully implemented".} {.error: "not fully implemented".}
var it = threadList var it = threadList
@ -1074,7 +1079,7 @@ proc stackSize(): int {.noinline.} =
var var
jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
# a little hack to get the size of a TJmpBuf in the generated C code # a little hack to get the size of a JmpBuf in the generated C code
# in a platform independent way # in a platform independent way
when defined(sparc): # For SPARC architecture. when defined(sparc): # For SPARC architecture.
@ -1086,7 +1091,7 @@ when defined(sparc): # For SPARC architecture.
var x = cast[ByteAddress](p) var x = cast[ByteAddress](p)
result = a <=% x and x <=% b result = a <=% x and x <=% b
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
when defined(sparcv9): when defined(sparcv9):
asm """"flushw \n" """ asm """"flushw \n" """
else: else:
@ -1117,7 +1122,7 @@ elif stackIncreases:
var x = cast[ByteAddress](p) var x = cast[ByteAddress](p)
result = a <=% x and x <=% b result = a <=% x and x <=% b
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
var registers: C_JmpBuf var registers: C_JmpBuf
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
var max = cast[ByteAddress](gch.stackBottom) var max = cast[ByteAddress](gch.stackBottom)
@ -1140,7 +1145,7 @@ else:
var x = cast[ByteAddress](p) var x = cast[ByteAddress](p)
result = a <=% x and x <=% b result = a <=% x and x <=% b
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
# We use a jmp_buf buffer that is in the C stack. # We use a jmp_buf buffer that is in the C stack.
# Used to traverse the stack and registers assuming # Used to traverse the stack and registers assuming
# that 'setjmp' will save registers in the C stack. # that 'setjmp' will save registers in the C stack.
@ -1181,7 +1186,7 @@ else:
# end of non-portable code # end of non-portable code
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
proc releaseCell(gch: var TGcHeap, cell: PCell) = proc releaseCell(gch: var GcHeap, cell: PCell) =
if cell.color != rcReallyDead: if cell.color != rcReallyDead:
prepareDealloc(cell) prepareDealloc(cell)
cell.setColor rcReallyDead cell.setColor rcReallyDead
@ -1210,13 +1215,13 @@ proc releaseCell(gch: var TGcHeap, cell: PCell) =
# recursion). # recursion).
# We can ignore it now as the ZCT cleaner will reach it soon. # We can ignore it now as the ZCT cleaner will reach it soon.
proc collectZCT(gch: var TGcHeap): bool = proc collectZCT(gch: var GcHeap): bool =
const workPackage = 100 const workPackage = 100
var L = addr(gch.zct.len) var L = addr(gch.zct.len)
when withRealtime: when withRealtime:
var steps = workPackage var steps = workPackage
var t0: TTicks var t0: Ticks
if gch.maxPause > 0: t0 = getticks() if gch.maxPause > 0: t0 = getticks()
while L[] > 0: while L[] > 0:
@ -1257,7 +1262,7 @@ proc collectZCT(gch: var TGcHeap): bool =
#deInit(gch.zct) #deInit(gch.zct)
#init(gch.zct) #init(gch.zct)
proc unmarkStackAndRegisters(gch: var TGcHeap) = proc unmarkStackAndRegisters(gch: var GcHeap) =
var d = gch.decStack.d var d = gch.decStack.d
for i in 0 .. <gch.decStack.len: for i in 0 .. <gch.decStack.len:
sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters" sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
@ -1283,7 +1288,7 @@ proc unmarkStackAndRegisters(gch: var TGcHeap) =
gch.decStack.len = 0 gch.decStack.len = 0
proc collectCTBody(gch: var TGcHeap) = proc collectCTBody(gch: var GcHeap) =
when withRealtime: when withRealtime:
let t0 = getticks() let t0 = getticks()
when debugGC: inc gcCollectionIdx when debugGC: inc gcCollectionIdx
@ -1315,20 +1320,20 @@ proc collectCTBody(gch: var TGcHeap) =
if gch.maxPause > 0 and duration > gch.maxPause: if gch.maxPause > 0 and duration > gch.maxPause:
c_fprintf(c_stdout, "[GC] missed deadline: %ld\n", duration) c_fprintf(c_stdout, "[GC] missed deadline: %ld\n", duration)
proc collectCT(gch: var TGcHeap) = proc collectCT(gch: var GcHeap) =
if (gch.zct.len >= ZctThreshold or (cycleGC and if (gch.zct.len >= ZctThreshold or (cycleGC and
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
gch.recGcLock == 0: gch.recGcLock == 0:
collectCTBody(gch) collectCTBody(gch)
when withRealtime: when withRealtime:
proc toNano(x: int): TNanos {.inline.} = proc toNano(x: int): Nanos {.inline.} =
result = x * 1000 result = x * 1000
proc GC_setMaxPause*(MaxPauseInUs: int) = proc GC_setMaxPause*(MaxPauseInUs: int) =
gch.maxPause = MaxPauseInUs.toNano gch.maxPause = MaxPauseInUs.toNano
proc GC_step(gch: var TGcHeap, us: int, strongAdvice: bool) = proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
acquire(gch) acquire(gch)
gch.maxPause = us.toNano gch.maxPause = us.toNano
if (gch.zct.len >= ZctThreshold or (cycleGC and if (gch.zct.len >= ZctThreshold or (cycleGC and

View file

@ -26,49 +26,50 @@ when defined(memProfiler):
proc nimProfile(requestedSize: int) proc nimProfile(requestedSize: int)
type type
TWalkOp = enum WalkOp = enum
waMarkGlobal, # we need to mark conservatively for global marker procs waMarkGlobal, # we need to mark conservatively for global marker procs
# as these may refer to a global var and not to a thread # as these may refer to a global var and not to a thread
# local # local
waMarkPrecise # fast precise marking waMarkPrecise # fast precise marking
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
# A ref type can have a finalizer that is called before the object's # A ref type can have a finalizer that is called before the object's
# storage is freed. # storage is freed.
TGlobalMarkerProc = proc () {.nimcall, benign.} GlobalMarkerProc = proc () {.nimcall, benign.}
TGcStat = object GcStat = object
collections: int # number of performed full collections collections: int # number of performed full collections
maxThreshold: int # max threshold that has been set maxThreshold: int # max threshold that has been set
maxStackSize: int # max stack size maxStackSize: int # max stack size
freedObjects: int # max entries in cycle table freedObjects: int # max entries in cycle table
TGcHeap = object # this contains the zero count and GcHeap = object # this contains the zero count and
# non-zero count table # non-zero count table
stackBottom: pointer stackBottom: pointer
cycleThreshold: int cycleThreshold: int
when useCellIds: when useCellIds:
idGenerator: int idGenerator: int
when withBitvectors: when withBitvectors:
allocated, marked: TCellSet allocated, marked: CellSet
tempStack: TCellSeq # temporary stack for recursion elimination tempStack: CellSeq # temporary stack for recursion elimination
recGcLock: int # prevent recursion via finalizers; no thread lock recGcLock: int # prevent recursion via finalizers; no thread lock
region: TMemRegion # garbage collected region region: MemRegion # garbage collected region
stat: TGcStat stat: GcStat
additionalRoots: TCellSeq # dummy roots for GC_ref/unref additionalRoots: CellSeq # dummy roots for GC_ref/unref
{.deprecated: [TWalkOp: WalkOp, TFinalizer: Finalizer, TGcStat: GcStat,
TGlobalMarkerProc: GlobalMarkerProc, TGcHeap, GcHeap].}
var var
gch {.rtlThreadVar.}: TGcHeap gch {.rtlThreadVar.}: GcHeap
when not defined(useNimRtl): when not defined(useNimRtl):
instantiateForRegion(gch.region) instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) = template acquire(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
acquireSys(HeapLock) acquireSys(HeapLock)
template release(gch: TGcHeap) = template release(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap: when hasThreadSupport and hasSharedHeap:
releaseSys(HeapLock) releaseSys(HeapLock)
@ -80,11 +81,11 @@ template gcAssert(cond: bool, msg: string) =
proc cellToUsr(cell: PCell): pointer {.inline.} = proc cellToUsr(cell: PCell): pointer {.inline.} =
# convert object (=pointer to refcount) to pointer to userdata # convert object (=pointer to refcount) to pointer to userdata
result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(TCell))) result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
proc usrToCell(usr: pointer): PCell {.inline.} = proc usrToCell(usr: pointer): PCell {.inline.} =
# convert pointer to userdata to object (=pointer to refcount) # convert pointer to userdata to object (=pointer to refcount)
result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(TCell))) result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
proc canbeCycleRoot(c: PCell): bool {.inline.} = proc canbeCycleRoot(c: PCell): bool {.inline.} =
result = ntfAcyclic notin c.typ.flags result = ntfAcyclic notin c.typ.flags
@ -101,9 +102,9 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
var var
globalMarkersLen: int globalMarkersLen: int
globalMarkers: array[0.. 7_000, TGlobalMarkerProc] globalMarkers: array[0.. 7_000, GlobalMarkerProc]
proc nimRegisterGlobalMarker(markerProc: TGlobalMarkerProc) {.compilerProc.} = proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerProc.} =
if globalMarkersLen <= high(globalMarkers): if globalMarkersLen <= high(globalMarkers):
globalMarkers[globalMarkersLen] = markerProc globalMarkers[globalMarkersLen] = markerProc
inc globalMarkersLen inc globalMarkersLen
@ -116,11 +117,11 @@ when BitsPerPage mod (sizeof(int)*8) != 0:
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".} {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
# forward declarations: # forward declarations:
proc collectCT(gch: var TGcHeap) {.benign.} proc collectCT(gch: var GcHeap) {.benign.}
proc isOnStack*(p: pointer): bool {.noinline, benign.} proc isOnStack*(p: pointer): bool {.noinline, benign.}
proc forAllChildren(cell: PCell, op: TWalkOp) {.benign.} proc forAllChildren(cell: PCell, op: WalkOp) {.benign.}
proc doOperation(p: pointer, op: TWalkOp) {.benign.} proc doOperation(p: pointer, op: WalkOp) {.benign.}
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) {.benign.} proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
# we need the prototype here for debugging purposes # we need the prototype here for debugging purposes
proc prepareDealloc(cell: PCell) = proc prepareDealloc(cell: PCell) =
@ -131,7 +132,7 @@ proc prepareDealloc(cell: PCell) =
# prevend recursive entering here by a lock. # prevend recursive entering here by a lock.
# XXX: we should set the cell's children to nil! # XXX: we should set the cell's children to nil!
inc(gch.recGcLock) inc(gch.recGcLock)
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell)) (cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
dec(gch.recGcLock) dec(gch.recGcLock)
proc nimGCref(p: pointer) {.compilerProc.} = proc nimGCref(p: pointer) {.compilerProc.} =
@ -182,7 +183,7 @@ proc setupForeignThreadGc*() =
setStackBottom(addr(stackTop)) setStackBottom(addr(stackTop))
initGC() initGC()
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.benign.} = proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
case n.kind case n.kind
of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
@ -194,7 +195,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) {.benign.} =
if m != nil: forAllSlotsAux(dest, m, op) if m != nil: forAllSlotsAux(dest, m, op)
of nkNone: sysAssert(false, "forAllSlotsAux") of nkNone: sysAssert(false, "forAllSlotsAux")
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) = proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
var d = cast[ByteAddress](dest) var d = cast[ByteAddress](dest)
if dest == nil: return # nothing to do if dest == nil: return # nothing to do
if ntfNoRefs notin mt.flags: if ntfNoRefs notin mt.flags:
@ -208,7 +209,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) =
forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op) forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
else: discard else: discard
proc forAllChildren(cell: PCell, op: TWalkOp) = proc forAllChildren(cell: PCell, op: WalkOp) =
gcAssert(cell != nil, "forAllChildren: 1") gcAssert(cell != nil, "forAllChildren: 1")
gcAssert(cell.typ != nil, "forAllChildren: 2") gcAssert(cell.typ != nil, "forAllChildren: 2")
gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3" gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3"
@ -228,12 +229,12 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
GenericSeqSize), cell.typ.base, op) GenericSeqSize), cell.typ.base, op)
else: discard else: discard
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer = proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
# generates a new object and sets its reference counter to 0 # generates a new object and sets its reference counter to 0
acquire(gch) acquire(gch)
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
collectCT(gch) collectCT(gch)
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT # now it is buffered in the ZCT
res.typ = typ res.typ = typ
@ -285,20 +286,20 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
cast[PGenericSeq](result).reserved = len cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size) when defined(memProfiler): nimProfile(size)
proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
acquire(gch) acquire(gch)
collectCT(gch) collectCT(gch)
var ol = usrToCell(old) var ol = usrToCell(old)
sysAssert(ol.typ != nil, "growObj: 1") sysAssert(ol.typ != nil, "growObj: 1")
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(TCell))) var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
var elemSize = 1 var elemSize = 1
if ol.typ.kind != tyString: elemSize = ol.typ.base.size if ol.typ.kind != tyString: elemSize = ol.typ.base.size
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
copyMem(res, ol, oldsize + sizeof(TCell)) copyMem(res, ol, oldsize + sizeof(Cell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)), zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
newsize-oldsize) newsize-oldsize)
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
when false: when false:
@ -306,7 +307,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
when withBitvectors: excl(gch.allocated, ol) when withBitvectors: excl(gch.allocated, ol)
when reallyDealloc: rawDealloc(gch.region, ol) when reallyDealloc: rawDealloc(gch.region, ol)
else: else:
zeroMem(ol, sizeof(TCell)) zeroMem(ol, sizeof(Cell))
when withBitvectors: incl(gch.allocated, res) when withBitvectors: incl(gch.allocated, res)
when useCellIds: when useCellIds:
inc gch.idGenerator inc gch.idGenerator
@ -322,7 +323,7 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
# ----------------- collector ----------------------------------------------- # ----------------- collector -----------------------------------------------
proc mark(gch: var TGcHeap, c: PCell) = proc mark(gch: var GcHeap, c: PCell) =
when withBitvectors: when withBitvectors:
incl(gch.marked, c) incl(gch.marked, c)
gcAssert gch.tempStack.len == 0, "stack not empty!" gcAssert gch.tempStack.len == 0, "stack not empty!"
@ -344,7 +345,7 @@ proc mark(gch: var TGcHeap, c: PCell) =
d.refCount = rcBlack d.refCount = rcBlack
forAllChildren(d, waMarkPrecise) forAllChildren(d, waMarkPrecise)
proc doOperation(p: pointer, op: TWalkOp) = proc doOperation(p: pointer, op: WalkOp) =
if p == nil: return if p == nil: return
var c: PCell = usrToCell(p) var c: PCell = usrToCell(p)
gcAssert(c != nil, "doOperation: 1") gcAssert(c != nil, "doOperation: 1")
@ -359,17 +360,17 @@ proc doOperation(p: pointer, op: TWalkOp) =
of waMarkPrecise: add(gch.tempStack, c) of waMarkPrecise: add(gch.tempStack, c)
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
doOperation(d, TWalkOp(op)) doOperation(d, WalkOp(op))
proc freeCyclicCell(gch: var TGcHeap, c: PCell) = proc freeCyclicCell(gch: var GcHeap, c: PCell) =
inc gch.stat.freedObjects inc gch.stat.freedObjects
prepareDealloc(c) prepareDealloc(c)
when reallyDealloc: rawDealloc(gch.region, c) when reallyDealloc: rawDealloc(gch.region, c)
else: else:
gcAssert(c.typ != nil, "freeCyclicCell") gcAssert(c.typ != nil, "freeCyclicCell")
zeroMem(c, sizeof(TCell)) zeroMem(c, sizeof(Cell))
proc sweep(gch: var TGcHeap) = proc sweep(gch: var GcHeap) =
when withBitvectors: when withBitvectors:
for c in gch.allocated.elementsExcept(gch.marked): for c in gch.allocated.elementsExcept(gch.marked):
gch.allocated.excl(c) gch.allocated.excl(c)
@ -391,12 +392,12 @@ when false:
writeStackTrace() writeStackTrace()
quit 1 quit 1
proc markGlobals(gch: var TGcHeap) = proc markGlobals(gch: var GcHeap) =
for i in 0 .. < globalMarkersLen: globalMarkers[i]() for i in 0 .. < globalMarkersLen: globalMarkers[i]()
let d = gch.additionalRoots.d let d = gch.additionalRoots.d
for i in 0 .. < gch.additionalRoots.len: mark(gch, d[i]) for i in 0 .. < gch.additionalRoots.len: mark(gch, d[i])
proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} = proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
# the addresses are not as cells on the stack, so turn them to cells: # the addresses are not as cells on the stack, so turn them to cells:
var cell = usrToCell(p) var cell = usrToCell(p)
var c = cast[ByteAddress](cell) var c = cast[ByteAddress](cell)
@ -446,7 +447,7 @@ when defined(sparc): # For SPARC architecture.
var x = cast[ByteAddress](p) var x = cast[ByteAddress](p)
result = a <=% x and x <=% b result = a <=% x and x <=% b
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
when defined(sparcv9): when defined(sparcv9):
asm """"flushw \n" """ asm """"flushw \n" """
else: else:
@ -479,10 +480,10 @@ elif stackIncreases:
var var
jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
# a little hack to get the size of a TJmpBuf in the generated C code # a little hack to get the size of a JmpBuf in the generated C code
# in a platform independent way # in a platform independent way
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
var registers: C_JmpBuf var registers: C_JmpBuf
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
var max = cast[ByteAddress](gch.stackBottom) var max = cast[ByteAddress](gch.stackBottom)
@ -505,7 +506,7 @@ else:
var x = cast[ByteAddress](p) var x = cast[ByteAddress](p)
result = a <=% x and x <=% b result = a <=% x and x <=% b
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
# We use a jmp_buf buffer that is in the C stack. # We use a jmp_buf buffer that is in the C stack.
# Used to traverse the stack and registers assuming # Used to traverse the stack and registers assuming
# that 'setjmp' will save registers in the C stack. # that 'setjmp' will save registers in the C stack.
@ -543,7 +544,7 @@ else:
# end of non-portable code # end of non-portable code
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
proc collectCTBody(gch: var TGcHeap) = proc collectCTBody(gch: var GcHeap) =
gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize()) gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
prepareForInteriorPointerChecking(gch.region) prepareForInteriorPointerChecking(gch.region)
markStackAndRegisters(gch) markStackAndRegisters(gch)
@ -558,7 +559,7 @@ proc collectCTBody(gch: var TGcHeap) =
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold) gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
sysAssert(allocInv(gch.region), "collectCT: end") sysAssert(allocInv(gch.region), "collectCT: end")
proc collectCT(gch: var TGcHeap) = proc collectCT(gch: var GcHeap) =
if getOccupiedMem(gch.region) >= gch.cycleThreshold and gch.recGcLock == 0: if getOccupiedMem(gch.region) >= gch.cycleThreshold and gch.recGcLock == 0:
collectCTBody(gch) collectCTBody(gch)

View file

@ -15,13 +15,13 @@ else:
proc log*(s: cstring) {.importc: "console.log", varargs, nodecl.} proc log*(s: cstring) {.importc: "console.log", varargs, nodecl.}
type type
PSafePoint = ptr TSafePoint PSafePoint = ptr SafePoint
TSafePoint {.compilerproc, final.} = object SafePoint {.compilerproc, final.} = object
prev: PSafePoint # points to next safe point prev: PSafePoint # points to next safe point
exc: ref Exception exc: ref Exception
PCallFrame = ptr TCallFrame PCallFrame = ptr CallFrame
TCallFrame {.importc, nodecl, final.} = object CallFrame {.importc, nodecl, final.} = object
prev: PCallFrame prev: PCallFrame
procname: cstring procname: cstring
line: int # current line number line: int # current line number
@ -33,6 +33,7 @@ type
lineNumber {.importc.}: int lineNumber {.importc.}: int
message {.importc.}: cstring message {.importc.}: cstring
stack {.importc.}: cstring stack {.importc.}: cstring
{.deprecated: [TSafePoint: SafePoint, TCallFrame: CallFrame].}
var var
framePtr {.importc, nodecl, volatile.}: PCallFrame framePtr {.importc, nodecl, volatile.}: PCallFrame
@ -60,12 +61,13 @@ proc getCurrentExceptionMsg*(): string =
proc auxWriteStackTrace(f: PCallFrame): string = proc auxWriteStackTrace(f: PCallFrame): string =
type type
TTempFrame = tuple[procname: cstring, line: int] TempFrame = tuple[procname: cstring, line: int]
{.deprecated: [TTempFrame: TempFrame].}
var var
it = f it = f
i = 0 i = 0
total = 0 total = 0
tempFrames: array [0..63, TTempFrame] tempFrames: array [0..63, TempFrame]
while it != nil and i <= high(tempFrames): while it != nil and i <= high(tempFrames):
tempFrames[i].procname = it.procname tempFrames[i].procname = it.procname
tempFrames[i].line = it.line tempFrames[i].line = it.line
@ -260,17 +262,17 @@ proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} =
""" """
type type
TDocument {.importc.} = object of RootObj Document {.importc.} = object of RootObj
write: proc (text: cstring) {.nimcall.} write: proc (text: cstring) {.nimcall.}
writeln: proc (text: cstring) {.nimcall.} writeln: proc (text: cstring) {.nimcall.}
createAttribute: proc (identifier: cstring): ref TNode {.nimcall.} createAttribute: proc (identifier: cstring): ref Node {.nimcall.}
createElement: proc (identifier: cstring): ref TNode {.nimcall.} createElement: proc (identifier: cstring): ref Node {.nimcall.}
createTextNode: proc (identifier: cstring): ref TNode {.nimcall.} createTextNode: proc (identifier: cstring): ref Node {.nimcall.}
getElementById: proc (id: cstring): ref TNode {.nimcall.} getElementById: proc (id: cstring): ref Node {.nimcall.}
getElementsByName: proc (name: cstring): seq[ref TNode] {.nimcall.} getElementsByName: proc (name: cstring): seq[ref Node] {.nimcall.}
getElementsByTagName: proc (name: cstring): seq[ref TNode] {.nimcall.} getElementsByTagName: proc (name: cstring): seq[ref Node] {.nimcall.}
TNodeType* = enum NodeType* = enum
ElementNode = 1, ElementNode = 1,
AttributeNode, AttributeNode,
TextNode, TextNode,
@ -283,35 +285,36 @@ type
DocumentTypeNode, DocumentTypeNode,
DocumentFragmentNode, DocumentFragmentNode,
NotationNode NotationNode
TNode* {.importc.} = object of RootObj Node* {.importc.} = object of RootObj
attributes*: seq[ref TNode] attributes*: seq[ref Node]
childNodes*: seq[ref TNode] childNodes*: seq[ref Node]
data*: cstring data*: cstring
firstChild*: ref TNode firstChild*: ref Node
lastChild*: ref TNode lastChild*: ref Node
nextSibling*: ref TNode nextSibling*: ref Node
nodeName*: cstring nodeName*: cstring
nodeType*: TNodeType nodeType*: NodeType
nodeValue*: cstring nodeValue*: cstring
parentNode*: ref TNode parentNode*: ref Node
previousSibling*: ref TNode previousSibling*: ref Node
appendChild*: proc (child: ref TNode) {.nimcall.} appendChild*: proc (child: ref Node) {.nimcall.}
appendData*: proc (data: cstring) {.nimcall.} appendData*: proc (data: cstring) {.nimcall.}
cloneNode*: proc (copyContent: bool) {.nimcall.} cloneNode*: proc (copyContent: bool) {.nimcall.}
deleteData*: proc (start, len: int) {.nimcall.} deleteData*: proc (start, len: int) {.nimcall.}
getAttribute*: proc (attr: cstring): cstring {.nimcall.} getAttribute*: proc (attr: cstring): cstring {.nimcall.}
getAttributeNode*: proc (attr: cstring): ref TNode {.nimcall.} getAttributeNode*: proc (attr: cstring): ref Node {.nimcall.}
getElementsByTagName*: proc (): seq[ref TNode] {.nimcall.} getElementsByTagName*: proc (): seq[ref Node] {.nimcall.}
hasChildNodes*: proc (): bool {.nimcall.} hasChildNodes*: proc (): bool {.nimcall.}
insertBefore*: proc (newNode, before: ref TNode) {.nimcall.} insertBefore*: proc (newNode, before: ref Node) {.nimcall.}
insertData*: proc (position: int, data: cstring) {.nimcall.} insertData*: proc (position: int, data: cstring) {.nimcall.}
removeAttribute*: proc (attr: cstring) {.nimcall.} removeAttribute*: proc (attr: cstring) {.nimcall.}
removeAttributeNode*: proc (attr: ref TNode) {.nimcall.} removeAttributeNode*: proc (attr: ref Node) {.nimcall.}
removeChild*: proc (child: ref TNode) {.nimcall.} removeChild*: proc (child: ref Node) {.nimcall.}
replaceChild*: proc (newNode, oldNode: ref TNode) {.nimcall.} replaceChild*: proc (newNode, oldNode: ref Node) {.nimcall.}
replaceData*: proc (start, len: int, text: cstring) {.nimcall.} replaceData*: proc (start, len: int, text: cstring) {.nimcall.}
setAttribute*: proc (name, value: cstring) {.nimcall.} setAttribute*: proc (name, value: cstring) {.nimcall.}
setAttributeNode*: proc (attr: ref TNode) {.nimcall.} setAttributeNode*: proc (attr: ref Node) {.nimcall.}
{.deprecated: [TNode: Node, TNodeType: NodeType, TDocument: Document].}
when defined(kwin): when defined(kwin):
proc rawEcho {.compilerproc, asmNoStackFrame.} = proc rawEcho {.compilerproc, asmNoStackFrame.} =
@ -337,7 +340,7 @@ elif defined(nodejs):
else: else:
var var
document {.importc, nodecl.}: ref TDocument document {.importc, nodecl.}: ref Document
proc ewriteln(x: cstring) = proc ewriteln(x: cstring) =
var node = document.getElementsByTagName("body")[0] var node = document.getElementsByTagName("body")[0]

View file

@ -34,9 +34,10 @@ const
type type
PPointer = ptr pointer PPointer = ptr pointer
TByteArray = array[0..1000_0000, byte] ByteArray = array[0..1000_0000, byte]
PByte = ptr TByteArray PByte = ptr ByteArray
PString = ptr string PString = ptr string
{.deprecated: [TByteArray: ByteArray].}
# Page size of the system; in most cases 4096 bytes. For exotic OS or # Page size of the system; in most cases 4096 bytes. For exotic OS or
# CPU this needs to be changed: # CPU this needs to be changed:
@ -180,16 +181,17 @@ when defined(boehmgc):
dest[] = src dest[] = src
type type
TMemRegion = object {.final, pure.} MemRegion = object {.final, pure.}
{.deprecated: [TMemRegion: MemRegion].}
proc alloc(r: var TMemRegion, size: int): pointer = proc alloc(r: var MemRegion, size: int): pointer =
result = boehmAlloc(size) result = boehmAlloc(size)
if result == nil: raiseOutOfMem() if result == nil: raiseOutOfMem()
proc alloc0(r: var TMemRegion, size: int): pointer = proc alloc0(r: var MemRegion, size: int): pointer =
result = alloc(size) result = alloc(size)
zeroMem(result, size) zeroMem(result, size)
proc dealloc(r: var TMemRegion, p: pointer) = boehmDealloc(p) proc dealloc(r: var MemRegion, p: pointer) = boehmDealloc(p)
proc deallocOsPages(r: var TMemRegion) {.inline.} = discard proc deallocOsPages(r: var MemRegion) {.inline.} = discard
proc deallocOsPages() {.inline.} = discard proc deallocOsPages() {.inline.} = discard
include "system/cellsets" include "system/cellsets"
@ -257,14 +259,15 @@ elif defined(nogc) and defined(useMalloc):
dest[] = src dest[] = src
type type
TMemRegion = object {.final, pure.} MemRegion = object {.final, pure.}
{.deprecated: [TMemRegion: MemRegion].}
proc alloc(r: var TMemRegion, size: int): pointer = proc alloc(r: var MemRegion, size: int): pointer =
result = alloc(size) result = alloc(size)
proc alloc0(r: var TMemRegion, size: int): pointer = proc alloc0(r: var MemRegion, size: int): pointer =
result = alloc0(size) result = alloc0(size)
proc dealloc(r: var TMemRegion, p: pointer) = dealloc(p) proc dealloc(r: var MemRegion, p: pointer) = dealloc(p)
proc deallocOsPages(r: var TMemRegion) {.inline.} = discard proc deallocOsPages(r: var MemRegion) {.inline.} = discard
proc deallocOsPages() {.inline.} = discard proc deallocOsPages() {.inline.} = discard
elif defined(nogc): elif defined(nogc):
@ -313,7 +316,7 @@ elif defined(nogc):
proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} =
dest[] = src dest[] = src
var allocator {.rtlThreadVar.}: TMemRegion var allocator {.rtlThreadVar.}: MemRegion
instantiateForRegion(allocator) instantiateForRegion(allocator)
include "system/cellsets" include "system/cellsets"
@ -323,7 +326,7 @@ else:
include "system/cellsets" include "system/cellsets"
when not leakDetector: when not leakDetector:
sysAssert(sizeof(TCell) == sizeof(TFreeCell), "sizeof TFreeCell") sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell")
when compileOption("gc", "v2"): when compileOption("gc", "v2"):
include "system/gc2" include "system/gc2"
elif defined(gcMarkAndSweep): elif defined(gcMarkAndSweep):

View file

@ -19,10 +19,11 @@ const
MaxTraceLen = 20 # tracking the last 20 calls is enough MaxTraceLen = 20 # tracking the last 20 calls is enough
type type
TStackTrace* = array [0..MaxTraceLen-1, cstring] StackTrace* = array [0..MaxTraceLen-1, cstring]
TProfilerHook* = proc (st: TStackTrace) {.nimcall.} ProfilerHook* = proc (st: StackTrace) {.nimcall.}
{.deprecated: [TStackTrace: StackTrace, TProfilerHook: ProfilerHook].}
proc captureStackTrace(f: PFrame, st: var TStackTrace) = proc captureStackTrace(f: PFrame, st: var StackTrace) =
const const
firstCalls = 5 firstCalls = 5
var var
@ -51,14 +52,15 @@ proc captureStackTrace(f: PFrame, st: var TStackTrace) =
when defined(memProfiler): when defined(memProfiler):
type type
TMemProfilerHook* = proc (st: TStackTrace, requestedSize: int) {.nimcall, benign.} MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, benign.}
{.deprecated: [TMemProfilerHook: MemProfilerHook].}
var var
profilerHook*: TMemProfilerHook profilerHook*: MemProfilerHook
## set this variable to provide a procedure that implements a profiler in ## set this variable to provide a procedure that implements a profiler in
## user space. See the `nimprof` module for a reference implementation. ## user space. See the `nimprof` module for a reference implementation.
proc callProfilerHook(hook: TMemProfilerHook, requestedSize: int) = proc callProfilerHook(hook: MemProfilerHook, requestedSize: int) =
var st: TStackTrace var st: StackTrace
captureStackTrace(framePtr, st) captureStackTrace(framePtr, st)
hook(st, requestedSize) hook(st, requestedSize)
@ -70,15 +72,15 @@ else:
SamplingInterval = 50_000 SamplingInterval = 50_000
# set this to change the default sampling interval # set this to change the default sampling interval
var var
profilerHook*: TProfilerHook profilerHook*: ProfilerHook
## set this variable to provide a procedure that implements a profiler in ## set this variable to provide a procedure that implements a profiler in
## user space. See the `nimprof` module for a reference implementation. ## user space. See the `nimprof` module for a reference implementation.
gTicker {.threadvar.}: int gTicker {.threadvar.}: int
proc callProfilerHook(hook: TProfilerHook) {.noinline.} = proc callProfilerHook(hook: ProfilerHook) {.noinline.} =
# 'noinline' so that 'nimProfile' does not perform the stack allocation # 'noinline' so that 'nimProfile' does not perform the stack allocation
# in the common case. # in the common case.
var st: TStackTrace var st: StackTrace
captureStackTrace(framePtr, st) captureStackTrace(framePtr, st)
hook(st) hook(st)

View file

@ -121,38 +121,39 @@ proc reprSet(p: pointer, typ: PNimType): string {.compilerRtl.} =
reprSetAux(result, p, typ) reprSetAux(result, p, typ)
type type
TReprClosure {.final.} = object # we cannot use a global variable here ReprClosure {.final.} = object # we cannot use a global variable here
# as this wouldn't be thread-safe # as this wouldn't be thread-safe
when declared(TCellSet): when declared(CellSet):
marked: TCellSet marked: CellSet
recdepth: int # do not recurse endlessly recdepth: int # do not recurse endlessly
indent: int # indentation indent: int # indentation
{.deprecated: [TReprClosure: ReprClosure].}
when not defined(useNimRtl): when not defined(useNimRtl):
proc initReprClosure(cl: var TReprClosure) = proc initReprClosure(cl: var ReprClosure) =
# Important: cellsets does not lock the heap when doing allocations! We # Important: cellsets does not lock the heap when doing allocations! We
# have to do it here ... # have to do it here ...
when hasThreadSupport and hasSharedHeap and declared(heapLock): when hasThreadSupport and hasSharedHeap and declared(heapLock):
AcquireSys(HeapLock) AcquireSys(HeapLock)
when declared(TCellSet): when declared(CellSet):
init(cl.marked) init(cl.marked)
cl.recdepth = -1 # default is to display everything! cl.recdepth = -1 # default is to display everything!
cl.indent = 0 cl.indent = 0
proc deinitReprClosure(cl: var TReprClosure) = proc deinitReprClosure(cl: var ReprClosure) =
when declared(TCellSet): deinit(cl.marked) when declared(CellSet): deinit(cl.marked)
when hasThreadSupport and hasSharedHeap and declared(heapLock): when hasThreadSupport and hasSharedHeap and declared(heapLock):
ReleaseSys(HeapLock) ReleaseSys(HeapLock)
proc reprBreak(result: var string, cl: TReprClosure) = proc reprBreak(result: var string, cl: ReprClosure) =
add result, "\n" add result, "\n"
for i in 0..cl.indent-1: add result, ' ' for i in 0..cl.indent-1: add result, ' '
proc reprAux(result: var string, p: pointer, typ: PNimType, proc reprAux(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) {.benign.} cl: var ReprClosure) {.benign.}
proc reprArray(result: var string, p: pointer, typ: PNimType, proc reprArray(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) = cl: var ReprClosure) =
add result, "[" add result, "["
var bs = typ.base.size var bs = typ.base.size
for i in 0..typ.size div bs - 1: for i in 0..typ.size div bs - 1:
@ -161,7 +162,7 @@ when not defined(useNimRtl):
add result, "]" add result, "]"
proc reprSequence(result: var string, p: pointer, typ: PNimType, proc reprSequence(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) = cl: var ReprClosure) =
if p == nil: if p == nil:
add result, "nil" add result, "nil"
return return
@ -174,7 +175,7 @@ when not defined(useNimRtl):
add result, "]" add result, "]"
proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode, proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode,
cl: var TReprClosure) {.benign.} = cl: var ReprClosure) {.benign.} =
case n.kind case n.kind
of nkNone: sysAssert(false, "reprRecordAux") of nkNone: sysAssert(false, "reprRecordAux")
of nkSlot: of nkSlot:
@ -191,7 +192,7 @@ when not defined(useNimRtl):
if m != nil: reprRecordAux(result, p, m, cl) if m != nil: reprRecordAux(result, p, m, cl)
proc reprRecord(result: var string, p: pointer, typ: PNimType, proc reprRecord(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) = cl: var ReprClosure) =
add result, "[" add result, "["
let oldLen = result.len let oldLen = result.len
reprRecordAux(result, p, typ.node, cl) reprRecordAux(result, p, typ.node, cl)
@ -201,9 +202,9 @@ when not defined(useNimRtl):
add result, "]" add result, "]"
proc reprRef(result: var string, p: pointer, typ: PNimType, proc reprRef(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) = cl: var ReprClosure) =
# we know that p is not nil here: # we know that p is not nil here:
when declared(TCellSet): when declared(CellSet):
when defined(boehmGC) or defined(nogc): when defined(boehmGC) or defined(nogc):
var cell = cast[PCell](p) var cell = cast[PCell](p)
else: else:
@ -216,7 +217,7 @@ when not defined(useNimRtl):
reprAux(result, p, typ.base, cl) reprAux(result, p, typ.base, cl)
proc reprAux(result: var string, p: pointer, typ: PNimType, proc reprAux(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) = cl: var ReprClosure) =
if cl.recdepth == 0: if cl.recdepth == 0:
add result, "..." add result, "..."
return return
@ -261,7 +262,7 @@ when not defined(useNimRtl):
proc reprOpenArray(p: pointer, length: int, elemtyp: PNimType): string {. proc reprOpenArray(p: pointer, length: int, elemtyp: PNimType): string {.
compilerRtl.} = compilerRtl.} =
var var
cl: TReprClosure cl: ReprClosure
initReprClosure(cl) initReprClosure(cl)
result = "[" result = "["
var bs = elemtyp.size var bs = elemtyp.size
@ -274,7 +275,7 @@ proc reprOpenArray(p: pointer, length: int, elemtyp: PNimType): string {.
when not defined(useNimRtl): when not defined(useNimRtl):
proc reprAny(p: pointer, typ: PNimType): string = proc reprAny(p: pointer, typ: PNimType): string =
var var
cl: TReprClosure cl: ReprClosure
initReprClosure(cl) initReprClosure(cl)
result = "" result = ""
if typ.kind in {tyObject, tyTuple, tyArray, tyArrayConstr, tySet}: if typ.kind in {tyObject, tyTuple, tyArray, tyArrayConstr, tySet}:

View file

@ -10,7 +10,8 @@
# set handling # set handling
type type
TNimSet = array [0..4*2048-1, uint8] NimSet = array [0..4*2048-1, uint8]
{.deprecated: [TNimSet: NimSet].}
proc countBits32(n: int32): int {.compilerproc.} = proc countBits32(n: int32): int {.compilerproc.} =
var v = n var v = n
@ -22,7 +23,7 @@ proc countBits64(n: int64): int {.compilerproc.} =
result = countBits32(toU32(n and 0xffff'i64)) + result = countBits32(toU32(n and 0xffff'i64)) +
countBits32(toU32(n shr 16'i64)) countBits32(toU32(n shr 16'i64))
proc cardSet(s: TNimSet, len: int): int {.compilerproc.} = proc cardSet(s: NimSet, len: int): int {.compilerproc.} =
result = 0 result = 0
for i in countup(0, len-1): for i in countup(0, len-1):
inc(result, countBits32(int32(s[i]))) inc(result, countBits32(int32(s[i])))

View file

@ -11,8 +11,8 @@
when defined(Windows): when defined(Windows):
type type
THandle = int Handle = int
TSysLock {.final, pure.} = object # CRITICAL_SECTION in WinApi SysLock {.final, pure.} = object # CRITICAL_SECTION in WinApi
DebugInfo: pointer DebugInfo: pointer
LockCount: int32 LockCount: int32
RecursionCount: int32 RecursionCount: int32
@ -20,85 +20,87 @@ when defined(Windows):
LockSemaphore: int LockSemaphore: int
Reserved: int32 Reserved: int32
TSysCond = THandle SysCond = Handle
{.deprecated: [THandle: Handle, TSysLock: SysLock, TSysCond: SysCond].}
proc initSysLock(L: var TSysLock) {.stdcall, noSideEffect, proc initSysLock(L: var SysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "InitializeCriticalSection".} dynlib: "kernel32", importc: "InitializeCriticalSection".}
## Initializes the lock `L`. ## Initializes the lock `L`.
proc tryAcquireSysAux(L: var TSysLock): int32 {.stdcall, noSideEffect, proc tryAcquireSysAux(L: var SysLock): int32 {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "TryEnterCriticalSection".} dynlib: "kernel32", importc: "TryEnterCriticalSection".}
## Tries to acquire the lock `L`. ## Tries to acquire the lock `L`.
proc tryAcquireSys(L: var TSysLock): bool {.inline.} = proc tryAcquireSys(L: var SysLock): bool {.inline.} =
result = tryAcquireSysAux(L) != 0'i32 result = tryAcquireSysAux(L) != 0'i32
proc acquireSys(L: var TSysLock) {.stdcall, noSideEffect, proc acquireSys(L: var SysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "EnterCriticalSection".} dynlib: "kernel32", importc: "EnterCriticalSection".}
## Acquires the lock `L`. ## Acquires the lock `L`.
proc releaseSys(L: var TSysLock) {.stdcall, noSideEffect, proc releaseSys(L: var SysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "LeaveCriticalSection".} dynlib: "kernel32", importc: "LeaveCriticalSection".}
## Releases the lock `L`. ## Releases the lock `L`.
proc deinitSys(L: var TSysLock) {.stdcall, noSideEffect, proc deinitSys(L: var SysLock) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "DeleteCriticalSection".} dynlib: "kernel32", importc: "DeleteCriticalSection".}
proc createEvent(lpEventAttributes: pointer, proc createEvent(lpEventAttributes: pointer,
bManualReset, bInitialState: int32, bManualReset, bInitialState: int32,
lpName: cstring): TSysCond {.stdcall, noSideEffect, lpName: cstring): SysCond {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "CreateEventA".} dynlib: "kernel32", importc: "CreateEventA".}
proc closeHandle(hObject: THandle) {.stdcall, noSideEffect, proc closeHandle(hObject: Handle) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "CloseHandle".} dynlib: "kernel32", importc: "CloseHandle".}
proc waitForSingleObject(hHandle: THandle, dwMilliseconds: int32): int32 {. proc waitForSingleObject(hHandle: Handle, dwMilliseconds: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "WaitForSingleObject", noSideEffect.} stdcall, dynlib: "kernel32", importc: "WaitForSingleObject", noSideEffect.}
proc signalSysCond(hEvent: TSysCond) {.stdcall, noSideEffect, proc signalSysCond(hEvent: SysCond) {.stdcall, noSideEffect,
dynlib: "kernel32", importc: "SetEvent".} dynlib: "kernel32", importc: "SetEvent".}
proc initSysCond(cond: var TSysCond) {.inline.} = proc initSysCond(cond: var SysCond) {.inline.} =
cond = createEvent(nil, 0'i32, 0'i32, nil) cond = createEvent(nil, 0'i32, 0'i32, nil)
proc deinitSysCond(cond: var TSysCond) {.inline.} = proc deinitSysCond(cond: var SysCond) {.inline.} =
closeHandle(cond) closeHandle(cond)
proc waitSysCond(cond: var TSysCond, lock: var TSysLock) = proc waitSysCond(cond: var SysCond, lock: var SysLock) =
releaseSys(lock) releaseSys(lock)
discard waitForSingleObject(cond, -1'i32) discard waitForSingleObject(cond, -1'i32)
acquireSys(lock) acquireSys(lock)
proc waitSysCondWindows(cond: var TSysCond) = proc waitSysCondWindows(cond: var SysCond) =
discard waitForSingleObject(cond, -1'i32) discard waitForSingleObject(cond, -1'i32)
else: else:
type type
TSysLock {.importc: "pthread_mutex_t", pure, final, SysLock {.importc: "pthread_mutex_t", pure, final,
header: "<sys/types.h>".} = object header: "<sys/types.h>".} = object
TSysCond {.importc: "pthread_cond_t", pure, final, SysCond {.importc: "pthread_cond_t", pure, final,
header: "<sys/types.h>".} = object header: "<sys/types.h>".} = object
proc initSysLock(L: var TSysLock, attr: pointer = nil) {. proc initSysLock(L: var SysLock, attr: pointer = nil) {.
importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.} importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.}
proc acquireSys(L: var TSysLock) {.noSideEffect, proc acquireSys(L: var SysLock) {.noSideEffect,
importc: "pthread_mutex_lock", header: "<pthread.h>".} importc: "pthread_mutex_lock", header: "<pthread.h>".}
proc tryAcquireSysAux(L: var TSysLock): cint {.noSideEffect, proc tryAcquireSysAux(L: var SysLock): cint {.noSideEffect,
importc: "pthread_mutex_trylock", header: "<pthread.h>".} importc: "pthread_mutex_trylock", header: "<pthread.h>".}
proc tryAcquireSys(L: var TSysLock): bool {.inline.} = proc tryAcquireSys(L: var SysLock): bool {.inline.} =
result = tryAcquireSysAux(L) == 0'i32 result = tryAcquireSysAux(L) == 0'i32
proc releaseSys(L: var TSysLock) {.noSideEffect, proc releaseSys(L: var SysLock) {.noSideEffect,
importc: "pthread_mutex_unlock", header: "<pthread.h>".} importc: "pthread_mutex_unlock", header: "<pthread.h>".}
proc deinitSys(L: var TSysLock) {.noSideEffect, proc deinitSys(L: var SysLock) {.noSideEffect,
importc: "pthread_mutex_destroy", header: "<pthread.h>".} importc: "pthread_mutex_destroy", header: "<pthread.h>".}
proc initSysCond(cond: var TSysCond, cond_attr: pointer = nil) {. proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) {.
importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.} importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
proc waitSysCond(cond: var TSysCond, lock: var TSysLock) {. proc waitSysCond(cond: var SysCond, lock: var SysLock) {.
importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.} importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
proc signalSysCond(cond: var TSysCond) {. proc signalSysCond(cond: var SysCond) {.
importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.} importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
proc deinitSysCond(cond: var TSysCond) {.noSideEffect, proc deinitSysCond(cond: var SysCond) {.noSideEffect,
importc: "pthread_cond_destroy", header: "<pthread.h>".} importc: "pthread_cond_destroy", header: "<pthread.h>".}

View file

@ -19,9 +19,9 @@ when not declared(NimString):
type type
CondVar = object CondVar = object
c: TSysCond c: SysCond
when defined(posix): when defined(posix):
stupidLock: TSysLock stupidLock: SysLock
counter: int counter: int
proc createCondVar(): CondVar = proc createCondVar(): CondVar =

View file

@ -24,8 +24,8 @@
## import locks ## import locks
## ##
## var ## var
## thr: array [0..4, TThread[tuple[a,b: int]]] ## thr: array [0..4, Thread[tuple[a,b: int]]]
## L: TLock ## L: Lock
## ##
## proc threadFunc(interval: tuple[a,b: int]) {.thread.} = ## proc threadFunc(interval: tuple[a,b: int]) {.thread.} =
## for i in interval.a..interval.b: ## for i in interval.a..interval.b:
@ -51,40 +51,41 @@ const
when defined(windows): when defined(windows):
type type
TSysThread = THandle SysThread = Handle
TWinThreadProc = proc (x: pointer): int32 {.stdcall.} WinThreadProc = proc (x: pointer): int32 {.stdcall.}
{.deprecated: [TSysThread: SysThread, TWinThreadProc: WinThreadProc].}
proc createThread(lpThreadAttributes: pointer, dwStackSize: int32, proc createThread(lpThreadAttributes: pointer, dwStackSize: int32,
lpStartAddress: TWinThreadProc, lpStartAddress: WinThreadProc,
lpParameter: pointer, lpParameter: pointer,
dwCreationFlags: int32, dwCreationFlags: int32,
lpThreadId: var int32): TSysThread {. lpThreadId: var int32): SysThread {.
stdcall, dynlib: "kernel32", importc: "CreateThread".} stdcall, dynlib: "kernel32", importc: "CreateThread".}
proc winSuspendThread(hThread: TSysThread): int32 {. proc winSuspendThread(hThread: SysThread): int32 {.
stdcall, dynlib: "kernel32", importc: "SuspendThread".} stdcall, dynlib: "kernel32", importc: "SuspendThread".}
proc winResumeThread(hThread: TSysThread): int32 {. proc winResumeThread(hThread: SysThread): int32 {.
stdcall, dynlib: "kernel32", importc: "ResumeThread".} stdcall, dynlib: "kernel32", importc: "ResumeThread".}
proc waitForMultipleObjects(nCount: int32, proc waitForMultipleObjects(nCount: int32,
lpHandles: ptr TSysThread, lpHandles: ptr SysThread,
bWaitAll: int32, bWaitAll: int32,
dwMilliseconds: int32): int32 {. dwMilliseconds: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".} stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}
proc terminateThread(hThread: TSysThread, dwExitCode: int32): int32 {. proc terminateThread(hThread: SysThread, dwExitCode: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "TerminateThread".} stdcall, dynlib: "kernel32", importc: "TerminateThread".}
type type
TThreadVarSlot = distinct int32 ThreadVarSlot = distinct int32
when true: when true:
proc threadVarAlloc(): TThreadVarSlot {. proc threadVarAlloc(): ThreadVarSlot {.
importc: "TlsAlloc", stdcall, header: "<windows.h>".} importc: "TlsAlloc", stdcall, header: "<windows.h>".}
proc threadVarSetValue(dwTlsIndex: TThreadVarSlot, lpTlsValue: pointer) {. proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
importc: "TlsSetValue", stdcall, header: "<windows.h>".} importc: "TlsSetValue", stdcall, header: "<windows.h>".}
proc tlsGetValue(dwTlsIndex: TThreadVarSlot): pointer {. proc tlsGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
importc: "TlsGetValue", stdcall, header: "<windows.h>".} importc: "TlsGetValue", stdcall, header: "<windows.h>".}
proc getLastError(): uint32 {. proc getLastError(): uint32 {.
@ -92,16 +93,16 @@ when defined(windows):
proc setLastError(x: uint32) {. proc setLastError(x: uint32) {.
importc: "SetLastError", stdcall, header: "<windows.h>".} importc: "SetLastError", stdcall, header: "<windows.h>".}
proc threadVarGetValue(dwTlsIndex: TThreadVarSlot): pointer = proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer =
let realLastError = getLastError() let realLastError = getLastError()
result = tlsGetValue(dwTlsIndex) result = tlsGetValue(dwTlsIndex)
setLastError(realLastError) setLastError(realLastError)
else: else:
proc threadVarAlloc(): TThreadVarSlot {. proc threadVarAlloc(): ThreadVarSlot {.
importc: "TlsAlloc", stdcall, dynlib: "kernel32".} importc: "TlsAlloc", stdcall, dynlib: "kernel32".}
proc threadVarSetValue(dwTlsIndex: TThreadVarSlot, lpTlsValue: pointer) {. proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
importc: "TlsSetValue", stdcall, dynlib: "kernel32".} importc: "TlsSetValue", stdcall, dynlib: "kernel32".}
proc threadVarGetValue(dwTlsIndex: TThreadVarSlot): pointer {. proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
importc: "TlsGetValue", stdcall, dynlib: "kernel32".} importc: "TlsGetValue", stdcall, dynlib: "kernel32".}
else: else:
@ -111,55 +112,58 @@ else:
{.passC: "-pthread".} {.passC: "-pthread".}
type type
TSysThread {.importc: "pthread_t", header: "<sys/types.h>", SysThread {.importc: "pthread_t", header: "<sys/types.h>",
final, pure.} = object final, pure.} = object
Tpthread_attr {.importc: "pthread_attr_t", Pthread_attr {.importc: "pthread_attr_t",
header: "<sys/types.h>", final, pure.} = object header: "<sys/types.h>", final, pure.} = object
Ttimespec {.importc: "struct timespec", Timespec {.importc: "struct timespec",
header: "<time.h>", final, pure.} = object header: "<time.h>", final, pure.} = object
tv_sec: int tv_sec: int
tv_nsec: int tv_nsec: int
{.deprecated: [TSysThread: SysThread, Tpthread_attr: PThreadAttr,
Ttimespec: Timespec].}
proc pthread_attr_init(a1: var TPthread_attr) {. proc pthread_attr_init(a1: var PthreadAttr) {.
importc, header: "<pthread.h>".} importc, header: "<pthread.h>".}
proc pthread_attr_setstacksize(a1: var TPthread_attr, a2: int) {. proc pthread_attr_setstacksize(a1: var PthreadAttr, a2: int) {.
importc, header: "<pthread.h>".} importc, header: "<pthread.h>".}
proc pthread_create(a1: var TSysThread, a2: var TPthread_attr, proc pthread_create(a1: var SysThread, a2: var PthreadAttr,
a3: proc (x: pointer): pointer {.noconv.}, a3: proc (x: pointer): pointer {.noconv.},
a4: pointer): cint {.importc: "pthread_create", a4: pointer): cint {.importc: "pthread_create",
header: "<pthread.h>".} header: "<pthread.h>".}
proc pthread_join(a1: TSysThread, a2: ptr pointer): cint {. proc pthread_join(a1: SysThread, a2: ptr pointer): cint {.
importc, header: "<pthread.h>".} importc, header: "<pthread.h>".}
proc pthread_cancel(a1: TSysThread): cint {. proc pthread_cancel(a1: SysThread): cint {.
importc: "pthread_cancel", header: "<pthread.h>".} importc: "pthread_cancel", header: "<pthread.h>".}
type type
TThreadVarSlot {.importc: "pthread_key_t", pure, final, ThreadVarSlot {.importc: "pthread_key_t", pure, final,
header: "<sys/types.h>".} = object header: "<sys/types.h>".} = object
{.deprecated: [TThreadVarSlot: ThreadVarSlot].}
proc pthread_getspecific(a1: TThreadVarSlot): pointer {. proc pthread_getspecific(a1: ThreadVarSlot): pointer {.
importc: "pthread_getspecific", header: "<pthread.h>".} importc: "pthread_getspecific", header: "<pthread.h>".}
proc pthread_key_create(a1: ptr TThreadVarSlot, proc pthread_key_create(a1: ptr ThreadVarSlot,
destruct: proc (x: pointer) {.noconv.}): int32 {. destruct: proc (x: pointer) {.noconv.}): int32 {.
importc: "pthread_key_create", header: "<pthread.h>".} importc: "pthread_key_create", header: "<pthread.h>".}
proc pthread_key_delete(a1: TThreadVarSlot): int32 {. proc pthread_key_delete(a1: ThreadVarSlot): int32 {.
importc: "pthread_key_delete", header: "<pthread.h>".} importc: "pthread_key_delete", header: "<pthread.h>".}
proc pthread_setspecific(a1: TThreadVarSlot, a2: pointer): int32 {. proc pthread_setspecific(a1: ThreadVarSlot, a2: pointer): int32 {.
importc: "pthread_setspecific", header: "<pthread.h>".} importc: "pthread_setspecific", header: "<pthread.h>".}
proc threadVarAlloc(): TThreadVarSlot {.inline.} = proc threadVarAlloc(): ThreadVarSlot {.inline.} =
discard pthread_key_create(addr(result), nil) discard pthread_key_create(addr(result), nil)
proc threadVarSetValue(s: TThreadVarSlot, value: pointer) {.inline.} = proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} =
discard pthread_setspecific(s, value) discard pthread_setspecific(s, value)
proc threadVarGetValue(s: TThreadVarSlot): pointer {.inline.} = proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} =
result = pthread_getspecific(s) result = pthread_getspecific(s)
when useStackMaskHack: when useStackMaskHack:
proc pthread_attr_setstack(attr: var TPthread_attr, stackaddr: pointer, proc pthread_attr_setstack(attr: var PthreadAttr, stackaddr: pointer,
size: int): cint {. size: int): cint {.
importc: "pthread_attr_setstack", header: "<pthread.h>".} importc: "pthread_attr_setstack", header: "<pthread.h>".}
@ -175,13 +179,13 @@ when emulatedThreadVars:
# allocations are needed. Currently less than 7K are used on a 64bit machine. # allocations are needed. Currently less than 7K are used on a 64bit machine.
# We use ``float`` for proper alignment: # We use ``float`` for proper alignment:
type type
TThreadLocalStorage = array [0..1_000, float] ThreadLocalStorage = array [0..1_000, float]
PGcThread = ptr TGcThread PGcThread = ptr GcThread
TGcThread {.pure, inheritable.} = object GcThread {.pure, inheritable.} = object
sys: TSysThread sys: SysThread
when emulatedThreadVars and not useStackMaskHack: when emulatedThreadVars and not useStackMaskHack:
tls: TThreadLocalStorage tls: ThreadLocalStorage
else: else:
nil nil
when hasSharedHeap: when hasSharedHeap:
@ -190,15 +194,16 @@ type
stackSize: int stackSize: int
else: else:
nil nil
{.deprecated: [TThreadLocalStorage: ThreadLocalStorage, TGcThread: GcThread].}
# XXX it'd be more efficient to not use a global variable for the # XXX it'd be more efficient to not use a global variable for the
# thread storage slot, but to rely on the implementation to assign slot X # thread storage slot, but to rely on the implementation to assign slot X
# for us... ;-) # for us... ;-)
var globalsSlot: TThreadVarSlot var globalsSlot: ThreadVarSlot
when not defined(useNimRtl): when not defined(useNimRtl):
when not useStackMaskHack: when not useStackMaskHack:
var mainThread: TGcThread var mainThread: GcThread
proc initThreadVarsEmulation() {.compilerProc, inline.} = proc initThreadVarsEmulation() {.compilerProc, inline.} =
when not defined(useNimRtl): when not defined(useNimRtl):
@ -206,7 +211,7 @@ proc initThreadVarsEmulation() {.compilerProc, inline.} =
when declared(mainThread): when declared(mainThread):
threadVarSetValue(globalsSlot, addr(mainThread)) threadVarSetValue(globalsSlot, addr(mainThread))
#const globalsSlot = TThreadVarSlot(0) #const globalsSlot = ThreadVarSlot(0)
#sysAssert checkSlot.int == globalsSlot.int #sysAssert checkSlot.int == globalsSlot.int
when emulatedThreadVars: when emulatedThreadVars:
@ -228,7 +233,7 @@ when not defined(useNimRtl):
initGC() initGC()
when emulatedThreadVars: when emulatedThreadVars:
if nimThreadVarsSize() > sizeof(TThreadLocalStorage): if nimThreadVarsSize() > sizeof(ThreadLocalStorage):
echo "too large thread local storage size requested" echo "too large thread local storage size requested"
quit 1 quit 1
@ -269,26 +274,27 @@ when not defined(useNimRtl):
# use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway. # use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway.
type type
TThread* {.pure, final.}[TArg] = Thread* {.pure, final.}[TArg] =
object of TGcThread ## Nim thread. A thread is a heavy object (~14K) object of GcThread ## Nim thread. A thread is a heavy object (~14K)
## that **must not** be part of a message! Use ## that **must not** be part of a message! Use
## a ``TThreadId`` for that. ## a ``ThreadId`` for that.
when TArg is void: when TArg is void:
dataFn: proc () {.nimcall, gcsafe.} dataFn: proc () {.nimcall, gcsafe.}
else: else:
dataFn: proc (m: TArg) {.nimcall, gcsafe.} dataFn: proc (m: TArg) {.nimcall, gcsafe.}
data: TArg data: TArg
TThreadId*[TArg] = ptr TThread[TArg] ## the current implementation uses ThreadId*[TArg] = ptr Thread[TArg] ## the current implementation uses
## a pointer as a thread ID. ## a pointer as a thread ID.
{.deprecated: [TThread: Thread, TThreadId: ThreadId].}
when not defined(boehmgc) and not hasSharedHeap: when not defined(boehmgc) and not hasSharedHeap:
proc deallocOsPages() proc deallocOsPages()
template threadProcWrapperBody(closure: expr) {.immediate.} = template threadProcWrapperBody(closure: expr) {.immediate.} =
when declared(globalsSlot): threadVarSetValue(globalsSlot, closure) when declared(globalsSlot): threadVarSetValue(globalsSlot, closure)
var t = cast[ptr TThread[TArg]](closure) var t = cast[ptr Thread[TArg]](closure)
when useStackMaskHack: when useStackMaskHack:
var tls: TThreadLocalStorage var tls: ThreadLocalStorage
when not defined(boehmgc) and not defined(nogc) and not hasSharedHeap: when not defined(boehmgc) and not defined(nogc) and not hasSharedHeap:
# init the GC for this thread: # init the GC for this thread:
setStackBottom(addr(t)) setStackBottom(addr(t))
@ -319,35 +325,35 @@ else:
threadProcWrapperBody(closure) threadProcWrapperBody(closure)
{.pop.} {.pop.}
proc running*[TArg](t: TThread[TArg]): bool {.inline.} = proc running*[TArg](t: Thread[TArg]): bool {.inline.} =
## returns true if `t` is running. ## returns true if `t` is running.
result = t.dataFn != nil result = t.dataFn != nil
when hostOS == "windows": when hostOS == "windows":
proc joinThread*[TArg](t: TThread[TArg]) {.inline.} = proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
## waits for the thread `t` to finish. ## waits for the thread `t` to finish.
discard waitForSingleObject(t.sys, -1'i32) discard waitForSingleObject(t.sys, -1'i32)
proc joinThreads*[TArg](t: varargs[TThread[TArg]]) = proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
## waits for every thread in `t` to finish. ## waits for every thread in `t` to finish.
var a: array[0..255, TSysThread] var a: array[0..255, SysThread]
sysAssert a.len >= t.len, "a.len >= t.len" sysAssert a.len >= t.len, "a.len >= t.len"
for i in 0..t.high: a[i] = t[i].sys for i in 0..t.high: a[i] = t[i].sys
discard waitForMultipleObjects(t.len.int32, discard waitForMultipleObjects(t.len.int32,
cast[ptr TSysThread](addr(a)), 1, -1) cast[ptr SysThread](addr(a)), 1, -1)
else: else:
proc joinThread*[TArg](t: TThread[TArg]) {.inline.} = proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
## waits for the thread `t` to finish. ## waits for the thread `t` to finish.
discard pthread_join(t.sys, nil) discard pthread_join(t.sys, nil)
proc joinThreads*[TArg](t: varargs[TThread[TArg]]) = proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
## waits for every thread in `t` to finish. ## waits for every thread in `t` to finish.
for i in 0..t.high: joinThread(t[i]) for i in 0..t.high: joinThread(t[i])
when false: when false:
# XXX a thread should really release its heap here somehow: # XXX a thread should really release its heap here somehow:
proc destroyThread*[TArg](t: var TThread[TArg]) = proc destroyThread*[TArg](t: var Thread[TArg]) =
## forces the thread `t` to terminate. This is potentially dangerous if ## forces the thread `t` to terminate. This is potentially dangerous if
## you don't have full control over `t` and its acquired resources. ## you don't have full control over `t` and its acquired resources.
when hostOS == "windows": when hostOS == "windows":
@ -358,7 +364,7 @@ when false:
t.dataFn = nil t.dataFn = nil
when hostOS == "windows": when hostOS == "windows":
proc createThread*[TArg](t: var TThread[TArg], proc createThread*[TArg](t: var Thread[TArg],
tp: proc (arg: TArg) {.thread.}, tp: proc (arg: TArg) {.thread.},
param: TArg) = param: TArg) =
## creates a new thread `t` and starts its execution. Entry point is the ## creates a new thread `t` and starts its execution. Entry point is the
@ -373,7 +379,7 @@ when hostOS == "windows":
if t.sys <= 0: if t.sys <= 0:
raise newException(ResourceExhaustedError, "cannot create thread") raise newException(ResourceExhaustedError, "cannot create thread")
else: else:
proc createThread*[TArg](t: var TThread[TArg], proc createThread*[TArg](t: var Thread[TArg],
tp: proc (arg: TArg) {.thread.}, tp: proc (arg: TArg) {.thread.},
param: TArg) = param: TArg) =
## creates a new thread `t` and starts its execution. Entry point is the ## creates a new thread `t` and starts its execution. Entry point is the
@ -382,29 +388,29 @@ else:
when TArg isnot void: t.data = param when TArg isnot void: t.data = param
t.dataFn = tp t.dataFn = tp
when hasSharedHeap: t.stackSize = ThreadStackSize when hasSharedHeap: t.stackSize = ThreadStackSize
var a {.noinit.}: Tpthread_attr var a {.noinit.}: PthreadAttr
pthread_attr_init(a) pthread_attr_init(a)
pthread_attr_setstacksize(a, ThreadStackSize) pthread_attr_setstacksize(a, ThreadStackSize)
if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0: if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0:
raise newException(ResourceExhaustedError, "cannot create thread") raise newException(ResourceExhaustedError, "cannot create thread")
proc threadId*[TArg](t: var TThread[TArg]): TThreadId[TArg] {.inline.} = proc threadId*[TArg](t: var Thread[TArg]): ThreadId[TArg] {.inline.} =
## returns the thread ID of `t`. ## returns the thread ID of `t`.
result = addr(t) result = addr(t)
proc myThreadId*[TArg](): TThreadId[TArg] = proc myThreadId*[TArg](): ThreadId[TArg] =
## returns the thread ID of the thread that calls this proc. This is unsafe ## returns the thread ID of the thread that calls this proc. This is unsafe
## because the type ``TArg`` is not checked for consistency! ## because the type ``TArg`` is not checked for consistency!
result = cast[TThreadId[TArg]](threadVarGetValue(globalsSlot)) result = cast[ThreadId[TArg]](threadVarGetValue(globalsSlot))
when false: when false:
proc mainThreadId*[TArg](): TThreadId[TArg] = proc mainThreadId*[TArg](): ThreadId[TArg] =
## returns the thread ID of the main thread. ## returns the thread ID of the main thread.
result = cast[TThreadId[TArg]](addr(mainThread)) result = cast[ThreadId[TArg]](addr(mainThread))
when useStackMaskHack: when useStackMaskHack:
proc runMain(tp: proc () {.thread.}) {.compilerproc.} = proc runMain(tp: proc () {.thread.}) {.compilerproc.} =
var mainThread: TThread[pointer] var mainThread: Thread[pointer]
createThread(mainThread, tp) createThread(mainThread, tp)
joinThread(mainThread) joinThread(mainThread)

View file

@ -11,83 +11,86 @@
## `<https://github.com/jckarter/clay/blob/master/compiler/src/hirestimer.cpp>`_ ## `<https://github.com/jckarter/clay/blob/master/compiler/src/hirestimer.cpp>`_
type type
TTicks = distinct int64 Ticks = distinct int64
TNanos = int64 Nanos = int64
{.deprecated: [TTicks: Ticks, TNanos: Nanos].}
when defined(windows): when defined(windows):
proc QueryPerformanceCounter(res: var TTicks) {. proc QueryPerformanceCounter(res: var Ticks) {.
importc: "QueryPerformanceCounter", stdcall, dynlib: "kernel32".} importc: "QueryPerformanceCounter", stdcall, dynlib: "kernel32".}
proc QueryPerformanceFrequency(res: var int64) {. proc QueryPerformanceFrequency(res: var int64) {.
importc: "QueryPerformanceFrequency", stdcall, dynlib: "kernel32".} importc: "QueryPerformanceFrequency", stdcall, dynlib: "kernel32".}
proc getTicks(): TTicks {.inline.} = proc getTicks(): Ticks {.inline.} =
QueryPerformanceCounter(result) QueryPerformanceCounter(result)
proc `-`(a, b: TTicks): TNanos = proc `-`(a, b: Ticks): Nanos =
var frequency: int64 var frequency: int64
QueryPerformanceFrequency(frequency) QueryPerformanceFrequency(frequency)
var performanceCounterRate = 1e+9'f64 / float64(frequency) var performanceCounterRate = 1e+9'f64 / float64(frequency)
result = TNanos(float64(a.int64 - b.int64) * performanceCounterRate) result = Nanos(float64(a.int64 - b.int64) * performanceCounterRate)
elif defined(macosx): elif defined(macosx):
type type
TMachTimebaseInfoData {.pure, final, MachTimebaseInfoData {.pure, final,
importc: "mach_timebase_info_data_t", importc: "mach_timebase_info_data_t",
header: "<mach/mach_time.h>".} = object header: "<mach/mach_time.h>".} = object
numer, denom: int32 numer, denom: int32
{.deprecated: [TMachTimebaseInfoData: MachTimebaseInfoData].}
proc mach_absolute_time(): int64 {.importc, header: "<mach/mach.h>".} proc mach_absolute_time(): int64 {.importc, header: "<mach/mach.h>".}
proc mach_timebase_info(info: var TMachTimebaseInfoData) {.importc, proc mach_timebase_info(info: var MachTimebaseInfoData) {.importc,
header: "<mach/mach_time.h>".} header: "<mach/mach_time.h>".}
proc getTicks(): TTicks {.inline.} = proc getTicks(): Ticks {.inline.} =
result = TTicks(mach_absolute_time()) result = Ticks(mach_absolute_time())
var timeBaseInfo: TMachTimebaseInfoData var timeBaseInfo: MachTimebaseInfoData
mach_timebase_info(timeBaseInfo) mach_timebase_info(timeBaseInfo)
proc `-`(a, b: TTicks): TNanos = proc `-`(a, b: Ticks): Nanos =
result = (a.int64 - b.int64) * timeBaseInfo.numer div timeBaseInfo.denom result = (a.int64 - b.int64) * timeBaseInfo.numer div timeBaseInfo.denom
elif defined(posixRealtime): elif defined(posixRealtime):
type type
TClockid {.importc: "clockid_t", header: "<time.h>", final.} = object Clockid {.importc: "clockid_t", header: "<time.h>", final.} = object
TTimeSpec {.importc: "struct timespec", header: "<time.h>", TimeSpec {.importc: "struct timespec", header: "<time.h>",
final, pure.} = object ## struct timespec final, pure.} = object ## struct timespec
tv_sec: int ## Seconds. tv_sec: int ## Seconds.
tv_nsec: int ## Nanoseconds. tv_nsec: int ## Nanoseconds.
{.deprecated: [TClockid: Clickid, TTimeSpec: TimeSpec].}
var var
CLOCK_REALTIME {.importc: "CLOCK_REALTIME", header: "<time.h>".}: TClockid CLOCK_REALTIME {.importc: "CLOCK_REALTIME", header: "<time.h>".}: Clockid
proc clock_gettime(clkId: TClockid, tp: var TTimespec) {. proc clock_gettime(clkId: Clockid, tp: var Timespec) {.
importc: "clock_gettime", header: "<time.h>".} importc: "clock_gettime", header: "<time.h>".}
proc getTicks(): TTicks = proc getTicks(): Ticks =
var t: TTimespec var t: Timespec
clock_gettime(CLOCK_REALTIME, t) clock_gettime(CLOCK_REALTIME, t)
result = TTicks(int64(t.tv_sec) * 1000000000'i64 + int64(t.tv_nsec)) result = Ticks(int64(t.tv_sec) * 1000000000'i64 + int64(t.tv_nsec))
proc `-`(a, b: TTicks): TNanos {.borrow.} proc `-`(a, b: Ticks): Nanos {.borrow.}
else: else:
# fallback Posix implementation: # fallback Posix implementation:
type type
Ttimeval {.importc: "struct timeval", header: "<sys/select.h>", Timeval {.importc: "struct timeval", header: "<sys/select.h>",
final, pure.} = object ## struct timeval final, pure.} = object ## struct timeval
tv_sec: int ## Seconds. tv_sec: int ## Seconds.
tv_usec: int ## Microseconds. tv_usec: int ## Microseconds.
{.deprecated: [Ttimeval: Timeval].}
proc posix_gettimeofday(tp: var Ttimeval, unused: pointer = nil) {. proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
importc: "gettimeofday", header: "<sys/time.h>".} importc: "gettimeofday", header: "<sys/time.h>".}
proc getTicks(): TTicks = proc getTicks(): Ticks =
var t: Ttimeval var t: Timeval
posix_gettimeofday(t) posix_gettimeofday(t)
result = TTicks(int64(t.tv_sec) * 1000_000_000'i64 + result = Ticks(int64(t.tv_sec) * 1000_000_000'i64 +
int64(t.tv_usec) * 1000'i64) int64(t.tv_usec) * 1000'i64)
proc `-`(a, b: TTicks): TNanos {.borrow.} proc `-`(a, b: Ticks): Nanos {.borrow.}

View file

@ -14,8 +14,9 @@ when not declared(NimString):
{.error: "You must not import this module explicitly".} {.error: "You must not import this module explicitly".}
type type
TUtf16Char* = distinct int16 Utf16Char* = distinct int16
WideCString* = ref array[0.. 1_000_000, TUtf16Char] WideCString* = ref array[0.. 1_000_000, Utf16Char]
{.deprecated: [TUtf16Char: Utf16Char].}
proc len*(w: WideCString): int = proc len*(w: WideCString): int =
## returns the length of a widestring. This traverses the whole string to ## returns the length of a widestring. This traverses the whole string to
@ -23,7 +24,7 @@ proc len*(w: WideCString): int =
while int16(w[result]) != 0'i16: inc result while int16(w[result]) != 0'i16: inc result
const const
UNI_REPLACEMENT_CHAR = TUtf16Char(0xFFFD'i16) UNI_REPLACEMENT_CHAR = Utf16Char(0xFFFD'i16)
UNI_MAX_BMP = 0x0000FFFF UNI_MAX_BMP = 0x0000FFFF
UNI_MAX_UTF16 = 0x0010FFFF UNI_MAX_UTF16 = 0x0010FFFF
UNI_MAX_UTF32 = 0x7FFFFFFF UNI_MAX_UTF32 = 0x7FFFFFFF
@ -89,16 +90,16 @@ proc newWideCString*(source: cstring, L: int): WideCString =
if ch >=% UNI_SUR_HIGH_START and ch <=% UNI_SUR_LOW_END: if ch >=% UNI_SUR_HIGH_START and ch <=% UNI_SUR_LOW_END:
result[d] = UNI_REPLACEMENT_CHAR result[d] = UNI_REPLACEMENT_CHAR
else: else:
result[d] = TUtf16Char(toU16(ch)) result[d] = Utf16Char(toU16(ch))
elif ch >% UNI_MAX_UTF16: elif ch >% UNI_MAX_UTF16:
result[d] = UNI_REPLACEMENT_CHAR result[d] = UNI_REPLACEMENT_CHAR
else: else:
let ch = ch -% halfBase let ch = ch -% halfBase
result[d] = TUtf16Char(toU16((ch shr halfShift) +% UNI_SUR_HIGH_START)) result[d] = Utf16Char(toU16((ch shr halfShift) +% UNI_SUR_HIGH_START))
inc d inc d
result[d] = TUtf16Char(toU16((ch and halfMask) +% UNI_SUR_LOW_START)) result[d] = Utf16Char(toU16((ch and halfMask) +% UNI_SUR_LOW_START))
inc d inc d
result[d] = TUtf16Char(0'i16) result[d] = Utf16Char(0'i16)
proc newWideCString*(s: cstring): WideCString = proc newWideCString*(s: cstring): WideCString =
if s.isNil: return nil if s.isNil: return nil