arc.nim 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2019 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. #[
  10. In this new runtime we simplify the object layouts a bit: The runtime type
  11. information is only accessed for the objects that have it and it's always
  12. at offset 0 then. The ``ref`` object header is independent from the
  13. runtime type and only contains a reference count.
  14. ]#
  15. when defined(gcOrc):
  16. const
  17. rcIncrement = 0b10000 # so that lowest 4 bits are not touched
  18. rcMask = 0b1111
  19. rcShift = 4 # shift by rcShift to get the reference counter
  20. else:
  21. const
  22. rcIncrement = 0b1000 # so that lowest 3 bits are not touched
  23. rcMask = 0b111
  24. rcShift = 3 # shift by rcShift to get the reference counter
  25. const
  26. orcLeakDetector = defined(nimOrcLeakDetector)
  27. type
  28. RefHeader = object
  29. rc: int # the object header is now a single RC field.
  30. # we could remove it in non-debug builds for the 'owned ref'
  31. # design but this seems unwise.
  32. when defined(gcOrc):
  33. rootIdx: int # thanks to this we can delete potential cycle roots
  34. # in O(1) without doubly linked lists
  35. when defined(nimArcDebug) or defined(nimArcIds):
  36. refId: int
  37. when defined(gcOrc) and orcLeakDetector:
  38. filename: cstring
  39. line: int
  40. Cell = ptr RefHeader
  41. template setFrameInfo(c: Cell) =
  42. when orcLeakDetector:
  43. if framePtr != nil and framePtr.prev != nil:
  44. c.filename = framePtr.prev.filename
  45. c.line = framePtr.prev.line
  46. else:
  47. c.filename = nil
  48. c.line = 0
  49. template head(p: pointer): Cell =
  50. cast[Cell](cast[int](p) -% sizeof(RefHeader))
  51. const
  52. traceCollector = defined(traceArc)
  53. when defined(nimArcDebug):
  54. include cellsets
  55. const traceId = 20 # 1037
  56. var gRefId: int
  57. var freedCells: CellSet
  58. elif defined(nimArcIds):
  59. var gRefId: int
  60. const traceId = -1
  61. when defined(gcAtomicArc) and hasThreadSupport:
  62. template decrement(cell: Cell): untyped =
  63. discard atomicDec(cell.rc, rcIncrement)
  64. template increment(cell: Cell): untyped =
  65. discard atomicInc(cell.rc, rcIncrement)
  66. template count(x: Cell): untyped =
  67. atomicLoadN(x.rc.addr, ATOMIC_ACQUIRE) shr rcShift
  68. else:
  69. template decrement(cell: Cell): untyped =
  70. dec(cell.rc, rcIncrement)
  71. template increment(cell: Cell): untyped =
  72. inc(cell.rc, rcIncrement)
  73. template count(x: Cell): untyped =
  74. x.rc shr rcShift
  75. when not defined(nimHasQuirky):
  76. {.pragma: quirky.}
  77. proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} =
  78. let hdrSize = align(sizeof(RefHeader), alignment)
  79. let s = size + hdrSize
  80. when defined(nimscript):
  81. discard
  82. else:
  83. result = alignedAlloc0(s, alignment) +! hdrSize
  84. when defined(nimArcDebug) or defined(nimArcIds):
  85. head(result).refId = gRefId
  86. atomicInc gRefId
  87. if head(result).refId == traceId:
  88. writeStackTrace()
  89. cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).count)
  90. when traceCollector:
  91. cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
  92. setFrameInfo head(result)
  93. proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} =
  94. # Same as 'newNewObj' but do not initialize the memory to zero.
  95. # The codegen proved for us that this is not necessary.
  96. let hdrSize = align(sizeof(RefHeader), alignment)
  97. let s = size + hdrSize
  98. when defined(nimscript):
  99. discard
  100. else:
  101. result = cast[ptr RefHeader](alignedAlloc(s, alignment) +! hdrSize)
  102. head(result).rc = 0
  103. when defined(gcOrc):
  104. head(result).rootIdx = 0
  105. when defined(nimArcDebug):
  106. head(result).refId = gRefId
  107. atomicInc gRefId
  108. if head(result).refId == traceId:
  109. writeStackTrace()
  110. cfprintf(cstderr, "[nimNewObjUninit] %p %ld\n", result, head(result).count)
  111. when traceCollector:
  112. cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
  113. setFrameInfo head(result)
  114. proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
  115. decrement head(p)
  116. proc isUniqueRef*[T](x: ref T): bool {.inline.} =
  117. ## Returns true if the object `x` points to is uniquely referenced. Such
  118. ## an object can potentially be passed over to a different thread safely,
  119. ## if great care is taken. This queries the internal reference count of
  120. ## the object which is subject to lots of optimizations! In other words
  121. ## the value of `isUniqueRef` can depend on the used compiler version and
  122. ## optimizer setting.
  123. ## Nevertheless it can be used as a very valuable debugging tool and can
  124. ## be used to specify the constraints of a threading related API
  125. ## via `assert isUniqueRef(x)`.
  126. head(cast[pointer](x)).rc == 0
  127. proc nimIncRef(p: pointer) {.compilerRtl, inl.} =
  128. when defined(nimArcDebug):
  129. if head(p).refId == traceId:
  130. writeStackTrace()
  131. cfprintf(cstderr, "[IncRef] %p %ld\n", p, head(p).count)
  132. increment head(p)
  133. when traceCollector:
  134. cprintf("[INCREF] %p\n", head(p))
  135. when not defined(gcOrc) or defined(nimThinout):
  136. proc unsureAsgnRef(dest: ptr pointer, src: pointer) {.inline.} =
  137. # This is only used by the old RTTI mechanism and we know
  138. # that 'dest[]' is nil and needs no destruction. Which is really handy
  139. # as we cannot destroy the object reliably if it's an object of unknown
  140. # compile-time type.
  141. dest[] = src
  142. if src != nil: nimIncRef src
  143. when not defined(nimscript) and defined(nimArcDebug):
  144. proc deallocatedRefId*(p: pointer): int =
  145. ## Returns the ref's ID if the ref was already deallocated. This
  146. ## is a memory corruption check. Returns 0 if there is no error.
  147. let c = head(p)
  148. if freedCells.data != nil and freedCells.contains(c):
  149. result = c.refId
  150. else:
  151. result = 0
  152. proc nimRawDispose(p: pointer, alignment: int) {.compilerRtl.} =
  153. when not defined(nimscript):
  154. when traceCollector:
  155. cprintf("[Freed] %p\n", p -! sizeof(RefHeader))
  156. when defined(nimOwnedEnabled):
  157. if head(p).rc >= rcIncrement:
  158. cstderr.rawWrite "[FATAL] dangling references exist\n"
  159. rawQuit 1
  160. when defined(nimArcDebug):
  161. # we do NOT really free the memory here in order to reliably detect use-after-frees
  162. if freedCells.data == nil: init(freedCells)
  163. freedCells.incl head(p)
  164. else:
  165. let hdrSize = align(sizeof(RefHeader), alignment)
  166. alignedDealloc(p -! hdrSize, alignment)
  167. template `=dispose`*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x), T.alignOf)
  168. #proc dispose*(x: pointer) = nimRawDispose(x)
  169. proc nimDestroyAndDispose(p: pointer) {.compilerRtl, quirky, raises: [].} =
  170. let rti = cast[ptr PNimTypeV2](p)
  171. if rti.destructor != nil:
  172. cast[DestructorProc](rti.destructor)(p)
  173. when false:
  174. cstderr.rawWrite cast[ptr PNimTypeV2](p)[].name
  175. cstderr.rawWrite "\n"
  176. if d == nil:
  177. cstderr.rawWrite "bah, nil\n"
  178. else:
  179. cstderr.rawWrite "has destructor!\n"
  180. nimRawDispose(p, rti.align)
  181. when defined(gcOrc):
  182. when defined(nimThinout):
  183. include cyclebreaker
  184. else:
  185. include orc
  186. #include cyclecollector
  187. proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
  188. result = false
  189. if p != nil:
  190. var cell = head(p)
  191. when defined(nimArcDebug):
  192. if cell.refId == traceId:
  193. writeStackTrace()
  194. cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.count)
  195. when defined(gcAtomicArc) and hasThreadSupport:
  196. # `atomicDec` returns the new value
  197. if atomicDec(cell.rc, rcIncrement) == -rcIncrement:
  198. result = true
  199. when traceCollector:
  200. cprintf("[ABOUT TO DESTROY] %p\n", cell)
  201. else:
  202. if cell.count == 0:
  203. result = true
  204. when traceCollector:
  205. cprintf("[ABOUT TO DESTROY] %p\n", cell)
  206. else:
  207. decrement cell
  208. # According to Lins it's correct to do nothing else here.
  209. when traceCollector:
  210. cprintf("[DECREF] %p\n", cell)
  211. proc GC_unref*[T](x: ref T) =
  212. ## New runtime only supports this operation for 'ref T'.
  213. var y {.cursor.} = x
  214. `=destroy`(y)
  215. proc GC_ref*[T](x: ref T) =
  216. ## New runtime only supports this operation for 'ref T'.
  217. if x != nil: nimIncRef(cast[pointer](x))
  218. when not defined(gcOrc):
  219. template GC_fullCollect* =
  220. ## Forces a full garbage collection pass. With `--mm:arc` a nop.
  221. discard
  222. template setupForeignThreadGc* =
  223. ## With `--mm:arc` a nop.
  224. discard
  225. template tearDownForeignThreadGc* =
  226. ## With `--mm:arc` a nop.
  227. discard
  228. proc isObjDisplayCheck(source: PNimTypeV2, targetDepth: int16, token: uint32): bool {.compilerRtl, inl.} =
  229. result = targetDepth <= source.depth and source.display[targetDepth] == token
  230. when defined(gcDestructors):
  231. proc nimGetVTable(p: pointer, index: int): pointer
  232. {.compilerRtl, inline, raises: [].} =
  233. result = cast[ptr PNimTypeV2](p).vTable[index]