Merge branch 'devel' into gogc

This commit is contained in:
Stefan Talpalaru 2015-06-15 08:44:02 +02:00
commit bdac85f2cf
212 changed files with 8304 additions and 7492 deletions

View file

@ -98,46 +98,49 @@ const
SmallChunkSize = PageSize
type
PTrunk = ptr TTrunk
TTrunk {.final.} = object
PTrunk = ptr Trunk
Trunk {.final.} = object
next: PTrunk # all nodes are connected with this pointer
key: int # start address at bit 0
bits: array[0..IntsPerTrunk-1, int] # a bit vector
TTrunkBuckets = array[0..255, PTrunk]
TIntSet {.final.} = object
data: TTrunkBuckets
TrunkBuckets = array[0..255, PTrunk]
IntSet {.final.} = object
data: TrunkBuckets
{.deprecated: [TIntSet: IntSet, TTrunk: Trunk, TTrunkBuckets: TrunkBuckets].}
type
TAlignType = BiggestFloat
TFreeCell {.final, pure.} = object
next: ptr TFreeCell # next free cell in chunk (overlaid with refcount)
AlignType = BiggestFloat
FreeCell {.final, pure.} = object
next: ptr FreeCell # next free cell in chunk (overlaid with refcount)
zeroField: int # 0 means cell is not used (overlaid with typ field)
# 1 means cell is manually managed pointer
# otherwise a PNimType is stored in there
PChunk = ptr TBaseChunk
PBigChunk = ptr TBigChunk
PSmallChunk = ptr TSmallChunk
TBaseChunk {.pure, inheritable.} = object
PChunk = ptr BaseChunk
PBigChunk = ptr BigChunk
PSmallChunk = ptr SmallChunk
BaseChunk {.pure, inheritable.} = object
prevSize: int # size of previous chunk; for coalescing
size: int # if < PageSize it is a small chunk
used: bool # later will be optimized into prevSize...
TSmallChunk = object of TBaseChunk
SmallChunk = object of BaseChunk
next, prev: PSmallChunk # chunks of the same size
freeList: ptr TFreeCell
freeList: ptr FreeCell
free: int # how many bytes remain
acc: int # accumulator for small object allocation
data: TAlignType # start of usable memory
data: AlignType # start of usable memory
TBigChunk = object of TBaseChunk # not necessarily > PageSize!
BigChunk = object of BaseChunk # not necessarily > PageSize!
next, prev: PBigChunk # chunks of the same (or bigger) size
align: int
data: TAlignType # start of usable memory
data: AlignType # start of usable memory
{.deprecated: [TAlignType: AlignType, TFreeCell: FreeCell, TBaseChunk: BaseChunk,
TBigChunk: BigChunk, TSmallChunk: SmallChunk].}
template smallChunkOverhead(): expr = sizeof(TSmallChunk)-sizeof(TAlignType)
template bigChunkOverhead(): expr = sizeof(TBigChunk)-sizeof(TAlignType)
template smallChunkOverhead(): expr = sizeof(SmallChunk)-sizeof(AlignType)
template bigChunkOverhead(): expr = sizeof(BigChunk)-sizeof(AlignType)
proc roundup(x, v: int): int {.inline.} =
result = (x + (v-1)) and not (v-1)
@ -156,31 +159,32 @@ sysAssert(roundup(65, 8) == 72, "roundup broken 2")
# to the OS), a fixed size array can be used.
type
PLLChunk = ptr TLLChunk
TLLChunk {.pure.} = object ## *low-level* chunk
PLLChunk = ptr LLChunk
LLChunk {.pure.} = object ## *low-level* chunk
size: int # remaining size
acc: int # accumulator
next: PLLChunk # next low-level chunk; only needed for dealloc
PAvlNode = ptr TAvlNode
TAvlNode {.pure, final.} = object
PAvlNode = ptr AvlNode
AvlNode {.pure, final.} = object
link: array[0..1, PAvlNode] # Left (0) and right (1) links
key, upperBound: int
level: int
TMemRegion {.final, pure.} = object
MemRegion {.final, pure.} = object
minLargeObj, maxLargeObj: int
freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
llmem: PLLChunk
currMem, maxMem, freeMem: int # memory sizes (allocated from OS)
lastSize: int # needed for the case that OS gives us pages linearly
freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
chunkStarts: TIntSet
chunkStarts: IntSet
root, deleted, last, freeAvlNodes: PAvlNode
{.deprecated: [TLLChunk: LLChunk, TAvlNode: AvlNode, TMemRegion: MemRegion].}
# shared:
var
bottomData: TAvlNode
bottomData: AvlNode
bottom: PAvlNode
{.push stack_trace: off.}
@ -191,44 +195,44 @@ proc initAllocator() =
bottom.link[1] = bottom
{.pop.}
proc incCurrMem(a: var TMemRegion, bytes: int) {.inline.} =
proc incCurrMem(a: var MemRegion, bytes: int) {.inline.} =
inc(a.currMem, bytes)
proc decCurrMem(a: var TMemRegion, bytes: int) {.inline.} =
proc decCurrMem(a: var MemRegion, bytes: int) {.inline.} =
a.maxMem = max(a.maxMem, a.currMem)
dec(a.currMem, bytes)
proc getMaxMem(a: var TMemRegion): int =
proc getMaxMem(a: var MemRegion): int =
# Since we update maxPagesCount only when freeing pages,
# maxPagesCount may not be up to date. Thus we use the
# maximum of these both values here:
result = max(a.currMem, a.maxMem)
proc llAlloc(a: var TMemRegion, size: int): pointer =
proc llAlloc(a: var MemRegion, size: int): pointer =
# *low-level* alloc for the memory managers data structures. Deallocation
# is done at he end of the allocator's life time.
if a.llmem == nil or size > a.llmem.size:
# the requested size is ``roundup(size+sizeof(TLLChunk), PageSize)``, but
# the requested size is ``roundup(size+sizeof(LLChunk), PageSize)``, but
# since we know ``size`` is a (small) constant, we know the requested size
# is one page:
sysAssert roundup(size+sizeof(TLLChunk), PageSize) == PageSize, "roundup 6"
sysAssert roundup(size+sizeof(LLChunk), PageSize) == PageSize, "roundup 6"
var old = a.llmem # can be nil and is correct with nil
a.llmem = cast[PLLChunk](osAllocPages(PageSize))
incCurrMem(a, PageSize)
a.llmem.size = PageSize - sizeof(TLLChunk)
a.llmem.acc = sizeof(TLLChunk)
a.llmem.size = PageSize - sizeof(LLChunk)
a.llmem.acc = sizeof(LLChunk)
a.llmem.next = old
result = cast[pointer](cast[ByteAddress](a.llmem) + a.llmem.acc)
dec(a.llmem.size, size)
inc(a.llmem.acc, size)
zeroMem(result, size)
proc allocAvlNode(a: var TMemRegion, key, upperBound: int): PAvlNode =
proc allocAvlNode(a: var MemRegion, key, upperBound: int): PAvlNode =
if a.freeAvlNodes != nil:
result = a.freeAvlNodes
a.freeAvlNodes = a.freeAvlNodes.link[0]
else:
result = cast[PAvlNode](llAlloc(a, sizeof(TAvlNode)))
result = cast[PAvlNode](llAlloc(a, sizeof(AvlNode)))
result.key = key
result.upperBound = upperBound
result.link[0] = bottom
@ -238,13 +242,13 @@ proc allocAvlNode(a: var TMemRegion, key, upperBound: int): PAvlNode =
sysAssert(bottom.link[0] == bottom, "bottom link[0]")
sysAssert(bottom.link[1] == bottom, "bottom link[1]")
proc deallocAvlNode(a: var TMemRegion, n: PAvlNode) {.inline.} =
proc deallocAvlNode(a: var MemRegion, n: PAvlNode) {.inline.} =
n.link[0] = a.freeAvlNodes
a.freeAvlNodes = n
include "system/avltree"
proc llDeallocAll(a: var TMemRegion) =
proc llDeallocAll(a: var MemRegion) =
var it = a.llmem
while it != nil:
# we know each block in the list has the size of 1 page:
@ -252,14 +256,14 @@ proc llDeallocAll(a: var TMemRegion) =
osDeallocPages(it, PageSize)
it = next
proc intSetGet(t: TIntSet, key: int): PTrunk =
proc intSetGet(t: IntSet, key: int): PTrunk =
var it = t.data[key and high(t.data)]
while it != nil:
if it.key == key: return it
it = it.next
result = nil
proc intSetPut(a: var TMemRegion, t: var TIntSet, key: int): PTrunk =
proc intSetPut(a: var MemRegion, t: var IntSet, key: int): PTrunk =
result = intSetGet(t, key)
if result == nil:
result = cast[PTrunk](llAlloc(a, sizeof(result[])))
@ -267,7 +271,7 @@ proc intSetPut(a: var TMemRegion, t: var TIntSet, key: int): PTrunk =
t.data[key and high(t.data)] = result
result.key = key
proc contains(s: TIntSet, key: int): bool =
proc contains(s: IntSet, key: int): bool =
var t = intSetGet(s, key shr TrunkShift)
if t != nil:
var u = key and TrunkMask
@ -275,19 +279,19 @@ proc contains(s: TIntSet, key: int): bool =
else:
result = false
proc incl(a: var TMemRegion, s: var TIntSet, key: int) =
proc incl(a: var MemRegion, s: var IntSet, key: int) =
var t = intSetPut(a, s, key shr TrunkShift)
var u = key and TrunkMask
t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
proc excl(s: var TIntSet, key: int) =
proc excl(s: var IntSet, key: int) =
var t = intSetGet(s, key shr TrunkShift)
if t != nil:
var u = key and TrunkMask
t.bits[u shr IntShift] = t.bits[u shr IntShift] and not
(1 shl (u and IntMask))
iterator elements(t: TIntSet): int {.inline.} =
iterator elements(t: IntSet): int {.inline.} =
# while traversing it is forbidden to change the set!
for h in 0..high(t.data):
var r = t.data[h]
@ -311,7 +315,7 @@ proc isSmallChunk(c: PChunk): bool {.inline.} =
proc chunkUnused(c: PChunk): bool {.inline.} =
result = not c.used
iterator allObjects(m: TMemRegion): pointer {.inline.} =
iterator allObjects(m: MemRegion): pointer {.inline.} =
for s in elements(m.chunkStarts):
# we need to check here again as it could have been modified:
if s in m.chunkStarts:
@ -331,7 +335,7 @@ iterator allObjects(m: TMemRegion): pointer {.inline.} =
yield addr(c.data)
proc isCell(p: pointer): bool {.inline.} =
result = cast[ptr TFreeCell](p).zeroField >% 1
result = cast[ptr FreeCell](p).zeroField >% 1
# ------------- chunk management ----------------------------------------------
proc pageIndex(c: PChunk): int {.inline.} =
@ -344,7 +348,7 @@ proc pageAddr(p: pointer): PChunk {.inline.} =
result = cast[PChunk](cast[ByteAddress](p) and not PageMask)
#sysAssert(Contains(allocator.chunkStarts, pageIndex(result)))
proc requestOsChunks(a: var TMemRegion, size: int): PBigChunk =
proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
incCurrMem(a, size)
inc(a.freeMem, size)
result = cast[PBigChunk](osAllocPages(size))
@ -373,7 +377,7 @@ proc requestOsChunks(a: var TMemRegion, size: int): PBigChunk =
result.prevSize = 0 # unknown
a.lastSize = size # for next request
proc freeOsChunks(a: var TMemRegion, p: pointer, size: int) =
proc freeOsChunks(a: var MemRegion, p: pointer, size: int) =
# update next.prevSize:
var c = cast[PChunk](p)
var nxt = cast[ByteAddress](p) +% c.size
@ -387,7 +391,7 @@ proc freeOsChunks(a: var TMemRegion, p: pointer, size: int) =
dec(a.freeMem, size)
#c_fprintf(c_stdout, "[Alloc] back to OS: %ld\n", size)
proc isAccessible(a: TMemRegion, p: pointer): bool {.inline.} =
proc isAccessible(a: MemRegion, p: pointer): bool {.inline.} =
result = contains(a.chunkStarts, pageIndex(p))
proc contains[T](list, x: T): bool =
@ -396,7 +400,7 @@ proc contains[T](list, x: T): bool =
if it == x: return true
it = it.next
proc writeFreeList(a: TMemRegion) =
proc writeFreeList(a: MemRegion) =
var it = a.freeChunksList
c_fprintf(c_stdout, "freeChunksList: %p\n", it)
while it != nil:
@ -427,14 +431,14 @@ proc listRemove[T](head: var T, c: T) {.inline.} =
c.next = nil
c.prev = nil
proc updatePrevSize(a: var TMemRegion, c: PBigChunk,
proc updatePrevSize(a: var MemRegion, c: PBigChunk,
prevSize: int) {.inline.} =
var ri = cast[PChunk](cast[ByteAddress](c) +% c.size)
sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "updatePrevSize")
if isAccessible(a, ri):
ri.prevSize = prevSize
proc freeBigChunk(a: var TMemRegion, c: PBigChunk) =
proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
var c = c
sysAssert(c.size >= PageSize, "freeBigChunk")
inc(a.freeMem, c.size)
@ -467,7 +471,7 @@ proc freeBigChunk(a: var TMemRegion, c: PBigChunk) =
else:
freeOsChunks(a, c, c.size)
proc splitChunk(a: var TMemRegion, c: PBigChunk, size: int) =
proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) =
var rest = cast[PBigChunk](cast[ByteAddress](c) +% size)
sysAssert(rest notin a.freeChunksList, "splitChunk")
rest.size = c.size - size
@ -480,7 +484,7 @@ proc splitChunk(a: var TMemRegion, c: PBigChunk, size: int) =
incl(a, a.chunkStarts, pageIndex(rest))
listAdd(a.freeChunksList, rest)
proc getBigChunk(a: var TMemRegion, size: int): PBigChunk =
proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
# use first fit for now:
sysAssert((size and PageMask) == 0, "getBigChunk 1")
sysAssert(size > 0, "getBigChunk 2")
@ -507,16 +511,16 @@ proc getBigChunk(a: var TMemRegion, size: int): PBigChunk =
incl(a, a.chunkStarts, pageIndex(result))
dec(a.freeMem, size)
proc getSmallChunk(a: var TMemRegion): PSmallChunk =
proc getSmallChunk(a: var MemRegion): PSmallChunk =
var res = getBigChunk(a, PageSize)
sysAssert res.prev == nil, "getSmallChunk 1"
sysAssert res.next == nil, "getSmallChunk 2"
result = cast[PSmallChunk](res)
# -----------------------------------------------------------------------------
proc isAllocatedPtr(a: TMemRegion, p: pointer): bool {.benign.}
proc isAllocatedPtr(a: MemRegion, p: pointer): bool {.benign.}
proc allocInv(a: TMemRegion): bool =
proc allocInv(a: MemRegion): bool =
## checks some (not all yet) invariants of the allocator's data structures.
for s in low(a.freeSmallChunks)..high(a.freeSmallChunks):
var c = a.freeSmallChunks[s]
@ -537,10 +541,10 @@ proc allocInv(a: TMemRegion): bool =
c = c.next
result = true
proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer =
proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
sysAssert(allocInv(a), "rawAlloc: begin")
sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
sysAssert(requestedSize >= sizeof(TFreeCell), "rawAlloc: requested size too small")
sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small")
var size = roundup(requestedSize, MemAlign)
sysAssert(size >= requestedSize, "insufficient allocated size!")
#c_fprintf(c_stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
@ -601,11 +605,11 @@ proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer =
sysAssert(allocInv(a), "rawAlloc: end")
when logAlloc: cprintf("rawAlloc: %ld %p\n", requestedSize, result)
proc rawAlloc0(a: var TMemRegion, requestedSize: int): pointer =
proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
result = rawAlloc(a, requestedSize)
zeroMem(result, requestedSize)
proc rawDealloc(a: var TMemRegion, p: pointer) =
proc rawDealloc(a: var MemRegion, p: pointer) =
#sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
sysAssert(allocInv(a), "rawDealloc: begin")
var c = pageAddr(p)
@ -615,7 +619,7 @@ proc rawDealloc(a: var TMemRegion, p: pointer) =
var s = c.size
sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %%
s == 0, "rawDealloc 3")
var f = cast[ptr TFreeCell](p)
var f = cast[ptr FreeCell](p)
#echo("setting to nil: ", $cast[TAddress](addr(f.zeroField)))
sysAssert(f.zeroField != 0, "rawDealloc 1")
f.zeroField = 0
@ -623,8 +627,8 @@ proc rawDealloc(a: var TMemRegion, p: pointer) =
c.freeList = f
when overwriteFree:
# set to 0xff to check for usage after free bugs:
c_memset(cast[pointer](cast[int](p) +% sizeof(TFreeCell)), -1'i32,
s -% sizeof(TFreeCell))
c_memset(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
s -% sizeof(FreeCell))
# check if it is not in the freeSmallChunks[s] list:
if c.free < s:
# add it to the freeSmallChunks[s] array:
@ -649,7 +653,7 @@ proc rawDealloc(a: var TMemRegion, p: pointer) =
sysAssert(allocInv(a), "rawDealloc: end")
when logAlloc: cprintf("rawDealloc: %p\n", p)
proc isAllocatedPtr(a: TMemRegion, p: pointer): bool =
proc isAllocatedPtr(a: MemRegion, p: pointer): bool =
if isAccessible(a, p):
var c = pageAddr(p)
if not chunkUnused(c):
@ -658,16 +662,16 @@ proc isAllocatedPtr(a: TMemRegion, p: pointer): bool =
var offset = (cast[ByteAddress](p) and (PageSize-1)) -%
smallChunkOverhead()
result = (c.acc >% offset) and (offset %% c.size == 0) and
(cast[ptr TFreeCell](p).zeroField >% 1)
(cast[ptr FreeCell](p).zeroField >% 1)
else:
var c = cast[PBigChunk](c)
result = p == addr(c.data) and cast[ptr TFreeCell](p).zeroField >% 1
result = p == addr(c.data) and cast[ptr FreeCell](p).zeroField >% 1
proc prepareForInteriorPointerChecking(a: var TMemRegion) {.inline.} =
proc prepareForInteriorPointerChecking(a: var MemRegion) {.inline.} =
a.minLargeObj = lowGauge(a.root)
a.maxLargeObj = highGauge(a.root)
proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer =
proc interiorAllocatedPtr(a: MemRegion, p: pointer): pointer =
if isAccessible(a, p):
var c = pageAddr(p)
if not chunkUnused(c):
@ -678,7 +682,7 @@ proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer =
if c.acc >% offset:
sysAssert(cast[ByteAddress](addr(c.data)) +% offset ==
cast[ByteAddress](p), "offset is not what you think it is")
var d = cast[ptr TFreeCell](cast[ByteAddress](addr(c.data)) +%
var d = cast[ptr FreeCell](cast[ByteAddress](addr(c.data)) +%
offset -% (offset %% c.size))
if d.zeroField >% 1:
result = d
@ -686,7 +690,7 @@ proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer =
else:
var c = cast[PBigChunk](c)
var d = addr(c.data)
if p >= d and cast[ptr TFreeCell](d).zeroField >% 1:
if p >= d and cast[ptr FreeCell](d).zeroField >% 1:
result = d
sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
else:
@ -699,38 +703,38 @@ proc interiorAllocatedPtr(a: TMemRegion, p: pointer): pointer =
var k = cast[pointer](avlNode.key)
var c = cast[PBigChunk](pageAddr(k))
sysAssert(addr(c.data) == k, " k is not the same as addr(c.data)!")
if cast[ptr TFreeCell](k).zeroField >% 1:
if cast[ptr FreeCell](k).zeroField >% 1:
result = k
sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
proc ptrSize(p: pointer): int =
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(TFreeCell))
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
var c = pageAddr(p)
sysAssert(not chunkUnused(c), "ptrSize")
result = c.size -% sizeof(TFreeCell)
result = c.size -% sizeof(FreeCell)
if not isSmallChunk(c):
dec result, bigChunkOverhead()
proc alloc(allocator: var TMemRegion, size: Natural): pointer =
result = rawAlloc(allocator, size+sizeof(TFreeCell))
cast[ptr TFreeCell](result).zeroField = 1 # mark it as used
proc alloc(allocator: var MemRegion, size: Natural): pointer =
result = rawAlloc(allocator, size+sizeof(FreeCell))
cast[ptr FreeCell](result).zeroField = 1 # mark it as used
sysAssert(not isAllocatedPtr(allocator, result), "alloc")
result = cast[pointer](cast[ByteAddress](result) +% sizeof(TFreeCell))
result = cast[pointer](cast[ByteAddress](result) +% sizeof(FreeCell))
proc alloc0(allocator: var TMemRegion, size: Natural): pointer =
proc alloc0(allocator: var MemRegion, size: Natural): pointer =
result = alloc(allocator, size)
zeroMem(result, size)
proc dealloc(allocator: var TMemRegion, p: pointer) =
proc dealloc(allocator: var MemRegion, p: pointer) =
sysAssert(p != nil, "dealloc 0")
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(TFreeCell))
var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
sysAssert(x != nil, "dealloc 1")
sysAssert(isAccessible(allocator, x), "is not accessible")
sysAssert(cast[ptr TFreeCell](x).zeroField == 1, "dealloc 2")
sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc 2")
rawDealloc(allocator, x)
sysAssert(not isAllocatedPtr(allocator, x), "dealloc 3")
proc realloc(allocator: var TMemRegion, p: pointer, newsize: Natural): pointer =
proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer =
if newsize > 0:
result = alloc0(allocator, newsize)
if p != nil:
@ -739,7 +743,7 @@ proc realloc(allocator: var TMemRegion, p: pointer, newsize: Natural): pointer =
elif p != nil:
dealloc(allocator, p)
proc deallocOsPages(a: var TMemRegion) =
proc deallocOsPages(a: var MemRegion) =
# we free every 'ordinarily' allocated page by iterating over the page bits:
for p in elements(a.chunkStarts):
var page = cast[PChunk](p shl PageShift)
@ -756,9 +760,9 @@ proc deallocOsPages(a: var TMemRegion) =
# And then we free the pages that are in use for the page bits:
llDeallocAll(a)
proc getFreeMem(a: TMemRegion): int {.inline.} = result = a.freeMem
proc getTotalMem(a: TMemRegion): int {.inline.} = result = a.currMem
proc getOccupiedMem(a: TMemRegion): int {.inline.} =
proc getFreeMem(a: MemRegion): int {.inline.} = result = a.freeMem
proc getTotalMem(a: MemRegion): int {.inline.} = result = a.currMem
proc getOccupiedMem(a: MemRegion): int {.inline.} =
result = a.currMem - a.freeMem
# ---------------------- thread memory region -------------------------------
@ -769,7 +773,7 @@ template instantiateForRegion(allocator: expr) =
result = interiorAllocatedPtr(allocator, p)
proc isAllocatedPtr*(p: pointer): bool =
let p = cast[pointer](cast[ByteAddress](p)-%ByteAddress(sizeof(TCell)))
let p = cast[pointer](cast[ByteAddress](p)-%ByteAddress(sizeof(Cell)))
result = isAllocatedPtr(allocator, p)
proc deallocOsPages = deallocOsPages(allocator)
@ -803,8 +807,8 @@ template instantiateForRegion(allocator: expr) =
# -------------------- shared heap region ----------------------------------
when hasThreadSupport:
var sharedHeap: TMemRegion
var heapLock: TSysLock
var sharedHeap: MemRegion
var heapLock: SysLock
initSysLock(heapLock)
proc allocShared(size: Natural): pointer =

