Merge branch 'devel' into araq

This commit is contained in:
Andreas Rumpf 2017-09-15 09:27:51 +02:00
commit 39f0195ebf
334 changed files with 4636 additions and 2763 deletions

View file

@ -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:

View file

@ -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

View file

@ -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.

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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.}

View file

@ -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)

View file

@ -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.} =

View file

@ -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"

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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 =