arc.nim 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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. proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} =
  76. let hdrSize = align(sizeof(RefHeader), alignment)
  77. let s = size + hdrSize
  78. when defined(nimscript):
  79. discard
  80. else:
  81. result = alignedAlloc0(s, alignment) +! hdrSize
  82. when defined(nimArcDebug) or defined(nimArcIds):
  83. head(result).refId = gRefId
  84. atomicInc gRefId
  85. if head(result).refId == traceId:
  86. writeStackTrace()
  87. cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).count)
  88. when traceCollector:
  89. cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
  90. setFrameInfo head(result)
  91. proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} =
  92. # Same as 'newNewObj' but do not initialize the memory to zero.
  93. # The codegen proved for us that this is not necessary.
  94. let hdrSize = align(sizeof(RefHeader), alignment)
  95. let s = size + hdrSize
  96. when defined(nimscript):
  97. discard
  98. else:
  99. result = cast[ptr RefHeader](alignedAlloc(s, alignment) +! hdrSize)
  100. head(result).rc = 0
  101. when defined(gcOrc):
  102. head(result).rootIdx = 0
  103. when defined(nimArcDebug):
  104. head(result).refId = gRefId
  105. atomicInc gRefId
  106. if head(result).refId == traceId:
  107. writeStackTrace()
  108. cfprintf(cstderr, "[nimNewObjUninit] %p %ld\n", result, head(result).count)
  109. when traceCollector:
  110. cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
  111. setFrameInfo head(result)
  112. proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
  113. decrement head(p)
  114. proc isUniqueRef*[T](x: ref T): bool {.inline.} =
  115. ## Returns true if the object `x` points to is uniquely referenced. Such
  116. ## an object can potentially be passed over to a different thread safely,
  117. ## if great care is taken. This queries the internal reference count of
  118. ## the object which is subject to lots of optimizations! In other words
  119. ## the value of `isUniqueRef` can depend on the used compiler version and
  120. ## optimizer setting.
  121. ## Nevertheless it can be used as a very valuable debugging tool and can
  122. ## be used to specify the constraints of a threading related API
  123. ## via `assert isUniqueRef(x)`.
  124. head(cast[pointer](x)).rc == 0
  125. proc nimIncRef(p: pointer) {.compilerRtl, inl.} =
  126. when defined(nimArcDebug):
  127. if head(p).refId == traceId:
  128. writeStackTrace()
  129. cfprintf(cstderr, "[IncRef] %p %ld\n", p, head(p).count)
  130. increment head(p)
  131. when traceCollector:
  132. cprintf("[INCREF] %p\n", head(p))
  133. when not defined(gcOrc) or defined(nimThinout):
  134. proc unsureAsgnRef(dest: ptr pointer, src: pointer) {.inline.} =
  135. # This is only used by the old RTTI mechanism and we know
  136. # that 'dest[]' is nil and needs no destruction. Which is really handy
  137. # as we cannot destroy the object reliably if it's an object of unknown
  138. # compile-time type.
  139. dest[] = src
  140. if src != nil: nimIncRef src
  141. when not defined(nimscript) and defined(nimArcDebug):
  142. proc deallocatedRefId*(p: pointer): int =
  143. ## Returns the ref's ID if the ref was already deallocated. This
  144. ## is a memory corruption check. Returns 0 if there is no error.
  145. let c = head(p)
  146. if freedCells.data != nil and freedCells.contains(c):
  147. result = c.refId
  148. else:
  149. result = 0
  150. proc nimRawDispose(p: pointer, alignment: int) {.compilerRtl.} =
  151. when not defined(nimscript):
  152. when traceCollector:
  153. cprintf("[Freed] %p\n", p -! sizeof(RefHeader))
  154. when defined(nimOwnedEnabled):
  155. if head(p).rc >= rcIncrement:
  156. cstderr.rawWrite "[FATAL] dangling references exist\n"
  157. rawQuit 1
  158. when defined(nimArcDebug):
  159. # we do NOT really free the memory here in order to reliably detect use-after-frees
  160. if freedCells.data == nil: init(freedCells)
  161. freedCells.incl head(p)
  162. else:
  163. let hdrSize = align(sizeof(RefHeader), alignment)
  164. alignedDealloc(p -! hdrSize, alignment)
  165. template `=dispose`*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x), T.alignOf)
  166. #proc dispose*(x: pointer) = nimRawDispose(x)
  167. proc nimDestroyAndDispose(p: pointer) {.compilerRtl, raises: [].} =
  168. let rti = cast[ptr PNimTypeV2](p)
  169. if rti.destructor != nil:
  170. cast[DestructorProc](rti.destructor)(p)
  171. when false:
  172. cstderr.rawWrite cast[ptr PNimTypeV2](p)[].name
  173. cstderr.rawWrite "\n"
  174. if d == nil:
  175. cstderr.rawWrite "bah, nil\n"
  176. else:
  177. cstderr.rawWrite "has destructor!\n"
  178. nimRawDispose(p, rti.align)
  179. when defined(gcOrc):
  180. when defined(nimThinout):
  181. include cyclebreaker
  182. else:
  183. include orc
  184. #include cyclecollector
  185. proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
  186. if p != nil:
  187. var cell = head(p)
  188. when defined(nimArcDebug):
  189. if cell.refId == traceId:
  190. writeStackTrace()
  191. cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.count)
  192. when defined(gcAtomicArc) and hasThreadSupport:
  193. # `atomicDec` returns the new value
  194. if atomicDec(cell.rc, rcIncrement) == -rcIncrement:
  195. result = true
  196. when traceCollector:
  197. cprintf("[ABOUT TO DESTROY] %p\n", cell)
  198. else:
  199. if cell.count == 0:
  200. result = true
  201. when traceCollector:
  202. cprintf("[ABOUT TO DESTROY] %p\n", cell)
  203. else:
  204. decrement cell
  205. # According to Lins it's correct to do nothing else here.
  206. when traceCollector:
  207. cprintf("[DECREF] %p\n", cell)
  208. proc GC_unref*[T](x: ref T) =
  209. ## New runtime only supports this operation for 'ref T'.
  210. var y {.cursor.} = x
  211. `=destroy`(y)
  212. proc GC_ref*[T](x: ref T) =
  213. ## New runtime only supports this operation for 'ref T'.
  214. if x != nil: nimIncRef(cast[pointer](x))
  215. when not defined(gcOrc):
  216. template GC_fullCollect* =
  217. ## Forces a full garbage collection pass. With `--mm:arc` a nop.
  218. discard
  219. template setupForeignThreadGc* =
  220. ## With `--mm:arc` a nop.
  221. discard
  222. template tearDownForeignThreadGc* =
  223. ## With `--mm:arc` a nop.
  224. discard
  225. proc isObjDisplayCheck(source: PNimTypeV2, targetDepth: int16, token: uint32): bool {.compilerRtl, inl.} =
  226. result = targetDepth <= source.depth and source.display[targetDepth] == token
  227. when defined(gcDestructors):
  228. proc nimGetVTable(p: pointer, index: int): pointer
  229. {.compilerRtl, inline, raises: [].} =
  230. result = cast[ptr PNimTypeV2](p).vTable[index]