View file

@ -17,17 +17,114 @@ proc raiseOverflow {.compilerproc, noinline.} =
proc raiseDivByZero {.compilerproc, noinline.} =
sysFatal(DivByZeroError, "division by zero")
proc addInt64(a, b: int64): int64 {.compilerProc, inline.} =
result = a +% b
if (result xor a) >= int64(0) or (result xor b) >= int64(0):
return result
raiseOverflow()
when defined(builtinOverflow):
# Builtin compiler functions for improved performance
when sizeof(clong) == 8:
proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_saddl_overflow", nodecl, nosideeffect.}
proc subInt64(a, b: int64): int64 {.compilerProc, inline.} =
result = a -% b
if (result xor a) >= int64(0) or (result xor not b) >= int64(0):
return result
raiseOverflow()
proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_ssubl_overflow", nodecl, nosideeffect.}
proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_smull_overflow", nodecl, nosideeffect.}
elif sizeof(clonglong) == 8:
proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_saddll_overflow", nodecl, nosideeffect.}
proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_ssubll_overflow", nodecl, nosideeffect.}
proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
importc: "__builtin_smulll_overflow", nodecl, nosideeffect.}
when sizeof(int) == 8:
proc addIntOverflow(a, b: int, c: var int): bool {.inline.} =
addInt64Overflow(a, b, c)
proc subIntOverflow(a, b: int, c: var int): bool {.inline.} =
subInt64Overflow(a, b, c)
proc mulIntOverflow(a, b: int, c: var int): bool {.inline.} =
mulInt64Overflow(a, b, c)
elif sizeof(int) == 4 and sizeof(cint) == 4:
proc addIntOverflow(a, b: int, c: var int): bool {.
importc: "__builtin_sadd_overflow", nodecl, nosideeffect.}
proc subIntOverflow(a, b: int, c: var int): bool {.
importc: "__builtin_ssub_overflow", nodecl, nosideeffect.}
proc mulIntOverflow(a, b: int, c: var int): bool {.
importc: "__builtin_smul_overflow", nodecl, nosideeffect.}
proc addInt64(a, b: int64): int64 {.compilerProc, inline.} =
if addInt64Overflow(a, b, result):
raiseOverflow()
proc subInt64(a, b: int64): int64 {.compilerProc, inline.} =
if subInt64Overflow(a, b, result):
raiseOverflow()
proc mulInt64(a, b: int64): int64 {.compilerproc, inline.} =
if mulInt64Overflow(a, b, result):
raiseOverflow()
else:
proc addInt64(a, b: int64): int64 {.compilerProc, inline.} =
result = a +% b
if (result xor a) >= int64(0) or (result xor b) >= int64(0):
return result
raiseOverflow()
proc subInt64(a, b: int64): int64 {.compilerProc, inline.} =
result = a -% b
if (result xor a) >= int64(0) or (result xor not b) >= int64(0):
return result
raiseOverflow()
#
# This code has been inspired by Python's source code.
# The native int product x*y is either exactly right or *way* off, being
# just the last n bits of the true product, where n is the number of bits
# in an int (the delivered product is the true product plus i*2**n for
# some integer i).
#
# The native float64 product x*y is subject to three
# rounding errors: on a sizeof(int)==8 box, each cast to double can lose
# info, and even on a sizeof(int)==4 box, the multiplication can lose info.
# But, unlike the native int product, it's not in *range* trouble: even
# if sizeof(int)==32 (256-bit ints), the product easily fits in the
# dynamic range of a float64. So the leading 50 (or so) bits of the float64
# product are correct.
#
# We check these two ways against each other, and declare victory if they're
# approximately the same. Else, because the native int product is the only
# one that can lose catastrophic amounts of information, it's the native int
# product that must have overflowed.
#
proc mulInt64(a, b: int64): int64 {.compilerproc.} =
var
resAsFloat, floatProd: float64
result = a *% b
floatProd = toBiggestFloat(a) # conversion
floatProd = floatProd * toBiggestFloat(b)
resAsFloat = toBiggestFloat(result)
# Fast path for normal case: small multiplicands, and no info
# is lost in either method.
if resAsFloat == floatProd: return result
# Somebody somewhere lost info. Close enough, or way off? Note
# that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
# The difference either is or isn't significant compared to the
# true value (of which floatProd is a good approximation).
# abs(diff)/abs(prod) <= 1/32 iff
# 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
return result
raiseOverflow()
proc negInt64(a: int64): int64 {.compilerProc, inline.} =
if a != low(int64): return -a
@ -51,50 +148,6 @@ proc modInt64(a, b: int64): int64 {.compilerProc, inline.} =
raiseDivByZero()
return a mod b
#
# This code has been inspired by Python's source code.
# The native int product x*y is either exactly right or *way* off, being
# just the last n bits of the true product, where n is the number of bits
# in an int (the delivered product is the true product plus i*2**n for
# some integer i).
#
# The native float64 product x*y is subject to three
# rounding errors: on a sizeof(int)==8 box, each cast to double can lose
# info, and even on a sizeof(int)==4 box, the multiplication can lose info.
# But, unlike the native int product, it's not in *range* trouble: even
# if sizeof(int)==32 (256-bit ints), the product easily fits in the
# dynamic range of a float64. So the leading 50 (or so) bits of the float64
# product are correct.
#
# We check these two ways against each other, and declare victory if they're
# approximately the same. Else, because the native int product is the only
# one that can lose catastrophic amounts of information, it's the native int
# product that must have overflowed.
#
proc mulInt64(a, b: int64): int64 {.compilerproc.} =
var
resAsFloat, floatProd: float64
result = a *% b
floatProd = toBiggestFloat(a) # conversion
floatProd = floatProd * toBiggestFloat(b)
resAsFloat = toBiggestFloat(result)
# Fast path for normal case: small multiplicands, and no info
# is lost in either method.
if resAsFloat == floatProd: return result
# Somebody somewhere lost info. Close enough, or way off? Note
# that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
# The difference either is or isn't significant compared to the
# true value (of which floatProd is a good approximation).
# abs(diff)/abs(prod) <= 1/32 iff
# 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd):
return result
raiseOverflow()
proc absInt(a: int): int {.compilerProc, inline.} =
if a != low(int):
if a >= 0: return a
@ -246,6 +299,21 @@ elif false: # asmVersion and (defined(gcc) or defined(llvm_gcc)):
:"%edx"
"""
when not declared(addInt) and defined(builtinOverflow):
proc addInt(a, b: int): int {.compilerProc, inline.} =
if addIntOverflow(a, b, result):
raiseOverflow()
when not declared(subInt) and defined(builtinOverflow):
proc subInt(a, b: int): int {.compilerProc, inline.} =
if subIntOverflow(a, b, result):
raiseOverflow()
when not declared(mulInt) and defined(builtinOverflow):
proc mulInt(a, b: int): int {.compilerProc, inline.} =
if mulIntOverflow(a, b, result):
raiseOverflow()
# Platform independent versions of the above (slower!)
when not declared(addInt):
proc addInt(a, b: int): int {.compilerProc, inline.} =

View file

@ -37,39 +37,40 @@ when someGcc and hasThreadSupport:
## and release stores in all threads.
type
TAtomType* = SomeNumber|pointer|ptr|char|bool
AtomType* = SomeNumber|pointer|ptr|char|bool
## Type Class representing valid types for use with atomic procs
{.deprecated: [TAtomType: AtomType].}
proc atomicLoadN*[T: TAtomType](p: ptr T, mem: AtomMemModel): T {.
proc atomicLoadN*[T: AtomType](p: ptr T, mem: AtomMemModel): T {.
importc: "__atomic_load_n", nodecl.}
## This proc implements an atomic load operation. It returns the contents at p.
## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_CONSUME.
proc atomicLoad*[T: TAtomType](p, ret: ptr T, mem: AtomMemModel) {.
proc atomicLoad*[T: AtomType](p, ret: ptr T, mem: AtomMemModel) {.
importc: "__atomic_load", nodecl.}
## This is the generic version of an atomic load. It returns the contents at p in ret.
proc atomicStoreN*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel) {.
proc atomicStoreN*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel) {.
importc: "__atomic_store_n", nodecl.}
## This proc implements an atomic store operation. It writes val at p.
## ATOMIC_RELAXED, ATOMIC_SEQ_CST, and ATOMIC_RELEASE.
proc atomicStore*[T: TAtomType](p, val: ptr T, mem: AtomMemModel) {.
proc atomicStore*[T: AtomType](p, val: ptr T, mem: AtomMemModel) {.
importc: "__atomic_store", nodecl.}
## This is the generic version of an atomic store. It stores the value of val at p
proc atomicExchangeN*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
proc atomicExchangeN*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_exchange_n", nodecl.}
## This proc implements an atomic exchange operation. It writes val at p,
## and returns the previous contents at p.
## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_RELEASE, ATOMIC_ACQ_REL
proc atomicExchange*[T: TAtomType](p, val, ret: ptr T, mem: AtomMemModel) {.
proc atomicExchange*[T: AtomType](p, val, ret: ptr T, mem: AtomMemModel) {.
importc: "__atomic_exchange", nodecl.}
## This is the generic version of an atomic exchange. It stores the contents at val at p.
## The original value at p is copied into ret.
proc atomicCompareExchangeN*[T: TAtomType](p, expected: ptr T, desired: T,
proc atomicCompareExchangeN*[T: AtomType](p, expected: ptr T, desired: T,
weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {.
importc: "__atomic_compare_exchange_n ", nodecl.}
## This proc implements an atomic compare and exchange operation. This compares the
@ -85,7 +86,7 @@ when someGcc and hasThreadSupport:
## cannot be __ATOMIC_RELEASE nor __ATOMIC_ACQ_REL. It also cannot be a stronger model
## than that specified by success_memmodel.
proc atomicCompareExchange*[T: TAtomType](p, expected, desired: ptr T,
proc atomicCompareExchange*[T: AtomType](p, expected, desired: ptr T,
weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {.
importc: "__atomic_compare_exchange", nodecl.}
## This proc implements the generic version of atomic_compare_exchange.
@ -93,31 +94,31 @@ when someGcc and hasThreadSupport:
## value is also a pointer.
## Perform the operation return the new value, all memory models are valid
proc atomicAddFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
proc atomicAddFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_add_fetch", nodecl.}
proc atomicSubFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
proc atomicSubFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_sub_fetch", nodecl.}
proc atomicOrFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
proc atomicOrFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_or_fetch ", nodecl.}
proc atomicAndFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
proc atomicAndFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_and_fetch", nodecl.}
proc atomicXorFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
proc atomicXorFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_xor_fetch", nodecl.}
proc atomicNandFetch*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
proc atomicNandFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_nand_fetch ", nodecl.}
## Perform the operation return the old value, all memory models are valid
proc atomicFetchAdd*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
proc atomicFetchAdd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_fetch_add", nodecl.}
proc atomicFetchSub*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
proc atomicFetchSub*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_fetch_sub", nodecl.}
proc atomicFetchOr*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
proc atomicFetchOr*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_fetch_or", nodecl.}
proc atomicFetchAnd*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
proc atomicFetchAnd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_fetch_and", nodecl.}
proc atomicFetchXor*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
proc atomicFetchXor*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_fetch_xor", nodecl.}
proc atomicFetchNand*[T: TAtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
proc atomicFetchNand*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
importc: "__atomic_fetch_nand", nodecl.}
proc atomicTestAndSet*(p: pointer, mem: AtomMemModel): bool {.
@ -190,11 +191,11 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
result = memLoc
when defined(windows) and not someGcc:
proc interlockedCompareExchange(p: pointer; exchange, comparand: int32): int32
proc interlockedCompareExchange(p: pointer; exchange, comparand: int): int
{.importc: "InterlockedCompareExchange", header: "<windows.h>", cdecl.}
proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
interlockedCompareExchange(p, newValue.int32, oldValue.int32) != 0
interlockedCompareExchange(p, cast[int](newValue), cast[int](oldValue)) != 0
# XXX fix for 64 bit build
else:
# this is valid for GCC and Intel C++

View file

@ -51,7 +51,7 @@ proc split(t: var PAvlNode) =
t.link[0] = temp
inc t.level
proc add(a: var TMemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} =
proc add(a: var MemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} =
if t == bottom:
t = allocAvlNode(a, key, upperBound)
else:
@ -64,7 +64,7 @@ proc add(a: var TMemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} =
skew(t)
split(t)
proc del(a: var TMemRegion, t: var PAvlNode, x: int) {.benign.} =
proc del(a: var MemRegion, t: var PAvlNode, x: int) {.benign.} =
if t == bottom: return
a.last = t
if x <% t.key:

View file

@ -10,10 +10,10 @@
# Efficient set of pointers for the GC (and repr)
type
TRefCount = int
RefCount = int
TCell {.pure.} = object
refcount: TRefCount # the refcount and some flags
Cell {.pure.} = object
refcount: RefCount # the refcount and some flags
typ: PNimType
when trackAllocationSource:
filename: cstring
@ -21,34 +21,35 @@ type
when useCellIds:
id: int
PCell = ptr TCell
PCell = ptr Cell
PPageDesc = ptr TPageDesc
TBitIndex = range[0..UnitsPerPage-1]
TPageDesc {.final, pure.} = object
PPageDesc = ptr PageDesc
BitIndex = range[0..UnitsPerPage-1]
PageDesc {.final, pure.} = object
next: PPageDesc # all nodes are connected with this pointer
key: ByteAddress # start address at bit 0
bits: array[TBitIndex, int] # a bit vector
bits: array[BitIndex, int] # a bit vector
PPageDescArray = ptr array[0..1000_000, PPageDesc]
TCellSet {.final, pure.} = object
CellSet {.final, pure.} = object
counter, max: int
head: PPageDesc
data: PPageDescArray
PCellArray = ptr array[0..100_000_000, PCell]
TCellSeq {.final, pure.} = object
CellSeq {.final, pure.} = object
len, cap: int
d: PCellArray
{.deprecated: [TCell: Cell, TBitIndex: BitIndex, TPageDesc: PageDesc,
TRefCount: RefCount, TCellSet: CellSet, TCellSeq: CellSeq].}
# ------------------- cell seq handling ---------------------------------------
proc contains(s: TCellSeq, c: PCell): bool {.inline.} =
proc contains(s: CellSeq, c: PCell): bool {.inline.} =
for i in 0 .. s.len-1:
if s.d[i] == c: return true
return false
proc add(s: var TCellSeq, c: PCell) {.inline.} =
proc add(s: var CellSeq, c: PCell) {.inline.} =
if s.len >= s.cap:
s.cap = s.cap * 3 div 2
var d = cast[PCellArray](alloc(s.cap * sizeof(PCell)))
@ -59,12 +60,12 @@ proc add(s: var TCellSeq, c: PCell) {.inline.} =
s.d[s.len] = c
inc(s.len)
proc init(s: var TCellSeq, cap: int = 1024) =
proc init(s: var CellSeq, cap: int = 1024) =
s.len = 0
s.cap = cap
s.d = cast[PCellArray](alloc0(cap * sizeof(PCell)))
proc deinit(s: var TCellSeq) =
proc deinit(s: var CellSeq) =
dealloc(s.d)
s.d = nil
s.len = 0
@ -75,13 +76,13 @@ proc deinit(s: var TCellSeq) =
const
InitCellSetSize = 1024 # must be a power of two!
proc init(s: var TCellSet) =
proc init(s: var CellSet) =
s.data = cast[PPageDescArray](alloc0(InitCellSetSize * sizeof(PPageDesc)))
s.max = InitCellSetSize-1
s.counter = 0
s.head = nil
proc deinit(s: var TCellSet) =
proc deinit(s: var CellSet) =
var it = s.head
while it != nil:
var n = it.next
@ -98,14 +99,14 @@ proc nextTry(h, maxHash: int): int {.inline.} =
# generates each int in range(maxHash) exactly once (see any text on
# random-number generation for proof).
proc cellSetGet(t: TCellSet, key: ByteAddress): PPageDesc =
proc cellSetGet(t: CellSet, key: ByteAddress): PPageDesc =
var h = cast[int](key) and t.max
while t.data[h] != nil:
if t.data[h].key == key: return t.data[h]
h = nextTry(h, t.max)
return nil
proc cellSetRawInsert(t: TCellSet, data: PPageDescArray, desc: PPageDesc) =
proc cellSetRawInsert(t: CellSet, data: PPageDescArray, desc: PPageDesc) =
var h = cast[int](desc.key) and t.max
while data[h] != nil:
sysAssert(data[h] != desc, "CellSetRawInsert 1")
@ -113,7 +114,7 @@ proc cellSetRawInsert(t: TCellSet, data: PPageDescArray, desc: PPageDesc) =
sysAssert(data[h] == nil, "CellSetRawInsert 2")
data[h] = desc
proc cellSetEnlarge(t: var TCellSet) =
proc cellSetEnlarge(t: var CellSet) =
var oldMax = t.max
t.max = ((t.max+1)*2)-1
var n = cast[PPageDescArray](alloc0((t.max + 1) * sizeof(PPageDesc)))
@ -123,7 +124,7 @@ proc cellSetEnlarge(t: var TCellSet) =
dealloc(t.data)
t.data = n
proc cellSetPut(t: var TCellSet, key: ByteAddress): PPageDesc =
proc cellSetPut(t: var CellSet, key: ByteAddress): PPageDesc =
var h = cast[int](key) and t.max
while true:
var x = t.data[h]
@ -138,7 +139,7 @@ proc cellSetPut(t: var TCellSet, key: ByteAddress): PPageDesc =
while t.data[h] != nil: h = nextTry(h, t.max)
sysAssert(t.data[h] == nil, "CellSetPut")
# the new page descriptor goes into result
result = cast[PPageDesc](alloc0(sizeof(TPageDesc)))
result = cast[PPageDesc](alloc0(sizeof(PageDesc)))
result.next = t.head
result.key = key
t.head = result
@ -146,7 +147,7 @@ proc cellSetPut(t: var TCellSet, key: ByteAddress): PPageDesc =
# ---------- slightly higher level procs --------------------------------------
proc contains(s: TCellSet, cell: PCell): bool =
proc contains(s: CellSet, cell: PCell): bool =
var u = cast[ByteAddress](cell)
var t = cellSetGet(s, u shr PageShift)
if t != nil:
@ -155,13 +156,13 @@ proc contains(s: TCellSet, cell: PCell): bool =
else:
result = false
proc incl(s: var TCellSet, cell: PCell) {.noinline.} =
proc incl(s: var CellSet, cell: PCell) {.noinline.} =
var u = cast[ByteAddress](cell)
var t = cellSetPut(s, u shr PageShift)
u = (u %% PageSize) /% MemAlign
t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
proc excl(s: var TCellSet, cell: PCell) =
proc excl(s: var CellSet, cell: PCell) =
var u = cast[ByteAddress](cell)
var t = cellSetGet(s, u shr PageShift)
if t != nil:
@ -169,7 +170,7 @@ proc excl(s: var TCellSet, cell: PCell) =
t.bits[u shr IntShift] = (t.bits[u shr IntShift] and
not (1 shl (u and IntMask)))
proc containsOrIncl(s: var TCellSet, cell: PCell): bool =
proc containsOrIncl(s: var CellSet, cell: PCell): bool =
var u = cast[ByteAddress](cell)
var t = cellSetGet(s, u shr PageShift)
if t != nil:
@ -182,7 +183,7 @@ proc containsOrIncl(s: var TCellSet, cell: PCell): bool =
incl(s, cell)
result = false
iterator elements(t: TCellSet): PCell {.inline.} =
iterator elements(t: CellSet): PCell {.inline.} =
# while traversing it is forbidden to add pointers to the tree!
var r = t.head
while r != nil:
@ -200,7 +201,7 @@ iterator elements(t: TCellSet): PCell {.inline.} =
inc(i)
r = r.next
iterator elementsExcept(t, s: TCellSet): PCell {.inline.} =
iterator elementsExcept(t, s: CellSet): PCell {.inline.} =
var r = t.head
while r != nil:
let ss = cellSetGet(s, r.key)

View file

@ -12,12 +12,13 @@
proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.}
type
TLibHandle = pointer # private type
TProcAddr = pointer # library loading and loading of procs:
LibHandle = pointer # private type
ProcAddr = pointer # library loading and loading of procs:
{.deprecated: [TLibHandle: LibHandle, TProcAddr: ProcAddr].}
proc nimLoadLibrary(path: string): TLibHandle {.compilerproc.}
proc nimUnloadLibrary(lib: TLibHandle) {.compilerproc.}
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr {.compilerproc.}
proc nimLoadLibrary(path: string): LibHandle {.compilerproc.}
proc nimUnloadLibrary(lib: LibHandle) {.compilerproc.}
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.compilerproc.}
proc nimLoadLibraryError(path: string) {.compilerproc, noinline.}

View file

@ -19,18 +19,20 @@ when not declared(NimString):
type
pbytes = ptr array[0.. 0xffff, byte]
TRawChannel {.pure, final.} = object ## msg queue for a thread
RawChannel {.pure, final.} = object ## msg queue for a thread
rd, wr, count, mask: int
data: pbytes
lock: TSysLock
cond: TSysCond
lock: SysLock
cond: SysCond
elemType: PNimType
ready: bool
region: TMemRegion
PRawChannel = ptr TRawChannel
TLoadStoreMode = enum mStore, mLoad
TChannel* {.gcsafe.}[TMsg] = TRawChannel ## a channel for thread communication
region: MemRegion
PRawChannel = ptr RawChannel
LoadStoreMode = enum mStore, mLoad
Channel* {.gcsafe.}[TMsg] = RawChannel ## a channel for thread communication
{.deprecated: [TRawChannel: RawChannel, TLoadStoreMode: LoadStoreMode,
TChannel: Channel].}
const ChannelDeadMask = -2
proc initRawChannel(p: pointer) =
@ -49,9 +51,9 @@ proc deinitRawChannel(p: pointer) =
deinitSysCond(c.cond)
proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
mode: TLoadStoreMode) {.benign.}
mode: LoadStoreMode) {.benign.}
proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
mode: TLoadStoreMode) {.benign.} =
mode: LoadStoreMode) {.benign.} =
var
d = cast[ByteAddress](dest)
s = cast[ByteAddress](src)
@ -68,7 +70,7 @@ proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
of nkNone: sysAssert(false, "storeAux")
proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
mode: TLoadStoreMode) =
mode: LoadStoreMode) =
var
d = cast[ByteAddress](dest)
s = cast[ByteAddress](src)
@ -202,7 +204,7 @@ template sendImpl(q: expr) {.immediate.} =
releaseSys(q.lock)
signalSysCond(q.cond)
proc send*[TMsg](c: var TChannel[TMsg], msg: TMsg) =
proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) =
## sends a message to a thread. `msg` is deeply copied.
var q = cast[PRawChannel](addr(c))
sendImpl(q)
@ -218,7 +220,7 @@ proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
sysFatal(ValueError, "cannot receive message of wrong type")
rawRecv(q, res, typ)
proc recv*[TMsg](c: var TChannel[TMsg]): TMsg =
proc recv*[TMsg](c: var Channel[TMsg]): TMsg =
## receives a message from the channel `c`. This blocks until
## a message has arrived! You may use ``peek`` to avoid the blocking.
var q = cast[PRawChannel](addr(c))
@ -226,7 +228,7 @@ proc recv*[TMsg](c: var TChannel[TMsg]): TMsg =
llRecv(q, addr(result), cast[PNimType](getTypeInfo(result)))
releaseSys(q.lock)
proc tryRecv*[TMsg](c: var TChannel[TMsg]): tuple[dataAvailable: bool,
proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool,
msg: TMsg] =
## try to receives a message from the channel `c` if available. Otherwise
## it returns ``(false, default(msg))``.
@ -238,7 +240,7 @@ proc tryRecv*[TMsg](c: var TChannel[TMsg]): tuple[dataAvailable: bool,
result.dataAvailable = true
releaseSys(q.lock)
proc peek*[TMsg](c: var TChannel[TMsg]): int =
proc peek*[TMsg](c: var Channel[TMsg]): int =
## returns the current number of messages in the channel `c`. Returns -1
## if the channel has been closed. **Note**: This is dangerous to use
## as it encourages races. It's much better to use ``tryRecv`` instead.
@ -249,15 +251,15 @@ proc peek*[TMsg](c: var TChannel[TMsg]): int =
else:
result = -1
proc open*[TMsg](c: var TChannel[TMsg]) =
proc open*[TMsg](c: var Channel[TMsg]) =
## opens a channel `c` for inter thread communication.
initRawChannel(addr(c))
proc close*[TMsg](c: var TChannel[TMsg]) =
proc close*[TMsg](c: var Channel[TMsg]) =
## closes a channel `c` and frees its associated resources.
deinitRawChannel(addr(c))
proc ready*[TMsg](c: var TChannel[TMsg]): bool =
proc ready*[TMsg](c: var Channel[TMsg]): bool =
## returns true iff some thread is waiting on the channel `c` for
## new messages.
var q = cast[PRawChannel](addr(c))

View file

@ -10,22 +10,23 @@
## This file implements basic features for any debugger.
type
TVarSlot* {.compilerproc, final.} = object ## a slot in a frame
VarSlot* {.compilerproc, final.} = object ## a slot in a frame
address*: pointer ## the variable's address
typ*: PNimType ## the variable's type
name*: cstring ## the variable's name; for globals this is "module.name"
PExtendedFrame = ptr TExtendedFrame
TExtendedFrame = object # If the debugger is enabled the compiler
PExtendedFrame = ptr ExtendedFrame
ExtendedFrame = object # If the debugger is enabled the compiler
# provides an extended frame. Of course
# only slots that are
# needed are allocated and not 10_000,
# except for the global data description.
f: TFrame
slots: array[0..10_000, TVarSlot]
f: Frame
slots: array[0..10_000, VarSlot]
{.deprecated: [TVarSlot: VarSlot, TExtendedFrame: ExtendedFrame].}
var
dbgGlobalData: TExtendedFrame # this reserves much space, but
dbgGlobalData: ExtendedFrame # this reserves much space, but
# for now it is the most practical way
proc dbgRegisterGlobal(name: cstring, address: pointer,
@ -39,7 +40,7 @@ proc dbgRegisterGlobal(name: cstring, address: pointer,
dbgGlobalData.slots[i].address = address
inc(dbgGlobalData.f.len)
proc getLocal*(frame: PFrame; slot: int): TVarSlot {.inline.} =
proc getLocal*(frame: PFrame; slot: int): VarSlot {.inline.} =
## retrieves the meta data for the local variable at `slot`. CAUTION: An
## invalid `slot` value causes a corruption!
result = cast[PExtendedFrame](frame).slots[slot]
@ -48,7 +49,7 @@ proc getGlobalLen*(): int {.inline.} =
## gets the number of registered globals.
result = dbgGlobalData.f.len
proc getGlobal*(slot: int): TVarSlot {.inline.} =
proc getGlobal*(slot: int): VarSlot {.inline.} =
## retrieves the meta data for the global variable at `slot`. CAUTION: An
## invalid `slot` value causes a corruption!
result = dbgGlobalData.slots[slot]
@ -56,13 +57,13 @@ proc getGlobal*(slot: int): TVarSlot {.inline.} =
# ------------------- breakpoint support ------------------------------------
type
TBreakpoint* = object ## represents a break point
Breakpoint* = object ## represents a break point
low*, high*: int ## range from low to high; if disabled
## both low and high are set to their negative values
filename*: cstring ## the filename of the breakpoint
var
dbgBP: array[0..127, TBreakpoint] # breakpoints
dbgBP: array[0..127, Breakpoint] # breakpoints
dbgBPlen: int
dbgBPbloom: int64 # we use a bloom filter to speed up breakpoint checking
@ -131,16 +132,16 @@ proc canonFilename*(filename: cstring): cstring =
if fileMatches(result, filename): return result
result = nil
iterator listBreakpoints*(): ptr TBreakpoint =
iterator listBreakpoints*(): ptr Breakpoint =
## lists all breakpoints.
for i in 0..dbgBPlen-1: yield addr(dbgBP[i])
proc isActive*(b: ptr TBreakpoint): bool = b.low > 0
proc flip*(b: ptr TBreakpoint) =
proc isActive*(b: ptr Breakpoint): bool = b.low > 0
proc flip*(b: ptr Breakpoint) =
## enables or disables 'b' depending on its current state.
b.low = -b.low; b.high = -b.high
proc checkBreakpoints*(filename: cstring, line: int): ptr TBreakpoint =
proc checkBreakpoints*(filename: cstring, line: int): ptr Breakpoint =
## in which breakpoint (if any) we are.
if (dbgBPbloom and line) != line: return nil
for b in listBreakpoints():
@ -149,29 +150,30 @@ proc checkBreakpoints*(filename: cstring, line: int): ptr TBreakpoint =
# ------------------- watchpoint support ------------------------------------
type
THash = int
TWatchpoint {.pure, final.} = object
Hash = int
Watchpoint {.pure, final.} = object
name: cstring
address: pointer
typ: PNimType
oldValue: THash
oldValue: Hash
{.deprecated: [THash: Hash, TWatchpoint: Watchpoint].}
var
watchpoints: array [0..99, TWatchpoint]
watchpoints: array [0..99, Watchpoint]
watchpointsLen: int
proc `!&`(h: THash, val: int): THash {.inline.} =
proc `!&`(h: Hash, val: int): Hash {.inline.} =
result = h +% val
result = result +% result shl 10
result = result xor (result shr 6)
proc `!$`(h: THash): THash {.inline.} =
proc `!$`(h: Hash): Hash {.inline.} =
result = h +% h shl 3
result = result xor (result shr 11)
result = result +% result shl 15
proc hash(data: pointer, size: int): THash =
var h: THash = 0
proc hash(data: pointer, size: int): Hash =
var h: Hash = 0
var p = cast[cstring](data)
var i = 0
var s = size
@ -181,14 +183,14 @@ proc hash(data: pointer, size: int): THash =
dec(s)
result = !$h
proc hashGcHeader(data: pointer): THash =
proc hashGcHeader(data: pointer): Hash =
const headerSize = sizeof(int)*2
result = hash(cast[pointer](cast[int](data) -% headerSize), headerSize)
proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool,
h: THash): THash
h: Hash): Hash
proc genericHashAux(dest: pointer, n: ptr TNimNode, shallow: bool,
h: THash): THash =
h: Hash): Hash =
var d = cast[ByteAddress](dest)
case n.kind
of nkSlot:
@ -204,7 +206,7 @@ proc genericHashAux(dest: pointer, n: ptr TNimNode, shallow: bool,
of nkNone: sysAssert(false, "genericHashAux")
proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool,
h: THash): THash =
h: Hash): Hash =
sysAssert(mt != nil, "genericHashAux 2")
case mt.kind
of tyString:

View file

@ -15,7 +15,7 @@
{.push stack_trace: off.}
const
NilLibHandle: TLibHandle = nil
NilLibHandle: LibHandle = nil
proc rawWrite(f: File, s: string) =
# we cannot throw an exception here!
@ -55,22 +55,22 @@ when defined(posix):
var
RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int
proc dlclose(lib: TLibHandle) {.importc, header: "<dlfcn.h>".}
proc dlopen(path: cstring, mode: int): TLibHandle {.
proc dlclose(lib: LibHandle) {.importc, header: "<dlfcn.h>".}
proc dlopen(path: cstring, mode: int): LibHandle {.
importc, header: "<dlfcn.h>".}
proc dlsym(lib: TLibHandle, name: cstring): TProcAddr {.
proc dlsym(lib: LibHandle, name: cstring): ProcAddr {.
importc, header: "<dlfcn.h>".}
proc dlerror(): cstring {.importc, header: "<dlfcn.h>".}
proc nimUnloadLibrary(lib: TLibHandle) =
proc nimUnloadLibrary(lib: LibHandle) =
dlclose(lib)
proc nimLoadLibrary(path: string): TLibHandle =
proc nimLoadLibrary(path: string): LibHandle =
result = dlopen(path, RTLD_NOW)
#c_fprintf(c_stdout, "%s\n", dlerror())
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
result = dlsym(lib, name)
if result == nil: procAddrError(name)
@ -84,12 +84,12 @@ elif defined(windows) or defined(dos):
type
THINSTANCE {.importc: "HINSTANCE".} = object
x: pointer
proc getProcAddress(lib: THINSTANCE, name: cstring): TProcAddr {.
proc getProcAddress(lib: THINSTANCE, name: cstring): ProcAddr {.
importcpp: "(void*)GetProcAddress(@)", header: "<windows.h>", stdcall.}
else:
type
THINSTANCE {.importc: "HINSTANCE".} = pointer
proc getProcAddress(lib: THINSTANCE, name: cstring): TProcAddr {.
proc getProcAddress(lib: THINSTANCE, name: cstring): ProcAddr {.
importc: "GetProcAddress", header: "<windows.h>", stdcall.}
proc freeLibrary(lib: THINSTANCE) {.
@ -97,13 +97,13 @@ elif defined(windows) or defined(dos):
proc winLoadLibrary(path: cstring): THINSTANCE {.
importc: "LoadLibraryA", header: "<windows.h>", stdcall.}
proc nimUnloadLibrary(lib: TLibHandle) =
proc nimUnloadLibrary(lib: LibHandle) =
freeLibrary(cast[THINSTANCE](lib))
proc nimLoadLibrary(path: string): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(path))
proc nimLoadLibrary(path: string): LibHandle =
result = cast[LibHandle](winLoadLibrary(path))
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
result = getProcAddress(cast[THINSTANCE](lib), name)
if result == nil: procAddrError(name)
@ -115,13 +115,13 @@ elif defined(mac):
#
{.error: "no implementation for dyncalls yet".}
proc nimUnloadLibrary(lib: TLibHandle) =
proc nimUnloadLibrary(lib: LibHandle) =
NSUnLinkModule(NSModule(lib), NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES)
var
dyld_present {.importc: "_dyld_present", header: "<dyld.h>".}: int
proc nimLoadLibrary(path: string): TLibHandle =
proc nimLoadLibrary(path: string): LibHandle =
var
img: NSObjectFileImage
ret: NSObjectFileImageReturnCode
@ -134,13 +134,13 @@ elif defined(mac):
modul = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE or
NSLINKMODULE_OPTION_RETURN_ON_ERROR)
NSDestroyObjectFileImage(img)
result = TLibHandle(modul)
result = LibHandle(modul)
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
var
nss: NSSymbol
nss = NSLookupSymbolInModule(NSModule(lib), name)
result = TProcAddr(NSAddressOfSymbol(nss))
result = ProcAddr(NSAddressOfSymbol(nss))
if result == nil: ProcAddrError(name)
else:

View file

@ -20,63 +20,65 @@ const
EndbEnd = "***\n"
type
TStaticStr = object
StaticStr = object
len: int
data: array[0..100, char]
TBreakpointFilename = object
b: ptr TBreakpoint
filename: TStaticStr
BreakpointFilename = object
b: ptr Breakpoint
filename: StaticStr
TDbgState = enum
DbgState = enum
dbOff, # debugger is turned off
dbStepInto, # debugger is in tracing mode
dbStepOver,
dbSkipCurrent,
dbQuiting, # debugger wants to quit
dbBreakpoints # debugger is only interested in breakpoints
{.deprecated: [TStaticStr: StaticStr, TBreakpointFilename: BreakpointFilename,
TDbgState: DbgState].}
var
dbgUser: TStaticStr # buffer for user input; first command is ``step_into``
dbgUser: StaticStr # buffer for user input; first command is ``step_into``
# needs to be global cause we store the last command
# in it
dbgState: TDbgState # state of debugger
dbgState: DbgState # state of debugger
dbgSkipToFrame: PFrame # frame to be skipped to
maxDisplayRecDepth: int = 5 # do not display too much data!
brkPoints: array[0..127, TBreakpointFilename]
brkPoints: array[0..127, BreakpointFilename]
proc setLen(s: var TStaticStr, newLen=0) =
proc setLen(s: var StaticStr, newLen=0) =
s.len = newLen
s.data[newLen] = '\0'
proc add(s: var TStaticStr, c: char) =
proc add(s: var StaticStr, c: char) =
if s.len < high(s.data)-1:
s.data[s.len] = c
s.data[s.len+1] = '\0'
inc s.len
proc add(s: var TStaticStr, c: cstring) =
proc add(s: var StaticStr, c: cstring) =
var i = 0
while c[i] != '\0':
add s, c[i]
inc i
proc assign(s: var TStaticStr, c: cstring) =
proc assign(s: var StaticStr, c: cstring) =
setLen(s)
add s, c
proc `==`(a, b: TStaticStr): bool =
proc `==`(a, b: StaticStr): bool =
if a.len == b.len:
for i in 0 .. a.len-1:
if a.data[i] != b.data[i]: return false
return true
proc `==`(a: TStaticStr, b: cstring): bool =
proc `==`(a: StaticStr, b: cstring): bool =
result = c_strcmp(a.data, b) == 0
proc write(f: TFile, s: TStaticStr) =
proc write(f: File, s: StaticStr) =
write(f, cstring(s.data))
proc listBreakPoints() =
@ -95,14 +97,14 @@ proc listBreakPoints() =
write(stdout, "\n")
write(stdout, EndbEnd)
proc openAppend(filename: cstring): TFile =
proc openAppend(filename: cstring): File =
var p: pointer = fopen(filename, "ab")
if p != nil:
result = cast[TFile](p)
result = cast[File](p)
write(result, "----------------------------------------\n")
proc dbgRepr(p: pointer, typ: PNimType): string =
var cl: TReprClosure
var cl: ReprClosure
initReprClosure(cl)
cl.recDepth = maxDisplayRecDepth
# locks for the GC turned out to be a bad idea...
@ -112,12 +114,12 @@ proc dbgRepr(p: pointer, typ: PNimType): string =
# dec(recGcLock)
deinitReprClosure(cl)
proc writeVariable(stream: TFile, slot: TVarSlot) =
proc writeVariable(stream: File, slot: VarSlot) =
write(stream, slot.name)
write(stream, " = ")
writeln(stream, dbgRepr(slot.address, slot.typ))
proc listFrame(stream: TFile, f: PFrame) =
proc listFrame(stream: File, f: PFrame) =
write(stream, EndbBeg)
write(stream, "| Frame (")
write(stream, f.len)
@ -126,7 +128,7 @@ proc listFrame(stream: TFile, f: PFrame) =
writeln(stream, getLocal(f, i).name)
write(stream, EndbEnd)
proc listLocals(stream: TFile, f: PFrame) =
proc listLocals(stream: File, f: PFrame) =
write(stream, EndbBeg)
write(stream, "| Frame (")
write(stream, f.len)
@ -135,7 +137,7 @@ proc listLocals(stream: TFile, f: PFrame) =
writeVariable(stream, getLocal(f, i))
write(stream, EndbEnd)
proc listGlobals(stream: TFile) =
proc listGlobals(stream: File) =
write(stream, EndbBeg)
write(stream, "| Globals:\n")
for i in 0 .. getGlobalLen()-1:
@ -172,7 +174,7 @@ proc dbgShowExecutionPoint() =
write(stdout, framePtr.procname)
write(stdout, " ***\n")
proc scanAndAppendWord(src: cstring, a: var TStaticStr, start: int): int =
proc scanAndAppendWord(src: cstring, a: var StaticStr, start: int): int =
result = start
# skip whitespace:
while src[result] in {'\t', ' '}: inc(result)
@ -184,11 +186,11 @@ proc scanAndAppendWord(src: cstring, a: var TStaticStr, start: int): int =
else: break
inc(result)
proc scanWord(src: cstring, a: var TStaticStr, start: int): int =
proc scanWord(src: cstring, a: var StaticStr, start: int): int =
setlen(a)
result = scanAndAppendWord(src, a, start)
proc scanFilename(src: cstring, a: var TStaticStr, start: int): int =
proc scanFilename(src: cstring, a: var StaticStr, start: int): int =
result = start
setLen a
while src[result] in {'\t', ' '}: inc(result)
@ -250,8 +252,8 @@ proc hasExt(s: cstring): bool =
if s[i] == '.': return true
inc i
proc parseBreakpoint(s: cstring, start: int): TBreakpoint =
var dbgTemp: TStaticStr
proc parseBreakpoint(s: cstring, start: int): Breakpoint =
var dbgTemp: StaticStr
var i = scanNumber(s, result.low, start)
if result.low == 0: result.low = framePtr.line
i = scanNumber(s, result.high, i)
@ -279,11 +281,11 @@ proc breakpointToggle(s: cstring, start: int) =
if not b.isNil: b.flip
else: debugOut("[Warning] unknown breakpoint ")
proc dbgEvaluate(stream: TFile, s: cstring, start: int, f: PFrame) =
var dbgTemp: TStaticStr
proc dbgEvaluate(stream: File, s: cstring, start: int, f: PFrame) =
var dbgTemp: StaticStr
var i = scanWord(s, dbgTemp, start)
while s[i] in {' ', '\t'}: inc(i)
var v: TVarSlot
var v: VarSlot
if s[i] == '.':
inc(i)
add(dbgTemp, '.')
@ -299,7 +301,7 @@ proc dbgEvaluate(stream: TFile, s: cstring, start: int, f: PFrame) =
writeVariable(stream, v)
proc dbgOut(s: cstring, start: int, currFrame: PFrame) =
var dbgTemp: TStaticStr
var dbgTemp: StaticStr
var i = scanFilename(s, dbgTemp, start)
if dbgTemp.len == 0:
invalidCommand()
@ -312,7 +314,7 @@ proc dbgOut(s: cstring, start: int, currFrame: PFrame) =
close(stream)
proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) =
var dbgTemp: TStaticStr
var dbgTemp: StaticStr
var i = scanFilename(s, dbgTemp, start)
if dbgTemp.len == 0:
# just write it to stdout:
@ -325,7 +327,7 @@ proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) =
listFrame(stream, currFrame)
close(stream)
proc readLine(f: TFile, line: var TStaticStr): bool =
proc readLine(f: File, line: var StaticStr): bool =
while true:
var c = fgetc(f)
if c < 0'i32:
@ -358,7 +360,7 @@ proc commandPrompt() =
again = true
dbgFramePtr = framePtr # for going down and up the stack
dbgDown = 0 # how often we did go down
dbgTemp: TStaticStr
dbgTemp: StaticStr
while again:
write(stdout, "*** endb| >>")

View file

@ -365,5 +365,6 @@ when not defined(noSignalHandler):
proc setControlCHook(hook: proc () {.noconv.} not nil) =
# ugly cast, but should work on all architectures:
type TSignalHandler = proc (sig: cint) {.noconv, benign.}
c_signal(SIGINT, cast[TSignalHandler](hook))
type SignalHandler = proc (sig: cint) {.noconv, benign.}
{.deprecated: [TSignalHandler: SignalHandler].}
c_signal(SIGINT, cast[SignalHandler](hook))

View file

@ -45,17 +45,17 @@ const
rcShift = 3 # shift by rcShift to get the reference counter
colorMask = 0b011
type
TWalkOp = enum
WalkOp = enum
waMarkGlobal, # part of the backup/debug mark&sweep
waMarkPrecise, # part of the backup/debug mark&sweep
waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack,
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
# storage is freed.
TGcStat {.final, pure.} = object
GcStat {.final, pure.} = object
stackScans: int # number of performed stack scans (for statistics)
cycleCollections: int # number of performed full collections
maxThreshold: int # max threshold that has been set
@ -64,35 +64,36 @@ type
cycleTableSize: int # max entries in cycle table
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
stackBottom: pointer
cycleThreshold: int
when useCellIds:
idGenerator: int
zct: TCellSeq # the zero count table
decStack: TCellSeq # cells in the stack that are to decref again
cycleRoots: TCellSet
tempStack: TCellSeq # temporary stack for recursion elimination
zct: CellSeq # the zero count table
decStack: CellSeq # cells in the stack that are to decref again
cycleRoots: CellSet
tempStack: CellSeq # temporary stack for recursion elimination
recGcLock: int # prevent recursion via finalizers; no thread lock
when withRealTime:
maxPause: TNanos # max allowed pause in nanoseconds; active if > 0
region: TMemRegion # garbage collected region
stat: TGcStat
maxPause: Nanos # max allowed pause in nanoseconds; active if > 0
region: MemRegion # garbage collected region
stat: GcStat
when useMarkForDebug or useBackupGc:
marked: TCellSet
marked: CellSet
{.deprecated: [TWalkOp: WalkOp, TFinalizer: Finalizer, TGcHeap: GcHeap,
TGcStat: GcStat].}
var
gch {.rtlThreadVar.}: TGcHeap
gch {.rtlThreadVar.}: GcHeap
when not defined(useNimRtl):
instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) =
template acquire(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap:
acquireSys(HeapLock)
template release(gch: TGcHeap) =
template release(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap:
releaseSys(HeapLock)
@ -104,18 +105,18 @@ template gcAssert(cond: bool, msg: string) =
writeStackTrace()
quit 1
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
proc addZCT(s: var CellSeq, c: PCell) {.noinline.} =
if (c.refcount and ZctFlag) == 0:
c.refcount = c.refcount or ZctFlag
add(s, c)
proc cellToUsr(cell: PCell): pointer {.inline.} =
# 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.} =
# 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.} =
result = ntfAcyclic notin c.typ.flags
@ -152,11 +153,11 @@ template gcTrace(cell, state: expr): stmt {.immediate.} =
when traceGC: traceCell(cell, state)
# forward declarations:
proc collectCT(gch: var TGcHeap) {.benign.}
proc collectCT(gch: var GcHeap) {.benign.}
proc isOnStack*(p: pointer): bool {.noinline, benign.}
proc forAllChildren(cell: PCell, op: TWalkOp) {.benign.}
proc doOperation(p: pointer, op: TWalkOp) {.benign.}
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) {.benign.}
proc forAllChildren(cell: PCell, op: WalkOp) {.benign.}
proc doOperation(p: pointer, op: WalkOp) {.benign.}
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
# we need the prototype here for debugging purposes
when hasThreadSupport and hasSharedHeap:
@ -178,7 +179,7 @@ proc prepareDealloc(cell: PCell) =
# prevend recursive entering here by a lock.
# XXX: we should set the cell's children to nil!
inc(gch.recGcLock)
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell))
(cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
dec(gch.recGcLock)
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
@ -276,7 +277,7 @@ proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} =
proc initGC() =
when not defined(useNimRtl):
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.stat.stackScans = 0
gch.stat.cycleCollections = 0
@ -308,12 +309,13 @@ proc setupForeignThreadGc*() =
when useMarkForDebug or useBackupGc:
type
TGlobalMarkerProc = proc () {.nimcall, benign.}
GlobalMarkerProc = proc () {.nimcall, benign.}
{.deprecated: [TGlobalMarkerProc: GlobalMarkerProc].}
var
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):
globalMarkers[globalMarkersLen] = markerProc
inc globalMarkersLen
@ -321,11 +323,11 @@ when useMarkForDebug or useBackupGc:
echo "[GC] cannot register global variable; too many global variables"
quit 1
proc cellsetReset(s: var TCellSet) =
proc cellsetReset(s: var CellSet) =
deinit(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)
case n.kind
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)
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)
if dest == nil: return # nothing to do
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)
else: discard
proc forAllChildren(cell: PCell, op: TWalkOp) =
proc forAllChildren(cell: PCell, op: WalkOp) =
gcAssert(cell != nil, "forAllChildren: 1")
gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
gcAssert(cell.typ != nil, "forAllChildren: 3")
@ -380,7 +382,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
GenericSeqSize), cell.typ.base, op)
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.
# 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.
@ -431,13 +433,13 @@ proc gcInvariant*() =
markForDebug(gch)
{.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
sysAssert(allocInv(gch.region), "rawNewObj begin")
acquire(gch)
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
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")
# now it is buffered in the ZCT
res.typ = typ
@ -486,7 +488,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
collectCT(gch)
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((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT
@ -515,7 +517,7 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
cast[PGenericSeq](result).reserved = len
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)
collectCT(gch)
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")
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
if ol.typ.kind != tyString: elemSize = ol.typ.base.size
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
copyMem(res, ol, oldsize + sizeof(TCell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)),
copyMem(res, ol, oldsize + sizeof(Cell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
newsize-oldsize)
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
# 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)
else:
sysAssert(ol.typ != nil, "growObj: 5")
zeroMem(ol, sizeof(TCell))
zeroMem(ol, sizeof(Cell))
release(gch)
when useCellIds:
inc gch.idGenerator
@ -580,7 +582,7 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
# ---------------- cycle collector -------------------------------------------
proc freeCyclicCell(gch: var TGcHeap, c: PCell) =
proc freeCyclicCell(gch: var GcHeap, c: PCell) =
prepareDealloc(c)
gcTrace(c, csCycFreed)
when logGC: writeCell("cycle collector dealloc cell", c)
@ -589,7 +591,7 @@ proc freeCyclicCell(gch: var TGcHeap, c: PCell) =
rawDealloc(gch.region, c)
else:
gcAssert(c.typ != nil, "freeCyclicCell")
zeroMem(c, sizeof(TCell))
zeroMem(c, sizeof(Cell))
proc markGray(s: PCell) =
if s.color != rcGray:
@ -620,7 +622,7 @@ proc collectWhite(s: PCell) =
forAllChildren(s, waCollectWhite)
freeCyclicCell(gch, s)
proc markRoots(gch: var TGcHeap) =
proc markRoots(gch: var GcHeap) =
var tabSize = 0
for s in elements(gch.cycleRoots):
#writeCell("markRoot", s)
@ -635,7 +637,7 @@ proc markRoots(gch: var TGcHeap) =
gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, tabSize)
when useBackupGc:
proc sweep(gch: var TGcHeap) =
proc sweep(gch: var GcHeap) =
for x in allObjects(gch.region):
if isCell(x):
# cast to PCell is correct here:
@ -643,7 +645,7 @@ when useBackupGc:
if c notin gch.marked: freeCyclicCell(gch, c)
when useMarkForDebug or useBackupGc:
proc markS(gch: var TGcHeap, c: PCell) =
proc markS(gch: var GcHeap, c: PCell) =
incl(gch.marked, c)
gcAssert gch.tempStack.len == 0, "stack not empty!"
forAllChildren(c, waMarkPrecise)
@ -653,10 +655,10 @@ when useMarkForDebug or useBackupGc:
if not containsOrIncl(gch.marked, d):
forAllChildren(d, waMarkPrecise)
proc markGlobals(gch: var TGcHeap) =
proc markGlobals(gch: var GcHeap) =
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:
var cell = usrToCell(p)
var c = cast[TAddress](cell)
@ -688,7 +690,7 @@ when logGC:
forAllChildren(s, waDebug)
c_fprintf(c_stdout, "}\n")
proc doOperation(p: pointer, op: TWalkOp) =
proc doOperation(p: pointer, op: WalkOp) =
if p == nil: return
var c: PCell = usrToCell(p)
gcAssert(c != nil, "doOperation: 1")
@ -733,19 +735,19 @@ proc doOperation(p: pointer, op: TWalkOp) =
#of waDebug: debugGraph(c)
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:
proc markStackAndRegistersForSweep(gch: var TGcHeap) {.noinline, cdecl,
proc markStackAndRegistersForSweep(gch: var GcHeap) {.noinline, cdecl,
benign.}
proc collectRoots(gch: var TGcHeap) =
proc collectRoots(gch: var GcHeap) =
for s in elements(gch.cycleRoots):
collectWhite(s)
proc collectCycles(gch: var TGcHeap) =
proc collectCycles(gch: var GcHeap) =
# ensure the ZCT 'color' is not used:
while gch.zct.len > 0: discard collectZCT(gch)
when useBackupGc:
@ -778,7 +780,7 @@ proc collectCycles(gch: var TGcHeap) =
if cycleRootsLen != 0:
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:
sysAssert(allocInv(gch.region), "gcMark begin")
var cell = usrToCell(p)
@ -798,7 +800,7 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
add(gch.decStack, cell)
sysAssert(allocInv(gch.region), "gcMark end")
proc markThreadStacks(gch: var TGcHeap) =
proc markThreadStacks(gch: var GcHeap) =
when hasThreadSupport and hasSharedHeap:
{.error: "not fully implemented".}
var it = threadList
@ -887,7 +889,7 @@ elif stackIncreases:
var
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
template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} =
@ -947,18 +949,18 @@ else:
gcMark(gch, cast[PPointer](sp)[])
sp = sp +% sizeof(pointer)
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
forEachStackSlot(gch, gcMark)
when useMarkForDebug or useBackupGc:
proc markStackAndRegistersForSweep(gch: var TGcHeap) =
proc markStackAndRegistersForSweep(gch: var GcHeap) =
forEachStackSlot(gch, stackMarkS)
# ----------------------------------------------------------------------------
# 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
# deep freeing, which is bad for incremental operation. In order to
# avoid a deep stack, we move objects to keep the ZCT small.
@ -968,7 +970,7 @@ proc collectZCT(gch: var TGcHeap): bool =
when withRealTime:
var steps = workPackage
var t0: TTicks
var t0: Ticks
if gch.maxPause > 0: t0 = getticks()
while L[] > 0:
var c = gch.zct.d[0]
@ -1001,7 +1003,7 @@ proc collectZCT(gch: var TGcHeap): bool =
rawDealloc(gch.region, c)
else:
sysAssert(c.typ != nil, "collectZCT 2")
zeroMem(c, sizeof(TCell))
zeroMem(c, sizeof(Cell))
when withRealTime:
if steps == 0:
steps = workPackage
@ -1014,7 +1016,7 @@ proc collectZCT(gch: var TGcHeap): bool =
return false
result = true
proc unmarkStackAndRegisters(gch: var TGcHeap) =
proc unmarkStackAndRegisters(gch: var GcHeap) =
var d = gch.decStack.d
for i in 0..gch.decStack.len-1:
sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
@ -1026,7 +1028,7 @@ proc unmarkStackAndRegisters(gch: var TGcHeap) =
#sysAssert c.typ != nil, "unmarkStackAndRegisters 2"
gch.decStack.len = 0
proc collectCTBody(gch: var TGcHeap) =
proc collectCTBody(gch: var GcHeap) =
when withRealTime:
let t0 = getticks()
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)
when useMarkForDebug or useBackupGc:
proc markForDebug(gch: var TGcHeap) =
proc markForDebug(gch: var GcHeap) =
markStackAndRegistersForSweep(gch)
markGlobals(gch)
proc collectCT(gch: var TGcHeap) =
proc collectCT(gch: var GcHeap) =
# stackMarkCosts prevents some pathological behaviour: Stack marking
# 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.
@ -1077,13 +1079,13 @@ proc collectCT(gch: var TGcHeap) =
collectCTBody(gch)
when withRealTime:
proc toNano(x: int): TNanos {.inline.} =
proc toNano(x: int): Nanos {.inline.} =
result = x * 1000
proc GC_setMaxPause*(MaxPauseInUs: int) =
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)
gch.maxPause = us.toNano
if (gch.zct.len >= ZctThreshold or (cycleGC and

View file

@ -54,7 +54,7 @@ const
# was replaced by a resize operation.
# see growObj for details
rcColorMask = TRefCount(0b00111)
rcColorMask = RefCount(0b00111)
rcZct = 0b01000 # already added to ZCT
rcInCycleRoots = 0b10000 # already buffered as cycle candidate
@ -97,14 +97,14 @@ const
CollectCyclesStats = false
type
TWalkOp = enum
WalkOp = enum
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
# storage is freed.
TGcStat {.final, pure.} = object
GcStat {.final, pure.} = object
stackScans: int # number of performed stack scans (for statistics)
cycleCollections: int # number of performed full collections
maxThreshold: int # max threshold that has been set
@ -113,16 +113,16 @@ type
cycleTableSize: int # max entries in cycle table
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
stackBottom: pointer
stackTop: pointer
cycleThreshold: int
zct: TCellSeq # the zero count table
decStack: TCellSeq # cells in the stack that are to decref again
cycleRoots: TCellSeq
tempStack: TCellSeq # temporary stack for recursion elimination
freeStack: TCellSeq # objects ready to be freed
zct: CellSeq # the zero count table
decStack: CellSeq # cells in the stack that are to decref again
cycleRoots: CellSeq
tempStack: CellSeq # temporary stack for recursion elimination
freeStack: CellSeq # objects ready to be freed
recGcLock: int # prevent recursion via finalizers; no thread lock
cycleRootsTrimIdx: int # Trimming is a light-weight collection of the
# cycle roots table that uses a cheap linear scan
@ -132,21 +132,22 @@ type
# This index indicates the start of the range of
# such new objects within the table.
when withRealTime:
maxPause: TNanos # max allowed pause in nanoseconds; active if > 0
region: TMemRegion # garbage collected region
stat: TGcStat
maxPause: Nanos # max allowed pause in nanoseconds; active if > 0
region: MemRegion # garbage collected region
stat: GcStat
{.deprecated: [TWalkOp: WalkOp, TFinalizer: Finalizer, TGcStat: GcStat,
TGcHeap: GcHeap].}
var
gch* {.rtlThreadVar.}: TGcHeap
gch* {.rtlThreadVar.}: GcHeap
when not defined(useNimRtl):
instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) =
template acquire(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap:
AcquireSys(HeapLock)
template release(gch: TGcHeap) =
template release(gch: GcHeap) =
when hasThreadSupport and hasSharedHeap:
releaseSys(HeapLock)
@ -169,7 +170,7 @@ template isDead(c: Pcell): expr =
c.isBitUp(rcReallyDead) # also covers rcRetiredBuffer
template clearBit(c: PCell, bit): expr =
c.refcount = c.refcount and (not TRefCount(bit))
c.refcount = c.refcount and (not RefCount(bit))
when debugGC:
var gcCollectionIdx = 0
@ -206,7 +207,7 @@ when debugGC:
c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld\n",
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):
c.setBit rcZct
zct.add c
@ -221,7 +222,7 @@ template setStackTop(gch) =
var stackTop {.volatile.}: pointer
gch.stackTop = addr(stackTop)
template addCycleRoot(cycleRoots: var TCellSeq, c: PCell) =
template addCycleRoot(cycleRoots: var CellSeq, c: PCell) =
if c.color != rcCycleCandidate:
c.setColor rcCycleCandidate
@ -233,11 +234,11 @@ template addCycleRoot(cycleRoots: var TCellSeq, c: PCell) =
proc cellToUsr(cell: PCell): pointer {.inline.} =
# 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.} =
# 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.} =
result = ntfAcyclic notin c.typ.flags
@ -254,11 +255,11 @@ when BitsPerPage mod (sizeof(int)*8) != 0:
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
# forward declarations:
proc collectCT(gch: var TGcHeap)
proc collectCT(gch: var GcHeap)
proc isOnStack*(p: pointer): bool {.noinline.}
proc forAllChildren(cell: PCell, op: TWalkOp)
proc doOperation(p: pointer, op: TWalkOp)
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp)
proc forAllChildren(cell: PCell, op: WalkOp)
proc doOperation(p: pointer, op: WalkOp)
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp)
# we need the prototype here for debugging purposes
proc prepareDealloc(cell: PCell) =
@ -269,18 +270,19 @@ proc prepareDealloc(cell: PCell) =
# prevend recursive entering here by a lock.
# XXX: we should set the cell's children to nil!
inc(gch.recGcLock)
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell))
(cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
dec(gch.recGcLock)
when traceGC:
# traceGC is a special switch to enable extensive debugging
type
TCellState = enum
CellState = enum
csAllocated, csFreed
{.deprecated: [TCellState: CellState].}
var
states: array[TCellState, TCellSet]
states: array[CellState, CellSet]
proc traceCell(c: PCell, state: TCellState) =
proc traceCell(c: PCell, state: CellState) =
case state
of csAllocated:
if c in states[csAllocated]:
@ -300,7 +302,7 @@ when traceGC:
incl(states[state], c)
proc computeCellWeight(c: PCell): int =
var x: TCellSet
var x: CellSet
x.init
let startLen = gch.tempStack.len
@ -363,30 +365,32 @@ proc rtlAddZCT(c: PCell) {.rtl, inl.} =
WithHeapLock: addZCT(gch.zct, c)
type
TCyclicMode = enum
CyclicMode = enum
Cyclic,
Acyclic,
MaybeCyclic
TReleaseType = enum
ReleaseType = enum
AddToZTC
FreeImmediately
THeapType = enum
HeapType = enum
LocalHeap
SharedHeap
{.deprecated: [TCyclicMode: CyclicMode, TReleaseType: ReleaseType,
THeapType: HeapType].}
template `++` (rc: TRefCount, heapType: THeapType): stmt =
template `++` (rc: RefCount, heapType: HeapType): stmt =
when heapType == SharedHeap:
discard atomicInc(rc, rcIncrement)
else:
inc rc, rcIncrement
template `--`(rc: TRefCount): expr =
template `--`(rc: RefCount): expr =
dec 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)
template doDecRef(cc: PCell,
@ -479,7 +483,7 @@ when hasThreadSupport and hasSharedHeap:
proc initGC() =
when not defined(useNimRtl):
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.stat.stackScans = 0
gch.stat.cycleCollections = 0
@ -494,7 +498,7 @@ proc initGC() =
init(gch.cycleRoots)
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)
case n.kind
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)
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)
if dest == nil: return # nothing to do
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)
else: discard
proc forAllChildren(cell: PCell, op: TWalkOp) =
proc forAllChildren(cell: PCell, op: WalkOp) =
sysAssert(cell != nil, "forAllChildren: 1")
sysAssert(cell.typ != nil, "forAllChildren: 2")
sysAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3"
@ -549,7 +553,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
cell.typ.base, op)
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.
# 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.
@ -593,7 +597,7 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
return
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
acquire(gch)
sysAssert(allocInv(gch.region), "rawNewObj begin")
@ -602,7 +606,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap, rc1 = false): pointer
collectCT(gch)
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((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.}
proc freeCell(gch: var TGcHeap, c: PCell) =
proc freeCell(gch: var GcHeap, c: PCell) =
# prepareDealloc(c)
gcTrace(c, csFreed)
when reallyDealloc: rawDealloc(gch.region, c)
else:
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]
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.
# a candidate is removed either because it received a refup and
# 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).reserved = len
proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
acquire(gch)
collectCT(gch)
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(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
else: 1
@ -713,8 +717,8 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
# XXX: This should happen outside
# call user-defined move code
# call user-defined default constructor
copyMem(res, ol, oldsize + sizeof(TCell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(TCell)),
copyMem(res, ol, oldsize + sizeof(Cell))
zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
newsize-oldsize)
sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
@ -778,26 +782,27 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
# ---------------- cycle collector -------------------------------------------
proc doOperation(p: pointer, op: TWalkOp) =
proc doOperation(p: pointer, op: WalkOp) =
if p == nil: return
var c: PCell = usrToCell(p)
sysAssert(c != nil, "doOperation: 1")
gch.tempStack.add c
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
doOperation(d, TWalkOp(op))
doOperation(d, WalkOp(op))
type
TRecursionType = enum
RecursionType = enum
FromChildren,
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
proc trimCycleRoots(gch: var TGcHeap, startIdx = gch.cycleRootsTrimIdx) =
proc trimCycleRoots(gch: var GcHeap, startIdx = gch.cycleRootsTrimIdx) =
var i = startIdx
while i < gch.cycleRoots.len:
if gch.cycleRoots.d[i].color != rcCycleCandidate:
@ -808,7 +813,7 @@ proc trimCycleRoots(gch: var TGcHeap, startIdx = gch.cycleRootsTrimIdx) =
gch.cycleRootsTrimIdx = gch.cycleRoots.len
# 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
gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, gch.cycleRoots.len)
@ -990,7 +995,7 @@ var gcDebugging* = false
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:
sysAssert(allocInv(gch.region), "gcMark begin")
var cell = usrToCell(p)
@ -1025,7 +1030,7 @@ proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
add(gch.decStack, cell)
sysAssert(allocInv(gch.region), "gcMark end")
proc markThreadStacks(gch: var TGcHeap) =
proc markThreadStacks(gch: var GcHeap) =
when hasThreadSupport and hasSharedHeap:
{.error: "not fully implemented".}
var it = threadList
@ -1074,7 +1079,7 @@ proc stackSize(): int {.noinline.} =
var
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
when defined(sparc): # For SPARC architecture.
@ -1086,7 +1091,7 @@ when defined(sparc): # For SPARC architecture.
var x = cast[ByteAddress](p)
result = a <=% x and x <=% b
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
when defined(sparcv9):
asm """"flushw \n" """
else:
@ -1117,7 +1122,7 @@ elif stackIncreases:
var x = cast[ByteAddress](p)
result = a <=% x and x <=% b
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
var registers: C_JmpBuf
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
var max = cast[ByteAddress](gch.stackBottom)
@ -1140,7 +1145,7 @@ else:
var x = cast[ByteAddress](p)
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.
# Used to traverse the stack and registers assuming
# that 'setjmp' will save registers in the C stack.
@ -1181,7 +1186,7 @@ else:
# end of non-portable code
# ----------------------------------------------------------------------------
proc releaseCell(gch: var TGcHeap, cell: PCell) =
proc releaseCell(gch: var GcHeap, cell: PCell) =
if cell.color != rcReallyDead:
prepareDealloc(cell)
cell.setColor rcReallyDead
@ -1210,13 +1215,13 @@ proc releaseCell(gch: var TGcHeap, cell: PCell) =
# recursion).
# 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
var L = addr(gch.zct.len)
when withRealtime:
var steps = workPackage
var t0: TTicks
var t0: Ticks
if gch.maxPause > 0: t0 = getticks()
while L[] > 0:
@ -1257,7 +1262,7 @@ proc collectZCT(gch: var TGcHeap): bool =
#deInit(gch.zct)
#init(gch.zct)
proc unmarkStackAndRegisters(gch: var TGcHeap) =
proc unmarkStackAndRegisters(gch: var GcHeap) =
var d = gch.decStack.d
for i in 0 .. <gch.decStack.len:
sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
@ -1283,7 +1288,7 @@ proc unmarkStackAndRegisters(gch: var TGcHeap) =
gch.decStack.len = 0
proc collectCTBody(gch: var TGcHeap) =
proc collectCTBody(gch: var GcHeap) =
when withRealtime:
let t0 = getticks()
when debugGC: inc gcCollectionIdx
@ -1315,20 +1320,20 @@ proc collectCTBody(gch: var TGcHeap) =
if gch.maxPause > 0 and duration > gch.maxPause:
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
getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
gch.recGcLock == 0:
collectCTBody(gch)
when withRealtime:
proc toNano(x: int): TNanos {.inline.} =
proc toNano(x: int): Nanos {.inline.} =
result = x * 1000
proc GC_setMaxPause*(MaxPauseInUs: int) =
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)
gch.maxPause = us.toNano
if (gch.zct.len >= ZctThreshold or (cycleGC and

View file

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

View file

@ -15,13 +15,13 @@ else:
proc log*(s: cstring) {.importc: "console.log", varargs, nodecl.}
type
PSafePoint = ptr TSafePoint
TSafePoint {.compilerproc, final.} = object
PSafePoint = ptr SafePoint
SafePoint {.compilerproc, final.} = object
prev: PSafePoint # points to next safe point
exc: ref Exception
PCallFrame = ptr TCallFrame
TCallFrame {.importc, nodecl, final.} = object
PCallFrame = ptr CallFrame
CallFrame {.importc, nodecl, final.} = object
prev: PCallFrame
procname: cstring
line: int # current line number
@ -33,6 +33,7 @@ type
lineNumber {.importc.}: int
message {.importc.}: cstring
stack {.importc.}: cstring
{.deprecated: [TSafePoint: SafePoint, TCallFrame: CallFrame].}
var
framePtr {.importc, nodecl, volatile.}: PCallFrame
@ -60,12 +61,13 @@ proc getCurrentExceptionMsg*(): string =
proc auxWriteStackTrace(f: PCallFrame): string =
type
TTempFrame = tuple[procname: cstring, line: int]
TempFrame = tuple[procname: cstring, line: int]
{.deprecated: [TTempFrame: TempFrame].}
var
it = f
i = 0
total = 0
tempFrames: array [0..63, TTempFrame]
tempFrames: array [0..63, TempFrame]
while it != nil and i <= high(tempFrames):
tempFrames[i].procname = it.procname
tempFrames[i].line = it.line
@ -260,17 +262,17 @@ proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} =
"""
type
TDocument {.importc.} = object of RootObj
Document {.importc.} = object of RootObj
write: proc (text: cstring) {.nimcall.}
writeln: proc (text: cstring) {.nimcall.}
createAttribute: proc (identifier: cstring): ref TNode {.nimcall.}
createElement: proc (identifier: cstring): ref TNode {.nimcall.}
createTextNode: proc (identifier: cstring): ref TNode {.nimcall.}
getElementById: proc (id: cstring): ref TNode {.nimcall.}
getElementsByName: proc (name: cstring): seq[ref TNode] {.nimcall.}
getElementsByTagName: proc (name: cstring): seq[ref TNode] {.nimcall.}
createAttribute: proc (identifier: cstring): ref Node {.nimcall.}
createElement: proc (identifier: cstring): ref Node {.nimcall.}
createTextNode: proc (identifier: cstring): ref Node {.nimcall.}
getElementById: proc (id: cstring): ref Node {.nimcall.}
getElementsByName: proc (name: cstring): seq[ref Node] {.nimcall.}
getElementsByTagName: proc (name: cstring): seq[ref Node] {.nimcall.}
TNodeType* = enum
NodeType* = enum
ElementNode = 1,
AttributeNode,
TextNode,
@ -283,35 +285,36 @@ type
DocumentTypeNode,
DocumentFragmentNode,
NotationNode
TNode* {.importc.} = object of RootObj
attributes*: seq[ref TNode]
childNodes*: seq[ref TNode]
Node* {.importc.} = object of RootObj
attributes*: seq[ref Node]
childNodes*: seq[ref Node]
data*: cstring
firstChild*: ref TNode
lastChild*: ref TNode
nextSibling*: ref TNode
firstChild*: ref Node
lastChild*: ref Node
nextSibling*: ref Node
nodeName*: cstring
nodeType*: TNodeType
nodeType*: NodeType
nodeValue*: cstring
parentNode*: ref TNode
previousSibling*: ref TNode
appendChild*: proc (child: ref TNode) {.nimcall.}
parentNode*: ref Node
previousSibling*: ref Node
appendChild*: proc (child: ref Node) {.nimcall.}
appendData*: proc (data: cstring) {.nimcall.}
cloneNode*: proc (copyContent: bool) {.nimcall.}
deleteData*: proc (start, len: int) {.nimcall.}
getAttribute*: proc (attr: cstring): cstring {.nimcall.}
getAttributeNode*: proc (attr: cstring): ref TNode {.nimcall.}
getElementsByTagName*: proc (): seq[ref TNode] {.nimcall.}
getAttributeNode*: proc (attr: cstring): ref Node {.nimcall.}
getElementsByTagName*: proc (): seq[ref Node] {.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.}
removeAttribute*: proc (attr: cstring) {.nimcall.}
removeAttributeNode*: proc (attr: ref TNode) {.nimcall.}
removeChild*: proc (child: ref TNode) {.nimcall.}
replaceChild*: proc (newNode, oldNode: ref TNode) {.nimcall.}
removeAttributeNode*: proc (attr: ref Node) {.nimcall.}
removeChild*: proc (child: ref Node) {.nimcall.}
replaceChild*: proc (newNode, oldNode: ref Node) {.nimcall.}
replaceData*: proc (start, len: int, text: 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):
proc rawEcho {.compilerproc, asmNoStackFrame.} =
@ -337,7 +340,7 @@ elif defined(nodejs):
else:
var
document {.importc, nodecl.}: ref TDocument
document {.importc, nodecl.}: ref Document
proc ewriteln(x: cstring) =
var node = document.getElementsByTagName("body")[0]
@ -514,62 +517,70 @@ proc isFatPointer(ti: PNimType): bool =
tyArray, tyArrayConstr, tyTuple,
tyOpenArray, tySet, tyVar, tyRef, tyPtr}
proc nimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}
proc nimCopy(dest, src: pointer, ti: PNimType): pointer {.compilerproc.}
proc nimCopyAux(dest, src: pointer, n: ptr TNimNode) {.compilerproc.} =
case n.kind
of nkNone: sysAssert(false, "nimCopyAux")
of nkSlot:
asm "`dest`[`n`.offset] = nimCopy(`src`[`n`.offset], `n`.typ);"
asm """
var ddest = `dest`[`n`.offset];
if (ddest === undefined) ddest = null;
`dest`[`n`.offset] = nimCopy(ddest, `src`[`n`.offset], `n`.typ);
"""
of nkList:
for i in 0..n.len-1:
nimCopyAux(dest, src, n.sons[i])
of nkCase:
asm """
`dest`[`n`.offset] = nimCopy(`src`[`n`.offset], `n`.typ);
var ddest = `dest`[`n`.offset];
if (ddest === undefined) ddest = null;
`dest`[`n`.offset] = nimCopy(ddest, `src`[`n`.offset], `n`.typ);
for (var i = 0; i < `n`.sons.length; ++i) {
nimCopyAux(`dest`, `src`, `n`.sons[i][1]);
}
"""
proc nimCopy(x: pointer, ti: PNimType): pointer =
proc nimCopy(dest, src: pointer, ti: PNimType): pointer =
case ti.kind
of tyPtr, tyRef, tyVar, tyNil:
if not isFatPointer(ti):
result = x
result = src
else:
asm """
`result` = [null, 0];
`result`[0] = `x`[0];
`result`[1] = `x`[1];
"""
asm "`result` = [`src`[0], `src`[1]];"
of tySet:
asm """
`result` = {};
for (var key in `x`) { `result`[key] = `x`[key]; }
for (var key in `src`) { `result`[key] = `src`[key]; }
"""
of tyTuple, tyObject:
if ti.base != nil: result = nimCopy(x, ti.base)
if ti.base != nil: result = nimCopy(dest, src, ti.base)
elif ti.kind == tyObject:
asm "`result` = {m_type: `ti`};"
asm "`result` = (`dest` === null) ? {m_type: `ti`} : `dest`;"
else:
asm "`result` = {};"
nimCopyAux(result, x, ti.node)
asm "`result` = (`dest` === null) ? {} : `dest`;"
nimCopyAux(result, src, ti.node)
of tySequence, tyArrayConstr, tyOpenArray, tyArray:
asm """
`result` = new Array(`x`.length);
for (var i = 0; i < `x`.length; ++i) {
`result`[i] = nimCopy(`x`[i], `ti`.base);
if (`dest` === null) {
`dest` = new Array(`src`.length);
}
else {
`dest`.length = `src`.length;
}
`result` = `dest`;
for (var i = 0; i < `src`.length; ++i) {
`result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base);
}
"""
of tyString:
asm """
if (`x` !== null) {
`result` = `x`.slice(0);
if (`src` !== null) {
`result` = `src`.slice(0);
}
"""
else:
result = x
result = src
proc genericReset(x: pointer, ti: PNimType): pointer {.compilerproc.} =
case ti.kind
@ -608,7 +619,7 @@ proc arrayConstr(len: int, value: pointer, typ: PNimType): pointer {.
# types are fake
asm """
var result = new Array(`len`);
for (var i = 0; i < `len`; ++i) result[i] = nimCopy(`value`, `typ`);
for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`);
return result;
"""

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -11,83 +11,86 @@
## `<https://github.com/jckarter/clay/blob/master/compiler/src/hirestimer.cpp>`_
type
TTicks = distinct int64
TNanos = int64
Ticks = distinct int64
Nanos = int64
{.deprecated: [TTicks: Ticks, TNanos: Nanos].}
when defined(windows):
proc QueryPerformanceCounter(res: var TTicks) {.
proc QueryPerformanceCounter(res: var Ticks) {.
importc: "QueryPerformanceCounter", stdcall, dynlib: "kernel32".}
proc QueryPerformanceFrequency(res: var int64) {.
importc: "QueryPerformanceFrequency", stdcall, dynlib: "kernel32".}
proc getTicks(): TTicks {.inline.} =
proc getTicks(): Ticks {.inline.} =
QueryPerformanceCounter(result)
proc `-`(a, b: TTicks): TNanos =
proc `-`(a, b: Ticks): Nanos =
var frequency: int64
QueryPerformanceFrequency(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):
type
TMachTimebaseInfoData {.pure, final,
MachTimebaseInfoData {.pure, final,
importc: "mach_timebase_info_data_t",
header: "<mach/mach_time.h>".} = object
numer, denom: int32
{.deprecated: [TMachTimebaseInfoData: MachTimebaseInfoData].}
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>".}
proc getTicks(): TTicks {.inline.} =
result = TTicks(mach_absolute_time())
proc getTicks(): Ticks {.inline.} =
result = Ticks(mach_absolute_time())
var timeBaseInfo: TMachTimebaseInfoData
var timeBaseInfo: MachTimebaseInfoData
mach_timebase_info(timeBaseInfo)
proc `-`(a, b: TTicks): TNanos =
proc `-`(a, b: Ticks): Nanos =
result = (a.int64 - b.int64) * timeBaseInfo.numer div timeBaseInfo.denom
elif defined(posixRealtime):
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
tv_sec: int ## Seconds.
tv_nsec: int ## Nanoseconds.
{.deprecated: [TClockid: Clickid, TTimeSpec: TimeSpec].}
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>".}
proc getTicks(): TTicks =
var t: TTimespec
proc getTicks(): Ticks =
var t: Timespec
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:
# fallback Posix implementation:
type
Ttimeval {.importc: "struct timeval", header: "<sys/select.h>",
Timeval {.importc: "struct timeval", header: "<sys/select.h>",
final, pure.} = object ## struct timeval
tv_sec: int ## Seconds.
tv_usec: int ## Microseconds.
proc posix_gettimeofday(tp: var Ttimeval, unused: pointer = nil) {.
{.deprecated: [Ttimeval: Timeval].}
proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
importc: "gettimeofday", header: "<sys/time.h>".}
proc getTicks(): TTicks =
var t: Ttimeval
proc getTicks(): Ticks =
var t: Timeval
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)
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".}
type
TUtf16Char* = distinct int16
WideCString* = ref array[0.. 1_000_000, TUtf16Char]
Utf16Char* = distinct int16
WideCString* = ref array[0.. 1_000_000, Utf16Char]
{.deprecated: [TUtf16Char: Utf16Char].}
proc len*(w: WideCString): int =
## 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
const
UNI_REPLACEMENT_CHAR = TUtf16Char(0xFFFD'i16)
UNI_REPLACEMENT_CHAR = Utf16Char(0xFFFD'i16)
UNI_MAX_BMP = 0x0000FFFF
UNI_MAX_UTF16 = 0x0010FFFF
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:
result[d] = UNI_REPLACEMENT_CHAR
else:
result[d] = TUtf16Char(toU16(ch))
result[d] = Utf16Char(toU16(ch))
elif ch >% UNI_MAX_UTF16:
result[d] = UNI_REPLACEMENT_CHAR
else:
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
result[d] = TUtf16Char(toU16((ch and halfMask) +% UNI_SUR_LOW_START))
result[d] = Utf16Char(toU16((ch and halfMask) +% UNI_SUR_LOW_START))
inc d
result[d] = TUtf16Char(0'i16)
result[d] = Utf16Char(0'i16)
proc newWideCString*(s: cstring): WideCString =
if s.isNil: return nil