Merge branch 'devel' into araq
This commit is contained in:
commit
39f0195ebf
334 changed files with 4636 additions and 2763 deletions
|
|
@ -172,7 +172,7 @@ elif defined(vcc) and hasThreadSupport:
|
|||
header: "<intrin.h>".}
|
||||
else:
|
||||
proc addAndFetch*(p: ptr int, val: int): int {.
|
||||
importcpp: "_InterlockedExchangeAdd(static_cast<NI volatile *>(#), #)",
|
||||
importcpp: "_InterlockedExchangeAdd(reinterpret_cast<LONG volatile *>(#), static_cast<LONG>(#))",
|
||||
header: "<intrin.h>".}
|
||||
else:
|
||||
when sizeof(int) == 8:
|
||||
|
|
|
|||
|
|
@ -30,13 +30,12 @@ type
|
|||
key: ByteAddress # start address at bit 0
|
||||
bits: array[BitIndex, int] # a bit vector
|
||||
|
||||
PPageDescArray = ptr array[0..1000_000, PPageDesc]
|
||||
PPageDescArray = ptr UncheckedArray[PPageDesc]
|
||||
CellSet {.final, pure.} = object
|
||||
counter, max: int
|
||||
head: PPageDesc
|
||||
data: PPageDescArray
|
||||
|
||||
PCellArray = ptr array[0..100_000_000, PCell]
|
||||
PCellArray = ptr UncheckedArray[PCell]
|
||||
CellSeq {.final, pure.} = object
|
||||
len, cap: int
|
||||
d: PCellArray
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ when not declared(NimString):
|
|||
type
|
||||
pbytes = ptr array[0.. 0xffff, byte]
|
||||
RawChannel {.pure, final.} = object ## msg queue for a thread
|
||||
rd, wr, count, mask: int
|
||||
rd, wr, count, mask, maxItems: int
|
||||
data: pbytes
|
||||
lock: SysLock
|
||||
cond: SysCond
|
||||
|
|
@ -37,11 +37,12 @@ type
|
|||
|
||||
const ChannelDeadMask = -2
|
||||
|
||||
proc initRawChannel(p: pointer) =
|
||||
proc initRawChannel(p: pointer, maxItems: int) =
|
||||
var c = cast[PRawChannel](p)
|
||||
initSysLock(c.lock)
|
||||
initSysCond(c.cond)
|
||||
c.mask = -1
|
||||
c.maxItems = maxItems
|
||||
|
||||
proc deinitRawChannel(p: pointer) =
|
||||
var c = cast[PRawChannel](p)
|
||||
|
|
@ -203,28 +204,41 @@ proc rawRecv(q: PRawChannel, data: pointer, typ: PNimType) =
|
|||
storeAux(data, addr(q.data[q.rd * typ.size]), typ, q, mLoad)
|
||||
q.rd = (q.rd + 1) and q.mask
|
||||
|
||||
template lockChannel(q: expr, action: stmt) {.immediate.} =
|
||||
template lockChannel(q, action): untyped =
|
||||
acquireSys(q.lock)
|
||||
action
|
||||
releaseSys(q.lock)
|
||||
|
||||
template sendImpl(q: expr) {.immediate.} =
|
||||
proc sendImpl(q: PRawChannel, typ: PNimType, msg: pointer, noBlock: bool): bool =
|
||||
if q.mask == ChannelDeadMask:
|
||||
sysFatal(DeadThreadError, "cannot send message; thread died")
|
||||
acquireSys(q.lock)
|
||||
var typ = cast[PNimType](getTypeInfo(msg))
|
||||
rawSend(q, unsafeAddr(msg), typ)
|
||||
if q.maxItems > 0:
|
||||
# Wait until count is less than maxItems
|
||||
if noBlock and q.count >= q.maxItems:
|
||||
releaseSys(q.lock)
|
||||
return
|
||||
|
||||
while q.count >= q.maxItems:
|
||||
waitSysCond(q.cond, q.lock)
|
||||
|
||||
rawSend(q, msg, typ)
|
||||
q.elemType = typ
|
||||
releaseSys(q.lock)
|
||||
signalSysCond(q.cond)
|
||||
result = true
|
||||
|
||||
proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) =
|
||||
proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) {.inline.} =
|
||||
## sends a message to a thread. `msg` is deeply copied.
|
||||
var q = cast[PRawChannel](addr(c))
|
||||
sendImpl(q)
|
||||
discard sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), false)
|
||||
|
||||
proc trySend*[TMsg](c: var Channel[TMsg], msg: TMsg): bool {.inline.} =
|
||||
## Tries to send a message to a thread. `msg` is deeply copied. Doesn't block.
|
||||
## Returns `false` if the message was not sent because number of pending items
|
||||
## in the cannel exceeded `maxItems`.
|
||||
sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), true)
|
||||
|
||||
proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
|
||||
# to save space, the generic is as small as possible
|
||||
q.ready = true
|
||||
while q.count <= 0:
|
||||
waitSysCond(q.cond, q.lock)
|
||||
|
|
@ -233,6 +247,9 @@ proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
|
|||
releaseSys(q.lock)
|
||||
sysFatal(ValueError, "cannot receive message of wrong type")
|
||||
rawRecv(q, res, typ)
|
||||
if q.maxItems > 0 and q.count == q.maxItems - 1:
|
||||
# Parent thread is awaiting in send. Wake it up.
|
||||
signalSysCond(q.cond)
|
||||
|
||||
proc recv*[TMsg](c: var Channel[TMsg]): TMsg =
|
||||
## receives a message from the channel `c`. This blocks until
|
||||
|
|
@ -267,9 +284,11 @@ proc peek*[TMsg](c: var Channel[TMsg]): int =
|
|||
else:
|
||||
result = -1
|
||||
|
||||
proc open*[TMsg](c: var Channel[TMsg]) =
|
||||
## opens a channel `c` for inter thread communication.
|
||||
initRawChannel(addr(c))
|
||||
proc open*[TMsg](c: var Channel[TMsg], maxItems: int = 0) =
|
||||
## opens a channel `c` for inter thread communication. The `send` operation
|
||||
## will block until number of unprocessed items is less than `maxItems`.
|
||||
## For unlimited queue set `maxItems` to 0.
|
||||
initRawChannel(addr(c), maxItems)
|
||||
|
||||
proc close*[TMsg](c: var Channel[TMsg]) =
|
||||
## closes a channel `c` and frees its associated resources.
|
||||
|
|
|
|||
|
|
@ -148,11 +148,11 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
|
|||
let realType = x.typ
|
||||
sysAssert realType == mt, " types do differ"
|
||||
# this version should work for any possible GC:
|
||||
let size = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[].size else: mt.base.size
|
||||
let z = newObj(mt, size)
|
||||
let typ = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[] else: mt.base
|
||||
let z = newObj(mt, typ.size)
|
||||
unsureAsgnRef(cast[PPointer](dest), z)
|
||||
tab.put(s2, z)
|
||||
genericDeepCopyAux(z, s2, mt.base, tab)
|
||||
genericDeepCopyAux(z, s2, typ, tab)
|
||||
else:
|
||||
unsureAsgnRef(cast[PPointer](dest), z)
|
||||
of tyPtr:
|
||||
|
|
|
|||
|
|
@ -293,11 +293,11 @@ proc raiseExceptionAux(e: ref Exception) =
|
|||
quitOrDebug()
|
||||
else:
|
||||
# ugly, but avoids heap allocations :-)
|
||||
template xadd(buf, s, slen: expr) =
|
||||
template xadd(buf, s, slen) =
|
||||
if L + slen < high(buf):
|
||||
copyMem(addr(buf[L]), cstring(s), slen)
|
||||
inc L, slen
|
||||
template add(buf, s: expr) =
|
||||
template add(buf, s) =
|
||||
xadd(buf, s, s.len)
|
||||
var buf: array[0..2000, char]
|
||||
var L = 0
|
||||
|
|
@ -404,7 +404,8 @@ when not defined(noSignalHandler):
|
|||
GC_enable()
|
||||
else:
|
||||
var msg: cstring
|
||||
template asgn(y: expr) = msg = y
|
||||
template asgn(y) =
|
||||
msg = y
|
||||
processSignal(sign, asgn)
|
||||
showErrorMessage(msg)
|
||||
when defined(endb): dbgAborting = true
|
||||
|
|
|
|||
|
|
@ -238,21 +238,6 @@ proc nimGCunref(p: pointer) {.compilerProc.} =
|
|||
|
||||
include gc_common
|
||||
|
||||
proc prepareDealloc(cell: PCell) =
|
||||
when useMarkForDebug:
|
||||
gcAssert(cell notin gch.marked, "Cell still alive!")
|
||||
let t = cell.typ
|
||||
if t.finalizer != nil:
|
||||
# the finalizer could invoke something that
|
||||
# allocates memory; this could trigger a garbage
|
||||
# collection. Since we are already collecting we
|
||||
# prevend recursive entering here by a lock.
|
||||
# XXX: we should set the cell's children to nil!
|
||||
inc(gch.recGcLock)
|
||||
(cast[Finalizer](t.finalizer))(cellToUsr(cell))
|
||||
dec(gch.recGcLock)
|
||||
decTypeSize(cell, t)
|
||||
|
||||
template beforeDealloc(gch: var GcHeap; c: PCell; msg: typed) =
|
||||
when false:
|
||||
for i in 0..gch.decStack.len-1:
|
||||
|
|
@ -274,6 +259,9 @@ proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} =
|
|||
sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 2")
|
||||
sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 5")
|
||||
|
||||
proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} =
|
||||
decRef(usrToCell(p))
|
||||
|
||||
proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} =
|
||||
# the code generator calls this proc!
|
||||
gcAssert(not isOnStack(dest), "asgnRef")
|
||||
|
|
@ -754,8 +742,8 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
|
|||
|
||||
#[
|
||||
This method is conditionally marked with an attribute so that it gets ignored by the LLVM ASAN
|
||||
(Address SANitizer) intrumentation as it will raise false errors due to the implementation of
|
||||
garbage collection that is used by Nim. For more information, please see the documentation of
|
||||
(Address SANitizer) intrumentation as it will raise false errors due to the implementation of
|
||||
garbage collection that is used by Nim. For more information, please see the documentation of
|
||||
`CLANG_NO_SANITIZE_ADDRESS` in `lib/nimbase.h`.
|
||||
]#
|
||||
proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl, codegenDecl: "CLANG_NO_SANITIZE_ADDRESS $# $#$#".} =
|
||||
|
|
@ -920,11 +908,13 @@ when not defined(useNimRtl):
|
|||
else:
|
||||
inc(gch.recGcLock)
|
||||
proc GC_enable() =
|
||||
if gch.recGcLock > 0:
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
discard atomicDec(gch.recGcLock, 1)
|
||||
else:
|
||||
dec(gch.recGcLock)
|
||||
if gch.recGcLock <= 0:
|
||||
raise newException(AssertionError,
|
||||
"API usage error: GC_enable called but GC is already enabled")
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
discard atomicDec(gch.recGcLock, 1)
|
||||
else:
|
||||
dec(gch.recGcLock)
|
||||
|
||||
proc GC_setStrategy(strategy: GC_Strategy) =
|
||||
discard
|
||||
|
|
@ -945,7 +935,6 @@ when not defined(useNimRtl):
|
|||
release(gch)
|
||||
|
||||
proc GC_getStatistics(): string =
|
||||
GC_disable()
|
||||
result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
|
||||
"[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
|
||||
"[GC] stack scans: " & $gch.stat.stackScans & "\n" &
|
||||
|
|
@ -961,6 +950,5 @@ when not defined(useNimRtl):
|
|||
result = result & "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & cast[pointer](stack.maxStackSize).repr & "\n"
|
||||
else:
|
||||
result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
GC_enable()
|
||||
|
||||
{.pop.} # profiler: off, stackTrace: off
|
||||
|
|
|
|||
|
|
@ -351,3 +351,35 @@ else:
|
|||
# ----------------------------------------------------------------------------
|
||||
# end of non-portable code
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
proc prepareDealloc(cell: PCell) =
|
||||
when declared(useMarkForDebug):
|
||||
when useMarkForDebug:
|
||||
gcAssert(cell notin gch.marked, "Cell still alive!")
|
||||
let t = cell.typ
|
||||
if t.finalizer != nil:
|
||||
# the finalizer could invoke something that
|
||||
# allocates memory; this could trigger a garbage
|
||||
# collection. Since we are already collecting we
|
||||
# prevend recursive entering here by a lock.
|
||||
# XXX: we should set the cell's children to nil!
|
||||
inc(gch.recGcLock)
|
||||
(cast[Finalizer](t.finalizer))(cellToUsr(cell))
|
||||
dec(gch.recGcLock)
|
||||
decTypeSize(cell, t)
|
||||
|
||||
proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) =
|
||||
## Frees the thread local heap. Runs every finalizer if ``runFinalizers```
|
||||
## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation
|
||||
## happens to ensure the GC can continue to work after the call
|
||||
## to ``deallocHeap``.
|
||||
if runFinalizers:
|
||||
for x in allObjects(gch.region):
|
||||
if isCell(x):
|
||||
# cast to PCell is correct here:
|
||||
var c = cast[PCell](x)
|
||||
prepareDealloc(c)
|
||||
deallocOsPages(gch.region)
|
||||
zeroMem(addr gch.region, sizeof(gch.region))
|
||||
if allowGcAfterwards:
|
||||
initGC()
|
||||
|
|
|
|||
|
|
@ -221,18 +221,6 @@ when defined(nimGcRefLeak):
|
|||
|
||||
include gc_common
|
||||
|
||||
proc prepareDealloc(cell: PCell) =
|
||||
if cell.typ.finalizer != nil:
|
||||
# the finalizer could invoke something that
|
||||
# allocates memory; this could trigger a garbage
|
||||
# collection. Since we are already collecting we
|
||||
# prevend recursive entering here by a lock.
|
||||
# XXX: we should set the cell's children to nil!
|
||||
inc(gch.recGcLock)
|
||||
(cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell))
|
||||
dec(gch.recGcLock)
|
||||
decTypeSize cell, cell.typ
|
||||
|
||||
proc initGC() =
|
||||
when not defined(useNimRtl):
|
||||
gch.cycleThreshold = InitialThreshold
|
||||
|
|
@ -506,11 +494,13 @@ when not defined(useNimRtl):
|
|||
else:
|
||||
inc(gch.recGcLock)
|
||||
proc GC_enable() =
|
||||
if gch.recGcLock > 0:
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
atomicDec(gch.recGcLock, 1)
|
||||
else:
|
||||
dec(gch.recGcLock)
|
||||
if gch.recGcLock <= 0:
|
||||
raise newException(AssertionError,
|
||||
"API usage error: GC_enable called but GC is already enabled")
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
atomicDec(gch.recGcLock, 1)
|
||||
else:
|
||||
dec(gch.recGcLock)
|
||||
|
||||
proc GC_setStrategy(strategy: GC_Strategy) = discard
|
||||
|
||||
|
|
@ -530,7 +520,6 @@ when not defined(useNimRtl):
|
|||
release(gch)
|
||||
|
||||
proc GC_getStatistics(): string =
|
||||
GC_disable()
|
||||
result = "[GC] total memory: " & $getTotalMem() & "\n" &
|
||||
"[GC] occupied memory: " & $getOccupiedMem() & "\n" &
|
||||
"[GC] collections: " & $gch.stat.collections & "\n" &
|
||||
|
|
@ -542,6 +531,5 @@ when not defined(useNimRtl):
|
|||
result = result & "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n"
|
||||
else:
|
||||
result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
GC_enable()
|
||||
|
||||
{.pop.}
|
||||
|
|
|
|||
|
|
@ -44,10 +44,6 @@ type
|
|||
typ: PNimType
|
||||
nextFinal: ptr ObjHeader # next object with finalizer
|
||||
|
||||
Hole = object # stacks can have holes. Otherwise 'growObj' would be insane.
|
||||
zeroTyp: pointer # overlaid with 'typ' field. Always 'nil'.
|
||||
size: int # size of the free slot
|
||||
|
||||
Chunk = ptr BaseChunk
|
||||
BaseChunk = object
|
||||
next: Chunk
|
||||
|
|
@ -55,7 +51,15 @@ type
|
|||
head, tail: ptr ObjHeader # first and last object in chunk that
|
||||
# has a finalizer attached to it
|
||||
|
||||
const
|
||||
MaxSmallObject = 128
|
||||
|
||||
type
|
||||
FreeEntry = ptr object
|
||||
next: FreeEntry
|
||||
SizedFreeEntry = ptr object
|
||||
next: SizedFreeEntry
|
||||
size: int
|
||||
StackPtr = object
|
||||
bump: pointer
|
||||
remaining: int
|
||||
|
|
@ -66,12 +70,21 @@ type
|
|||
bump: pointer
|
||||
head, tail: Chunk
|
||||
nextChunkSize, totalSize: int
|
||||
hole: ptr Hole # we support individual freeing
|
||||
freeLists: array[MaxSmallObject div MemAlign, FreeEntry]
|
||||
holes: SizedFreeEntry
|
||||
when hasThreadSupport:
|
||||
lock: SysLock
|
||||
|
||||
SeqHeader = object # minor hack ahead: Since we know that seqs
|
||||
# and strings cannot have finalizers, we use the field
|
||||
# instead for a 'region' field so that they can grow
|
||||
# and shrink safely.
|
||||
typ: PNimType
|
||||
region: ptr MemRegion
|
||||
|
||||
var
|
||||
tlRegion {.threadVar.}: MemRegion
|
||||
# tempStrRegion {.threadVar.}: MemRegion # not yet used
|
||||
|
||||
template withRegion*(r: MemRegion; body: untyped) =
|
||||
let oldRegion = tlRegion
|
||||
|
|
@ -85,6 +98,9 @@ template withRegion*(r: MemRegion; body: untyped) =
|
|||
template inc(p: pointer, s: int) =
|
||||
p = cast[pointer](cast[int](p) +% s)
|
||||
|
||||
template dec(p: pointer, s: int) =
|
||||
p = cast[pointer](cast[int](p) -% s)
|
||||
|
||||
template `+!`(p: pointer, s: int): pointer =
|
||||
cast[pointer](cast[int](p) +% s)
|
||||
|
||||
|
|
@ -128,7 +144,22 @@ proc allocSlowPath(r: var MemRegion; size: int) =
|
|||
r.tail = fresh
|
||||
r.remaining = s - sizeof(BaseChunk)
|
||||
|
||||
proc alloc(r: var MemRegion; size: int): pointer {.inline.} =
|
||||
proc alloc(r: var MemRegion; size: int): pointer =
|
||||
if size <= MaxSmallObject:
|
||||
var it = r.freeLists[size div MemAlign]
|
||||
if it != nil:
|
||||
r.freeLists[size div MemAlign] = it.next
|
||||
return pointer(it)
|
||||
else:
|
||||
var it = r.holes
|
||||
var prev: SizedFreeEntry = nil
|
||||
while it != nil:
|
||||
if it.size >= size:
|
||||
if prev != nil: prev.next = it.next
|
||||
else: r.holes = it.next
|
||||
return pointer(it)
|
||||
prev = it
|
||||
it = it.next
|
||||
if size > r.remaining:
|
||||
allocSlowPath(r, size)
|
||||
sysAssert(size <= r.remaining, "size <= r.remaining")
|
||||
|
|
@ -145,12 +176,23 @@ proc runFinalizers(c: Chunk) =
|
|||
(cast[Finalizer](it.typ.finalizer))(it+!sizeof(ObjHeader))
|
||||
it = it.nextFinal
|
||||
|
||||
when false:
|
||||
proc dealloc(r: var MemRegion; p: pointer) =
|
||||
let it = cast[ptr ObjHeader](p-!sizeof(ObjHeader))
|
||||
if it.typ != nil and it.typ.finalizer != nil:
|
||||
(cast[Finalizer](it.typ.finalizer))(p)
|
||||
it.typ = nil
|
||||
proc dealloc(r: var MemRegion; p: pointer; size: int) =
|
||||
let it = cast[ptr ObjHeader](p-!sizeof(ObjHeader))
|
||||
if it.typ != nil and it.typ.finalizer != nil:
|
||||
(cast[Finalizer](it.typ.finalizer))(p)
|
||||
it.typ = nil
|
||||
# it is benefitial to not use the free lists here:
|
||||
if r.bump -! size == p:
|
||||
dec r.bump, size
|
||||
elif size <= MaxSmallObject:
|
||||
let it = cast[FreeEntry](p)
|
||||
it.next = r.freeLists[size div MemAlign]
|
||||
r.freeLists[size div MemAlign] = it
|
||||
else:
|
||||
let it = cast[SizedFreeEntry](p)
|
||||
it.size = size
|
||||
it.next = r.holes
|
||||
r.holes = it
|
||||
|
||||
proc deallocAll(r: var MemRegion; head: Chunk) =
|
||||
var it = head
|
||||
|
|
@ -175,12 +217,15 @@ template computeRemaining(r): untyped =
|
|||
|
||||
proc setObstackPtr*(r: var MemRegion; sp: StackPtr) =
|
||||
# free everything after 'sp':
|
||||
if sp.current != nil:
|
||||
if sp.current.next != nil:
|
||||
deallocAll(r, sp.current.next)
|
||||
sp.current.next = nil
|
||||
else:
|
||||
deallocAll(r, r.head)
|
||||
r.head = nil
|
||||
# better leak this memory than be sorry:
|
||||
for i in 0..high(r.freeLists): r.freeLists[i] = nil
|
||||
r.holes = nil
|
||||
#else:
|
||||
# deallocAll(r, r.head)
|
||||
# r.head = nil
|
||||
r.bump = sp.bump
|
||||
r.tail = sp.current
|
||||
r.remaining = sp.remaining
|
||||
|
|
@ -191,17 +236,28 @@ proc deallocAll*() = tlRegion.deallocAll()
|
|||
|
||||
proc deallocOsPages(r: var MemRegion) = r.deallocAll()
|
||||
|
||||
proc joinRegion*(dest: var MemRegion; src: MemRegion) =
|
||||
# merging is not hard.
|
||||
if dest.head.isNil:
|
||||
dest.head = src.head
|
||||
else:
|
||||
dest.tail.next = src.head
|
||||
dest.tail = src.tail
|
||||
dest.bump = src.bump
|
||||
dest.remaining = src.remaining
|
||||
dest.nextChunkSize = max(dest.nextChunkSize, src.nextChunkSize)
|
||||
inc dest.totalSize, src.totalSize
|
||||
template withScratchRegion*(body: untyped) =
|
||||
var scratch: MemRegion
|
||||
let oldRegion = tlRegion
|
||||
tlRegion = scratch
|
||||
try:
|
||||
body
|
||||
finally:
|
||||
tlRegion = oldRegion
|
||||
deallocAll(scratch)
|
||||
|
||||
when false:
|
||||
proc joinRegion*(dest: var MemRegion; src: MemRegion) =
|
||||
# merging is not hard.
|
||||
if dest.head.isNil:
|
||||
dest.head = src.head
|
||||
else:
|
||||
dest.tail.next = src.head
|
||||
dest.tail = src.tail
|
||||
dest.bump = src.bump
|
||||
dest.remaining = src.remaining
|
||||
dest.nextChunkSize = max(dest.nextChunkSize, src.nextChunkSize)
|
||||
inc dest.totalSize, src.totalSize
|
||||
|
||||
proc isOnHeap*(r: MemRegion; p: pointer): bool =
|
||||
# the tail chunk is the largest, so check it first. It's also special
|
||||
|
|
@ -213,159 +269,6 @@ proc isOnHeap*(r: MemRegion; p: pointer): bool =
|
|||
if it >= p and p <= it+!it.size: return true
|
||||
it = it.next
|
||||
|
||||
when false:
|
||||
# essential feature for later: copy data over from one region to another
|
||||
|
||||
proc isInteriorPointer(r: MemRegion; p: pointer): pointer =
|
||||
discard " we cannot patch stack pointers anyway!"
|
||||
|
||||
type
|
||||
PointerStackChunk = object
|
||||
next, prev: ptr PointerStackChunk
|
||||
len: int
|
||||
data: array[128, pointer]
|
||||
|
||||
template head(s: PointerStackChunk): untyped = s.prev
|
||||
template tail(s: PointerStackChunk): untyped = s.next
|
||||
|
||||
include chains
|
||||
|
||||
proc push(r: var MemRegion; s: var PointerStackChunk; x: pointer) =
|
||||
if s.len < high(s.data):
|
||||
s.data[s.len] = x
|
||||
inc s.len
|
||||
else:
|
||||
let fresh = cast[ptr PointerStackChunk](alloc(r, sizeof(PointerStackChunk)))
|
||||
fresh.len = 1
|
||||
fresh.data[0] = x
|
||||
fresh.next = nil
|
||||
fresh.prev = nil
|
||||
append(s, fresh)
|
||||
|
||||
|
||||
proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk;
|
||||
dest, src: pointer, mt: PNimType) {.benign.}
|
||||
proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk;
|
||||
dest, src: pointer, n: ptr TNimNode) {.benign.} =
|
||||
var
|
||||
d = cast[ByteAddress](dest)
|
||||
s = cast[ByteAddress](src)
|
||||
case n.kind
|
||||
of nkSlot:
|
||||
genericDeepCopyAux(cast[pointer](d +% n.offset),
|
||||
cast[pointer](s +% n.offset), n.typ)
|
||||
of nkList:
|
||||
for i in 0..n.len-1:
|
||||
genericDeepCopyAux(dest, src, n.sons[i])
|
||||
of nkCase:
|
||||
var dd = selectBranch(dest, n)
|
||||
var m = selectBranch(src, n)
|
||||
# reset if different branches are in use; note different branches also
|
||||
# imply that's not self-assignment (``x = x``)!
|
||||
if m != dd and dd != nil:
|
||||
genericResetAux(dest, dd)
|
||||
copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset),
|
||||
n.typ.size)
|
||||
if m != nil:
|
||||
genericDeepCopyAux(dest, src, m)
|
||||
of nkNone: sysAssert(false, "genericDeepCopyAux")
|
||||
|
||||
proc copyDeepString(dr: var MemRegion; stack: var PointerStackChunk; src: NimString): NimString {.inline.} =
|
||||
result = rawNewStringNoInit(dr, src.len)
|
||||
result.len = src.len
|
||||
copyMem(result.data, src.data, src.len + 1)
|
||||
|
||||
proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk;
|
||||
dest, src: pointer, mt: PNimType) =
|
||||
var
|
||||
d = cast[ByteAddress](dest)
|
||||
s = cast[ByteAddress](src)
|
||||
sysAssert(mt != nil, "genericDeepCopyAux 2")
|
||||
case mt.kind
|
||||
of tyString:
|
||||
var x = cast[PPointer](dest)
|
||||
var s2 = cast[PPointer](s)[]
|
||||
if s2 == nil:
|
||||
x[] = nil
|
||||
else:
|
||||
x[] = copyDeepString(cast[NimString](s2))
|
||||
of tySequence:
|
||||
var s2 = cast[PPointer](src)[]
|
||||
var seq = cast[PGenericSeq](s2)
|
||||
var x = cast[PPointer](dest)
|
||||
if s2 == nil:
|
||||
x[] = nil
|
||||
return
|
||||
sysAssert(dest != nil, "genericDeepCopyAux 3")
|
||||
x[] = newSeq(mt, seq.len)
|
||||
var dst = cast[ByteAddress](cast[PPointer](dest)[])
|
||||
for i in 0..seq.len-1:
|
||||
genericDeepCopyAux(dr, stack,
|
||||
cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize),
|
||||
cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +%
|
||||
GenericSeqSize),
|
||||
mt.base)
|
||||
of tyObject:
|
||||
# we need to copy m_type field for tyObject, as it could be empty for
|
||||
# sequence reallocations:
|
||||
var pint = cast[ptr PNimType](dest)
|
||||
pint[] = cast[ptr PNimType](src)[]
|
||||
if mt.base != nil:
|
||||
genericDeepCopyAux(dr, stack, dest, src, mt.base)
|
||||
genericDeepCopyAux(dr, stack, dest, src, mt.node)
|
||||
of tyTuple:
|
||||
genericDeepCopyAux(dr, stack, dest, src, mt.node)
|
||||
of tyArray, tyArrayConstr:
|
||||
for i in 0..(mt.size div mt.base.size)-1:
|
||||
genericDeepCopyAux(dr, stack,
|
||||
cast[pointer](d +% i*% mt.base.size),
|
||||
cast[pointer](s +% i*% mt.base.size), mt.base)
|
||||
of tyRef:
|
||||
let s2 = cast[PPointer](src)[]
|
||||
if s2 == nil:
|
||||
cast[PPointer](dest)[] = nil
|
||||
else:
|
||||
# we modify the header of the cell temporarily; instead of the type
|
||||
# field we store a forwarding pointer. XXX This is bad when the cloning
|
||||
# fails due to OOM etc.
|
||||
let x = usrToCell(s2)
|
||||
let forw = cast[int](x.typ)
|
||||
if (forw and 1) == 1:
|
||||
# we stored a forwarding pointer, so let's use that:
|
||||
let z = cast[pointer](forw and not 1)
|
||||
unsureAsgnRef(cast[PPointer](dest), z)
|
||||
else:
|
||||
let realType = x.typ
|
||||
let z = newObj(realType, realType.base.size)
|
||||
|
||||
unsureAsgnRef(cast[PPointer](dest), z)
|
||||
x.typ = cast[PNimType](cast[int](z) or 1)
|
||||
genericDeepCopyAux(dr, stack, z, s2, realType.base)
|
||||
x.typ = realType
|
||||
else:
|
||||
copyMem(dest, src, mt.size)
|
||||
|
||||
proc joinAliveDataFromRegion*(dest: var MemRegion; src: var MemRegion;
|
||||
root: pointer): pointer =
|
||||
# we mark the alive data and copy only alive data over to 'dest'.
|
||||
# This is O(liveset) but it nicely compacts memory, so it's fine.
|
||||
# We use the 'typ' field as a forwarding pointer. The forwarding
|
||||
# pointers have bit 0 set, so we can disambiguate them.
|
||||
# We allocate a temporary stack in 'src' that we later free:
|
||||
var s: PointerStackChunk
|
||||
s.len = 1
|
||||
s.data[0] = root
|
||||
while s.len > 0:
|
||||
var p: pointer
|
||||
if s.tail == nil:
|
||||
p = s.data[s.len-1]
|
||||
dec s.len
|
||||
else:
|
||||
p = s.tail.data[s.tail.len-1]
|
||||
dec s.tail.len
|
||||
if s.tail.len == 0:
|
||||
unlink(s, s.tail)
|
||||
|
||||
proc rawNewObj(r: var MemRegion, typ: PNimType, size: int): pointer =
|
||||
var res = cast[ptr ObjHeader](alloc(r, size + sizeof(ObjHeader)))
|
||||
res.typ = typ
|
||||
|
|
@ -374,6 +277,12 @@ proc rawNewObj(r: var MemRegion, typ: PNimType, size: int): pointer =
|
|||
r.head.head = res
|
||||
result = res +! sizeof(ObjHeader)
|
||||
|
||||
proc rawNewSeq(r: var MemRegion, typ: PNimType, size: int): pointer =
|
||||
var res = cast[ptr SeqHeader](alloc(r, size + sizeof(SeqHeader)))
|
||||
res.typ = typ
|
||||
res.region = addr(r)
|
||||
result = res +! sizeof(SeqHeader)
|
||||
|
||||
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
result = rawNewObj(tlRegion, typ, size)
|
||||
zeroMem(result, size)
|
||||
|
|
@ -384,28 +293,37 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
|||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
|
||||
result = newObj(typ, size)
|
||||
let size = roundup(addInt(mulInt(len, typ.base.size), GenericSeqSize),
|
||||
MemAlign)
|
||||
result = rawNewSeq(tlRegion, typ, size)
|
||||
zeroMem(result, size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
|
||||
proc newStr(typ: PNimType, len: int; init: bool): pointer {.compilerRtl.} =
|
||||
let size = roundup(addInt(len, GenericSeqSize), MemAlign)
|
||||
result = rawNewSeq(tlRegion, typ, size)
|
||||
if init: zeroMem(result, size)
|
||||
cast[PGenericSeq](result).len = 0
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
|
||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
result = rawNewObj(tlRegion, typ, size)
|
||||
zeroMem(result, size)
|
||||
|
||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
|
||||
result = newObj(typ, size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
result = newSeq(typ, len)
|
||||
|
||||
proc growObj(region: var MemRegion; old: pointer, newsize: int): pointer =
|
||||
let typ = cast[ptr ObjHeader](old -! sizeof(ObjHeader)).typ
|
||||
result = rawNewObj(region, typ, newsize)
|
||||
proc growObj(regionUnused: var MemRegion; old: pointer, newsize: int): pointer =
|
||||
let sh = cast[ptr SeqHeader](old -! sizeof(SeqHeader))
|
||||
let typ = sh.typ
|
||||
result = rawNewSeq(sh.region[], typ,
|
||||
roundup(newsize, MemAlign))
|
||||
let elemSize = if typ.kind == tyString: 1 else: typ.base.size
|
||||
let oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
|
||||
copyMem(result, old, oldsize)
|
||||
zeroMem(result +! oldsize, newsize-oldsize)
|
||||
copyMem(result, old, oldsize)
|
||||
dealloc(sh.region[], old, roundup(oldsize, MemAlign))
|
||||
|
||||
proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
|
||||
result = growObj(tlRegion, old, newsize)
|
||||
|
|
@ -53,7 +53,7 @@ proc isNimException(): bool {.asmNoStackFrame.} =
|
|||
else:
|
||||
asm "return `lastJSError`.m_type;"
|
||||
|
||||
proc getCurrentException*(): ref Exception =
|
||||
proc getCurrentException*(): ref Exception {.compilerRtl, benign.} =
|
||||
if isNimException(): result = cast[ref Exception](lastJSError)
|
||||
|
||||
proc getCurrentExceptionMsg*(): string =
|
||||
|
|
@ -157,10 +157,10 @@ proc reraiseException() {.compilerproc, asmNoStackFrame.} =
|
|||
|
||||
asm "throw lastJSError;"
|
||||
|
||||
proc raiseOverflow {.exportc: "raiseOverflow", noreturn.} =
|
||||
proc raiseOverflow {.exportc: "raiseOverflow", noreturn, compilerProc.} =
|
||||
raise newException(OverflowError, "over- or underflow")
|
||||
|
||||
proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn.} =
|
||||
proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn, compilerProc.} =
|
||||
raise newException(DivByZeroError, "division by zero")
|
||||
|
||||
proc raiseRangeError() {.compilerproc, noreturn.} =
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
const
|
||||
debugGC = false # we wish to debug the GC...
|
||||
logGC = false
|
||||
traceGC = defined(smokeCycles) # extensive debugging
|
||||
traceGC = false # extensive debugging
|
||||
alwaysCycleGC = defined(smokeCycles)
|
||||
alwaysGC = defined(fulldebug) # collect after every memory
|
||||
# allocation (for debugging)
|
||||
|
|
@ -34,7 +34,7 @@ const
|
|||
|
||||
type
|
||||
PPointer = ptr pointer
|
||||
ByteArray = array[0..ArrayDummySize, byte]
|
||||
ByteArray = UncheckedArray[byte]
|
||||
PByte = ptr ByteArray
|
||||
PString = ptr string
|
||||
{.deprecated: [TByteArray: ByteArray].}
|
||||
|
|
@ -543,7 +543,7 @@ elif defined(nogc):
|
|||
include "system/cellsets"
|
||||
|
||||
else:
|
||||
when not defined(gcStack):
|
||||
when not defined(gcRegions):
|
||||
include "system/alloc"
|
||||
|
||||
include "system/cellsets"
|
||||
|
|
@ -551,9 +551,9 @@ else:
|
|||
sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell")
|
||||
when compileOption("gc", "v2"):
|
||||
include "system/gc2"
|
||||
elif defined(gcStack):
|
||||
elif defined(gcRegions):
|
||||
# XXX due to bootstrapping reasons, we cannot use compileOption("gc", "stack") here
|
||||
include "system/gc_stack"
|
||||
include "system/gc_regions"
|
||||
elif defined(gcMarkAndSweep):
|
||||
# XXX use 'compileOption' here
|
||||
include "system/gc_ms"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ type
|
|||
amd64, ## x86_64 (AMD64); 64 bit x86 compatible CPU
|
||||
mips, ## Mips based processor
|
||||
mipsel, ## Little Endian Mips based processor
|
||||
mips64, ## 64-bit MIPS processor
|
||||
mips64el, ## Little Endian 64-bit MIPS processor
|
||||
arm, ## ARM based processor
|
||||
arm64, ## ARM64 based processor
|
||||
vm, ## Some Virtual machine: Nim's VM or JavaScript
|
||||
|
|
@ -33,7 +35,8 @@ type
|
|||
OsPlatform* {.pure.} = enum ## the OS this program will run on.
|
||||
none, dos, windows, os2, linux, morphos, skyos, solaris,
|
||||
irix, netbsd, freebsd, openbsd, aix, palmos, qnx, amiga,
|
||||
atari, netware, macos, macosx, haiku, js, nimVM, standalone
|
||||
atari, netware, macos, macosx, haiku, android, js, nimVM,
|
||||
standalone
|
||||
|
||||
const
|
||||
targetOS* = when defined(windows): OsPlatform.windows
|
||||
|
|
@ -56,6 +59,7 @@ const
|
|||
elif defined(macosx): OsPlatform.macosx
|
||||
elif defined(macos): OsPlatform.macos
|
||||
elif defined(haiku): OsPlatform.haiku
|
||||
elif defined(android): OsPlatform.android
|
||||
elif defined(js): OsPlatform.js
|
||||
elif defined(nimrodVM): OsPlatform.nimVM
|
||||
elif defined(standalone): OsPlatform.standalone
|
||||
|
|
@ -73,6 +77,8 @@ const
|
|||
elif defined(amd64): CpuPlatform.amd64
|
||||
elif defined(mips): CpuPlatform.mips
|
||||
elif defined(mipsel): CpuPlatform.mipsel
|
||||
elif defined(mips64): CpuPlatform.mips64
|
||||
elif defined(mips64el): CpuPlatform.mips64el
|
||||
elif defined(arm): CpuPlatform.arm
|
||||
elif defined(arm64): CpuPlatform.arm64
|
||||
elif defined(vm): CpuPlatform.vm
|
||||
|
|
|
|||
|
|
@ -86,11 +86,11 @@ proc writeBuffer(f: File, buffer: pointer, len: Natural): int =
|
|||
checkErr(f)
|
||||
|
||||
proc writeBytes(f: File, a: openArray[int8|uint8], start, len: Natural): int =
|
||||
var x = cast[ptr array[0..1000_000_000, int8]](a)
|
||||
result = writeBuffer(f, addr(x[start]), len)
|
||||
var x = cast[ptr UncheckedArray[int8]](a)
|
||||
result = writeBuffer(f, addr(x[int(start)]), len)
|
||||
proc writeChars(f: File, a: openArray[char], start, len: Natural): int =
|
||||
var x = cast[ptr array[0..1000_000_000, int8]](a)
|
||||
result = writeBuffer(f, addr(x[start]), len)
|
||||
var x = cast[ptr UncheckedArray[int8]](a)
|
||||
result = writeBuffer(f, addr(x[int(start)]), len)
|
||||
|
||||
proc write(f: File, s: string) =
|
||||
if writeBuffer(f, cstring(s), s.len) != s.len:
|
||||
|
|
|
|||
|
|
@ -38,6 +38,13 @@ when declared(allocAtomic):
|
|||
|
||||
template allocStrNoInit(size: untyped): untyped =
|
||||
cast[NimString](boehmAllocAtomic(size))
|
||||
elif defined(gcRegions):
|
||||
template allocStr(size: untyped): untyped =
|
||||
cast[NimString](newStr(addr(strDesc), size, true))
|
||||
|
||||
template allocStrNoInit(size: untyped): untyped =
|
||||
cast[NimString](newStr(addr(strDesc), size, false))
|
||||
|
||||
else:
|
||||
template allocStr(size: untyped): untyped =
|
||||
cast[NimString](newObj(addr(strDesc), size))
|
||||
|
|
@ -99,7 +106,7 @@ proc copyString(src: NimString): NimString {.compilerRtl.} =
|
|||
|
||||
proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
|
||||
if src != nil:
|
||||
when declared(newObjRC1):
|
||||
when declared(newObjRC1) and not defined(gcRegions):
|
||||
var s = src.len
|
||||
if s < 7: s = 7
|
||||
result = cast[NimString](newObjRC1(addr(strDesc), sizeof(TGenericSeq) +
|
||||
|
|
@ -235,7 +242,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
|
|||
# we need to decref here, otherwise the GC leaks!
|
||||
when not defined(boehmGC) and not defined(nogc) and
|
||||
not defined(gcMarkAndSweep) and not defined(gogc) and
|
||||
not defined(gcStack):
|
||||
not defined(gcRegions):
|
||||
when false: # compileOption("gc", "v2"):
|
||||
for i in newLen..result.len-1:
|
||||
let len0 = gch.tempStack.len
|
||||
|
|
|
|||
|
|
@ -115,10 +115,6 @@ when defined(windows):
|
|||
proc setThreadAffinityMask(hThread: SysThread, dwThreadAffinityMask: uint) {.
|
||||
importc: "SetThreadAffinityMask", stdcall, header: "<windows.h>".}
|
||||
|
||||
proc getThreadId*(): int =
|
||||
## get the ID of the currently running thread.
|
||||
result = int(getCurrentThreadId())
|
||||
|
||||
elif defined(genode):
|
||||
const
|
||||
GenodeHeader = "genode_cpp/threads.h"
|
||||
|
|
@ -249,48 +245,6 @@ else:
|
|||
proc setAffinity(thread: SysThread; setsize: csize; s: var CpuSet) {.
|
||||
importc: "pthread_setaffinity_np", header: pthreadh.}
|
||||
|
||||
when defined(linux):
|
||||
proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "<unistd.h>".}
|
||||
var NR_gettid {.importc: "__NR_gettid", header: "<sys/syscall.h>".}: int
|
||||
|
||||
#type Pid {.importc: "pid_t", header: "<sys/types.h>".} = distinct int
|
||||
#proc gettid(): Pid {.importc, header: "<sys/types.h>".}
|
||||
|
||||
proc getThreadId*(): int =
|
||||
## get the ID of the currently running thread.
|
||||
result = int(syscall(NR_gettid))
|
||||
elif defined(dragonfly):
|
||||
proc lwp_gettid(): int32 {.importc, header: "unistd.h".}
|
||||
|
||||
proc getThreadId*(): int =
|
||||
result = int(lwp_gettid())
|
||||
elif defined(openbsd):
|
||||
proc getthrid(): int32 {.importc: "getthrid", header: "<unistd.h>".}
|
||||
|
||||
proc getThreadId*(): int =
|
||||
result = int(getthrid())
|
||||
elif defined(netbsd):
|
||||
proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".}
|
||||
|
||||
proc getThreadId*(): int =
|
||||
result = int(lwp_self())
|
||||
elif defined(macosx) or defined(freebsd):
|
||||
proc pthread_threadid_np(y: pointer; x: var uint64): cint {.importc, header: "pthread.h".}
|
||||
|
||||
proc getThreadId*(): int =
|
||||
## get the ID of the currently running thread.
|
||||
var x: uint64
|
||||
result = pthread_threadid_np(nil, x)
|
||||
result = int(x)
|
||||
elif defined(solaris):
|
||||
# just a guess really:
|
||||
type thread_t {.importc: "thread_t", header: "<thread.h>".} = distinct int
|
||||
proc thr_self(): thread_t {.importc, header: "<thread.h>".}
|
||||
|
||||
proc getThreadId*(): int =
|
||||
## get the ID of the currently running thread.
|
||||
result = int(thr_self())
|
||||
|
||||
const
|
||||
emulatedThreadVars = compileOption("tlsEmulation")
|
||||
|
||||
|
|
@ -302,8 +256,9 @@ when emulatedThreadVars:
|
|||
# we preallocate a fixed size for thread local storage, so that no heap
|
||||
# allocations are needed. Currently less than 7K are used on a 64bit machine.
|
||||
# We use ``float`` for proper alignment:
|
||||
const nimTlsSize {.intdefine.} = 8000
|
||||
type
|
||||
ThreadLocalStorage = array[0..1_000, float]
|
||||
ThreadLocalStorage = array[0..(nimTlsSize div sizeof(float)), float]
|
||||
|
||||
PGcThread = ptr GcThread
|
||||
GcThread {.pure, inheritable.} = object
|
||||
|
|
@ -369,7 +324,11 @@ when not defined(useNimRtl):
|
|||
|
||||
when emulatedThreadVars:
|
||||
if nimThreadVarsSize() > sizeof(ThreadLocalStorage):
|
||||
echo "too large thread local storage size requested"
|
||||
echo "too large thread local storage size requested ",
|
||||
"(", nimThreadVarsSize(), "/", sizeof(ThreadLocalStorage), "). ",
|
||||
"Use -d:\"nimTlsSize=", nimThreadVarsSize(),
|
||||
"\" to preallocate sufficient storage."
|
||||
|
||||
quit 1
|
||||
|
||||
when hasSharedHeap and not defined(boehmgc) and not defined(gogc) and not defined(nogc):
|
||||
|
|
@ -437,7 +396,7 @@ template afterThreadRuns() =
|
|||
for i in countdown(threadDestructionHandlers.len-1, 0):
|
||||
threadDestructionHandlers[i]()
|
||||
|
||||
when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcstack):
|
||||
when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions):
|
||||
proc deallocOsPages()
|
||||
|
||||
when defined(boehmgc):
|
||||
|
|
@ -475,7 +434,7 @@ else:
|
|||
proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) =
|
||||
when defined(boehmgc):
|
||||
boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd)
|
||||
elif not defined(nogc) and not defined(gogc) and not defined(gcstack):
|
||||
elif not defined(nogc) and not defined(gogc) and not defined(gcRegions):
|
||||
var p {.volatile.}: proc(a: ptr Thread[TArg]) {.nimcall.} =
|
||||
threadProcWrapDispatch[TArg]
|
||||
when not hasSharedHeap:
|
||||
|
|
@ -493,7 +452,7 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) =
|
|||
else:
|
||||
threadProcWrapDispatch(thrd)
|
||||
|
||||
template threadProcWrapperBody(closure: expr) {.immediate.} =
|
||||
template threadProcWrapperBody(closure: untyped): untyped =
|
||||
var thrd = cast[ptr Thread[TArg]](closure)
|
||||
var core = thrd.core
|
||||
when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core)
|
||||
|
|
@ -665,3 +624,82 @@ when useStackMaskHack:
|
|||
var mainThread: Thread[pointer]
|
||||
createThread(mainThread, tp)
|
||||
joinThread(mainThread)
|
||||
|
||||
## we need to cache current threadId to not perform syscall all the time
|
||||
var threadId {.threadvar.}: int
|
||||
|
||||
when defined(windows):
|
||||
proc getThreadId*(): int =
|
||||
## get the ID of the currently running thread.
|
||||
if threadId == 0:
|
||||
threadId = int(getCurrentThreadId())
|
||||
result = threadId
|
||||
|
||||
elif defined(linux):
|
||||
proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "<unistd.h>".}
|
||||
var NR_gettid {.importc: "__NR_gettid", header: "<sys/syscall.h>".}: int
|
||||
|
||||
proc getThreadId*(): int =
|
||||
## get the ID of the currently running thread.
|
||||
if threadId == 0:
|
||||
threadId = int(syscall(NR_gettid))
|
||||
result = threadId
|
||||
|
||||
elif defined(dragonfly):
|
||||
proc lwp_gettid(): int32 {.importc, header: "unistd.h".}
|
||||
|
||||
proc getThreadId*(): int =
|
||||
## get the ID of the currently running thread.
|
||||
if threadId == 0:
|
||||
threadId = int(lwp_gettid())
|
||||
result = threadId
|
||||
|
||||
elif defined(openbsd):
|
||||
proc getthrid(): int32 {.importc: "getthrid", header: "<unistd.h>".}
|
||||
|
||||
proc getThreadId*(): int =
|
||||
## get the ID of the currently running thread.
|
||||
if threadId == 0:
|
||||
threadId = int(getthrid())
|
||||
result = threadId
|
||||
|
||||
elif defined(netbsd):
|
||||
proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".}
|
||||
|
||||
proc getThreadId*(): int =
|
||||
## get the ID of the currently running thread.
|
||||
if threadId == 0:
|
||||
threadId = int(lwp_self())
|
||||
result = threadId
|
||||
|
||||
elif defined(freebsd):
|
||||
proc syscall(arg: cint, arg0: ptr cint): cint {.varargs, importc: "syscall", header: "<unistd.h>".}
|
||||
var SYS_thr_self {.importc:"SYS_thr_self", header:"<sys/syscall.h>"}: cint
|
||||
|
||||
proc getThreadId*(): int =
|
||||
## get the ID of the currently running thread.
|
||||
var tid = 0.cint
|
||||
if threadId == 0:
|
||||
discard syscall(SYS_thr_self, addr tid)
|
||||
threadId = tid
|
||||
result = threadId
|
||||
|
||||
elif defined(macosx):
|
||||
proc syscall(arg: cint): cint {.varargs, importc: "syscall", header: "<unistd.h>".}
|
||||
var SYS_thread_selfid {.importc:"SYS_thread_selfid", header:"<sys/syscall.h>".}: cint
|
||||
|
||||
proc getThreadId*(): int =
|
||||
## get the ID of the currently running thread.
|
||||
if threadId == 0:
|
||||
threadId = int(syscall(SYS_thread_selfid))
|
||||
result = threadId
|
||||
|
||||
elif defined(solaris):
|
||||
type thread_t {.importc: "thread_t", header: "<thread.h>".} = distinct int
|
||||
proc thr_self(): thread_t {.importc, header: "<thread.h>".}
|
||||
|
||||
proc getThreadId*(): int =
|
||||
## get the ID of the currently running thread.
|
||||
if threadId == 0:
|
||||
threadId = int(thr_self())
|
||||
result = threadId
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ when not declared(NimString):
|
|||
|
||||
type
|
||||
Utf16Char* = distinct int16
|
||||
WideCString* = ref array[0.. 1_000_000, Utf16Char]
|
||||
WideCString* = ref UncheckedArray[Utf16Char]
|
||||
{.deprecated: [TUtf16Char: Utf16Char].}
|
||||
|
||||
proc len*(w: WideCString): int =
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue