arc.nim 7.7 KB

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