#
#
# Nim's Runtime Library
# (c) Copyright 2019 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
#[
In this new runtime we simplify the object layouts a bit: The runtime type
information is only accessed for the objects that have it and it's always
at offset 0 then. The ``ref`` object header is independent from the
runtime type and only contains a reference count.
]#
when defined(gcOrc):
const
rcIncrement = 0b10000 # so that lowest 4 bits are not touched
rcMask = 0b1111
rcShift = 4 # shift by rcShift to get the reference counter
else:
const
rcIncrement = 0b1000 # so that lowest 3 bits are not touched
rcMask = 0b111
rcShift = 3 # shift by rcShift to get the reference counter
type
RefHeader = object
rc: int # the object header is now a single RC field.
# we could remove it in non-debug builds for the 'owned ref'
# design but this seems unwise.
when defined(gcOrc):
rootIdx: int # thanks to this we can delete potential cycle roots
# in O(1) without doubly linked lists
when defined(nimArcDebug) or defined(nimArcIds):
refId: int
Cell = ptr RefHeader
template head(p: pointer): Cell =
cast[Cell](cast[int](p) -% sizeof(RefHeader))
const
traceCollector = defined(traceArc)
when defined(nimArcDebug):
include cellsets
const traceId = 20 # 1037
var gRefId: int
var freedCells: CellSet
elif defined(nimArcIds):
var gRefId: int
const traceId = -1
proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} =
let hdrSize = align(sizeof(RefHeader), alignment)
let s = size + hdrSize
when defined(nimscript):
discard
else:
result = alignedAlloc0(s, alignment) +! hdrSize
when defined(nimArcDebug) or defined(nimArcIds):
head(result).refId = gRefId
atomicInc gRefId
if head(result).refId == traceId:
writeStackTrace()
cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).rc shr rcShift)
when traceCollector:
cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} =
# Same as 'newNewObj' but do not initialize the memory to zero.
# The codegen proved for us that this is not necessary.
let hdrSize = align(sizeof(RefHeader), alignment)
let s = size + hdrSize
when defined(nimscript):
discard
else:
result = cast[ptr RefHeader](alignedAlloc(s, alignment) +! hdrSize)
head(result).rc = 0
when defined(gcOrc):
head(result).rootIdx = 0
when defined(nimArcDebug):
head(result).refId = gRefId
atomicInc gRefId
if head(result).refId == traceId:
writeStackTrace()
cfprintf(cstderr, "[nimNewObjUninit] %p %ld\n", result, head(result).rc shr rcShift)
when traceCollector:
cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
dec head(p).rc, rcIncrement
proc nimIncRef(p: pointer) {.compilerRtl, inl.} =
when defined(nimArcDebug):
if head(p).refId == traceId:
writeStackTrace()
cfprintf(cstderr, "[IncRef] %p %ld\n", p, head(p).rc shr rcShift)
inc head(p).rc, rcIncrement
when traceCollector:
cprintf("[INCREF] %p\n", head(p))
when not defined(gcOrc) or defined(nimThinout):
proc unsureAsgnRef(dest: ptr pointer, src: pointer) {.inline.} =
# This is only used by the old RTTI mechanism and we know
# that 'dest[]' is nil and needs no destruction. Which is really handy
# as we cannot destroy the object reliably if it's an object of unknown
# compile-time type.
dest[] = src
if src != nil: nimIncRef src
when not defined(nimscript) and defined(nimArcDebug):
proc deallocatedRefId*(p: pointer): int =
## Returns the ref's ID if the ref was already deallocated. This
## is a memory corruption check. Returns 0 if there is no error.
let c = head(p)
if freedCells.data != nil and freedCells.contains(c):
result = c.refId
else:
result = 0
proc nimRawDispose(p: pointer, alignment: int) {.compilerRtl.} =
when not defined(nimscript):
when traceCollector:
cprintf("[Freed] %p\n", p -! sizeof(RefHeader))
when defined(nimOwnedEnabled):
if head(p).rc >= rcIncrement:
cstderr.rawWrite "[FATAL] dangling references exist\n"
rawQuit 1
when defined(nimArcDebug):
# we do NOT really free the memory here in order to reliably detect use-after-frees
if freedCells.data == nil: init(freedCells)
freedCells.incl head(p)
else:
let hdrSize = align(sizeof(RefHeader), alignment)
alignedDealloc(p -! hdrSize, alignment)
template `=dispose`*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x), T.alignOf)
#proc dispose*(x: pointer) = nimRawDispose(x)
proc nimDestroyAndDispose(p: pointer) {.compilerRtl, raises: [].} =
let rti = cast[ptr PNimTypeV2](p)
if rti.destructor != nil:
cast[DestructorProc](rti.destructor)(p)
when false:
cstderr.rawWrite cast[ptr PNimTypeV2](p)[].name
cstderr.rawWrite "\n"
if d == nil:
cstderr.rawWrite "bah, nil\n"
else:
cstderr.rawWrite "has destructor!\n"
nimRawDispose(p, rti.align)
when defined(gcOrc):
when defined(nimThinout):
include cyclebreaker
else:
include orc
#include cyclecollector
proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
if p != nil:
var cell = head(p)
when defined(nimArcDebug):
if cell.refId == traceId:
writeStackTrace()
cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.rc shr rcShift)
if (cell.rc and not rcMask) == 0:
result = true
when traceCollector:
cprintf("[ABOUT TO DESTROY] %p\n", cell)
else:
dec cell.rc, rcIncrement
# According to Lins it's correct to do nothing else here.
when traceCollector:
cprintf("[DeCREF] %p\n", cell)
proc GC_unref*[T](x: ref T) =
## New runtime only supports this operation for 'ref T'.
var y {.cursor.} = x
`=destroy`(y)
proc GC_ref*[T](x: ref T) =
## New runtime only supports this operation for 'ref T'.
if x != nil: nimIncRef(cast[pointer](x))
when not defined(gcOrc):
template GC_fullCollect* =
## Forces a full garbage collection pass. With `--gc:arc` a nop.
discard
template setupForeignThreadGc* =
## With `--gc:arc` a nop.
discard
template tearDownForeignThreadGc* =
## With `--gc:arc` a nop.
discard
proc isObjDisplayCheck(source: PNimTypeV2, targetDepth: int16, token: uint32): bool {.compilerRtl, inline.} =
result = targetDepth <= source.depth and source.display[targetDepth] == token