arc.nim 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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. proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} =
  49. let hdrSize = align(sizeof(RefHeader), alignment)
  50. let s = size + hdrSize
  51. when defined(nimscript):
  52. discard
  53. else:
  54. result = alignedAlloc0(s, alignment) +! hdrSize
  55. when defined(nimArcDebug) or defined(nimArcIds):
  56. head(result).refId = gRefId
  57. atomicInc gRefId
  58. if head(result).refId == traceId:
  59. writeStackTrace()
  60. cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).rc shr rcShift)
  61. when traceCollector:
  62. cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
  63. proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} =
  64. # Same as 'newNewObj' but do not initialize the memory to zero.
  65. # The codegen proved for us that this is not necessary.
  66. let hdrSize = align(sizeof(RefHeader), alignment)
  67. let s = size + hdrSize
  68. when defined(nimscript):
  69. discard
  70. else:
  71. result = cast[ptr RefHeader](alignedAlloc(s, alignment) +! hdrSize)
  72. head(result).rc = 0
  73. when defined(gcOrc):
  74. head(result).rootIdx = 0
  75. when defined(nimArcDebug):
  76. head(result).refId = gRefId
  77. atomicInc gRefId
  78. if head(result).refId == traceId:
  79. writeStackTrace()
  80. cfprintf(cstderr, "[nimNewObjUninit] %p %ld\n", result, head(result).rc shr rcShift)
  81. when traceCollector:
  82. cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
  83. proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
  84. dec head(p).rc, rcIncrement
  85. proc nimIncRef(p: pointer) {.compilerRtl, inl.} =
  86. when defined(nimArcDebug):
  87. if head(p).refId == traceId:
  88. writeStackTrace()
  89. cfprintf(cstderr, "[IncRef] %p %ld\n", p, head(p).rc shr rcShift)
  90. inc head(p).rc, rcIncrement
  91. when traceCollector:
  92. cprintf("[INCREF] %p\n", head(p))
  93. when not defined(gcOrc) or defined(nimThinout):
  94. proc unsureAsgnRef(dest: ptr pointer, src: pointer) {.inline.} =
  95. # This is only used by the old RTTI mechanism and we know
  96. # that 'dest[]' is nil and needs no destruction. Which is really handy
  97. # as we cannot destroy the object reliably if it's an object of unknown
  98. # compile-time type.
  99. dest[] = src
  100. if src != nil: nimIncRef src
  101. when not defined(nimscript) and defined(nimArcDebug):
  102. proc deallocatedRefId*(p: pointer): int =
  103. ## Returns the ref's ID if the ref was already deallocated. This
  104. ## is a memory corruption check. Returns 0 if there is no error.
  105. let c = head(p)
  106. if freedCells.data != nil and freedCells.contains(c):
  107. result = c.refId
  108. else:
  109. result = 0
  110. proc nimRawDispose(p: pointer, alignment: int) {.compilerRtl.} =
  111. when not defined(nimscript):
  112. when traceCollector:
  113. cprintf("[Freed] %p\n", p -! sizeof(RefHeader))
  114. when defined(nimOwnedEnabled):
  115. if head(p).rc >= rcIncrement:
  116. cstderr.rawWrite "[FATAL] dangling references exist\n"
  117. rawQuit 1
  118. when defined(nimArcDebug):
  119. # we do NOT really free the memory here in order to reliably detect use-after-frees
  120. if freedCells.data == nil: init(freedCells)
  121. freedCells.incl head(p)
  122. else:
  123. let hdrSize = align(sizeof(RefHeader), alignment)
  124. alignedDealloc(p -! hdrSize, alignment)
  125. template `=dispose`*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x), T.alignOf)
  126. #proc dispose*(x: pointer) = nimRawDispose(x)
  127. proc nimDestroyAndDispose(p: pointer) {.compilerRtl, raises: [].} =
  128. let rti = cast[ptr PNimTypeV2](p)
  129. if rti.destructor != nil:
  130. cast[DestructorProc](rti.destructor)(p)
  131. when false:
  132. cstderr.rawWrite cast[ptr PNimTypeV2](p)[].name
  133. cstderr.rawWrite "\n"
  134. if d == nil:
  135. cstderr.rawWrite "bah, nil\n"
  136. else:
  137. cstderr.rawWrite "has destructor!\n"
  138. nimRawDispose(p, rti.align)
  139. when defined(gcOrc):
  140. when defined(nimThinout):
  141. include cyclebreaker
  142. else:
  143. include orc
  144. #include cyclecollector
  145. proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
  146. if p != nil:
  147. var cell = head(p)
  148. when defined(nimArcDebug):
  149. if cell.refId == traceId:
  150. writeStackTrace()
  151. cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.rc shr rcShift)
  152. if (cell.rc and not rcMask) == 0:
  153. result = true
  154. when traceCollector:
  155. cprintf("[ABOUT TO DESTROY] %p\n", cell)
  156. else:
  157. dec cell.rc, rcIncrement
  158. # According to Lins it's correct to do nothing else here.
  159. when traceCollector:
  160. cprintf("[DeCREF] %p\n", cell)
  161. proc GC_unref*[T](x: ref T) =
  162. ## New runtime only supports this operation for 'ref T'.
  163. var y {.cursor.} = x
  164. `=destroy`(y)
  165. proc GC_ref*[T](x: ref T) =
  166. ## New runtime only supports this operation for 'ref T'.
  167. if x != nil: nimIncRef(cast[pointer](x))
  168. when not defined(gcOrc):
  169. template GC_fullCollect* =
  170. ## Forces a full garbage collection pass. With `--gc:arc` a nop.
  171. discard
  172. template setupForeignThreadGc* =
  173. ## With `--gc:arc` a nop.
  174. discard
  175. template tearDownForeignThreadGc* =
  176. ## With `--gc:arc` a nop.
  177. discard
  178. proc isObjDisplayCheck(source: PNimTypeV2, targetDepth: int16, token: uint32): bool {.compilerRtl, inline.} =
  179. result = targetDepth <= source.depth and source.display[targetDepth] == token