arc.nim 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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. Object subtyping is checked via the generated 'name'. This should have
  15. comparable overhead to the old pointer chasing approach but has the benefit
  16. that it works across DLL boundaries.
  17. The generated name is a concatenation of the object names in the hierarchy
  18. so that a subtype check becomes a substring check. For example::
  19. type
  20. ObjectA = object of RootObj
  21. ObjectB = object of ObjectA
  22. ObjectA's ``name`` is "|ObjectA|RootObj|".
  23. ObjectB's ``name`` is "|ObjectB|ObjectA|RootObj|".
  24. Now to check for ``x of ObjectB`` we need to check
  25. for ``x.typ.name.endsWith("|ObjectB|ObjectA|RootObj|")``.
  26. In the actual implementation, however, we could also use a
  27. hash of ``package & "." & module & "." & name`` to save space.
  28. ]#
  29. when defined(gcOrc):
  30. const
  31. rcIncrement = 0b10000 # so that lowest 4 bits are not touched
  32. rcMask = 0b1111
  33. rcShift = 4 # shift by rcShift to get the reference counter
  34. else:
  35. const
  36. rcIncrement = 0b1000 # so that lowest 3 bits are not touched
  37. rcMask = 0b111
  38. rcShift = 3 # shift by rcShift to get the reference counter
  39. type
  40. RefHeader = object
  41. rc: int # the object header is now a single RC field.
  42. # we could remove it in non-debug builds for the 'owned ref'
  43. # design but this seems unwise.
  44. when defined(gcOrc):
  45. rootIdx: int # thanks to this we can delete potential cycle roots
  46. # in O(1) without doubly linked lists
  47. when defined(nimArcDebug) or defined(nimArcIds):
  48. refId: int
  49. Cell = ptr RefHeader
  50. template head(p: pointer): Cell =
  51. cast[Cell](cast[int](p) -% sizeof(RefHeader))
  52. const
  53. traceCollector = defined(traceArc)
  54. when defined(nimArcDebug):
  55. include cellsets
  56. const traceId = 20 # 1037
  57. var gRefId: int
  58. var freedCells: CellSet
  59. elif defined(nimArcIds):
  60. var gRefId: int
  61. const traceId = -1
  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).rc shr rcShift)
  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).rc shr rcShift)
  95. when traceCollector:
  96. cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
  97. proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
  98. dec head(p).rc, rcIncrement
  99. proc nimIncRef(p: pointer) {.compilerRtl, inl.} =
  100. when defined(nimArcDebug):
  101. if head(p).refId == traceId:
  102. writeStackTrace()
  103. cfprintf(cstderr, "[IncRef] %p %ld\n", p, head(p).rc shr rcShift)
  104. inc head(p).rc, rcIncrement
  105. when traceCollector:
  106. cprintf("[INCREF] %p\n", head(p))
  107. when not defined(gcOrc) or defined(nimThinout):
  108. proc unsureAsgnRef(dest: ptr pointer, src: pointer) {.inline.} =
  109. # This is only used by the old RTTI mechanism and we know
  110. # that 'dest[]' is nil and needs no destruction. Which is really handy
  111. # as we cannot destroy the object reliably if it's an object of unknown
  112. # compile-time type.
  113. dest[] = src
  114. if src != nil: nimIncRef src
  115. when not defined(nimscript) and defined(nimArcDebug):
  116. proc deallocatedRefId*(p: pointer): int =
  117. ## Returns the ref's ID if the ref was already deallocated. This
  118. ## is a memory corruption check. Returns 0 if there is no error.
  119. let c = head(p)
  120. if freedCells.data != nil and freedCells.contains(c):
  121. result = c.refId
  122. else:
  123. result = 0
  124. proc nimRawDispose(p: pointer, alignment: int) {.compilerRtl.} =
  125. when not defined(nimscript):
  126. when traceCollector:
  127. cprintf("[Freed] %p\n", p -! sizeof(RefHeader))
  128. when defined(nimOwnedEnabled):
  129. if head(p).rc >= rcIncrement:
  130. cstderr.rawWrite "[FATAL] dangling references exist\n"
  131. quit 1
  132. when defined(nimArcDebug):
  133. # we do NOT really free the memory here in order to reliably detect use-after-frees
  134. if freedCells.data == nil: init(freedCells)
  135. freedCells.incl head(p)
  136. else:
  137. let hdrSize = align(sizeof(RefHeader), alignment)
  138. alignedDealloc(p -! hdrSize, alignment)
  139. template `=dispose`*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x), T.alignOf)
  140. #proc dispose*(x: pointer) = nimRawDispose(x)
  141. proc nimDestroyAndDispose(p: pointer) {.compilerRtl, raises: [].} =
  142. let rti = cast[ptr PNimTypeV2](p)
  143. if rti.destructor != nil:
  144. cast[DestructorProc](rti.destructor)(p)
  145. when false:
  146. cstderr.rawWrite cast[ptr PNimTypeV2](p)[].name
  147. cstderr.rawWrite "\n"
  148. if d == nil:
  149. cstderr.rawWrite "bah, nil\n"
  150. else:
  151. cstderr.rawWrite "has destructor!\n"
  152. nimRawDispose(p, rti.align)
  153. when defined(gcOrc):
  154. when defined(nimThinout):
  155. include cyclebreaker
  156. else:
  157. include orc
  158. #include cyclecollector
  159. proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
  160. if p != nil:
  161. var cell = head(p)
  162. when defined(nimArcDebug):
  163. if cell.refId == traceId:
  164. writeStackTrace()
  165. cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.rc shr rcShift)
  166. if (cell.rc and not rcMask) == 0:
  167. result = true
  168. when traceCollector:
  169. cprintf("[ABOUT TO DESTROY] %p\n", cell)
  170. else:
  171. dec cell.rc, rcIncrement
  172. # According to Lins it's correct to do nothing else here.
  173. when traceCollector:
  174. cprintf("[DeCREF] %p\n", cell)
  175. proc GC_unref*[T](x: ref T) =
  176. ## New runtime only supports this operation for 'ref T'.
  177. var y {.cursor.} = x
  178. `=destroy`(y)
  179. proc GC_ref*[T](x: ref T) =
  180. ## New runtime only supports this operation for 'ref T'.
  181. if x != nil: nimIncRef(cast[pointer](x))
  182. when not defined(gcOrc):
  183. template GC_fullCollect* =
  184. ## Forces a full garbage collection pass. With `--gc:arc` a nop.
  185. discard
  186. template setupForeignThreadGc* =
  187. ## With `--gc:arc` a nop.
  188. discard
  189. template tearDownForeignThreadGc* =
  190. ## With `--gc:arc` a nop.
  191. discard
  192. type ObjCheckCache = array[0..1, PNimTypeV2]
  193. proc memcmp(str1, str2: cstring, n: csize_t): cint {.importc, header: "<string.h>".}
  194. func endsWith(s, suffix: cstring): bool {.inline.} =
  195. let
  196. sLen = s.len
  197. suffixLen = suffix.len
  198. if suffixLen <= sLen:
  199. result = memcmp(cstring(addr s[sLen - suffixLen]), suffix, csize_t(suffixLen)) == 0
  200. proc isObj(obj: PNimTypeV2, subclass: cstring): bool {.compilerRtl, inl.} =
  201. result = endsWith(obj.name, subclass)
  202. proc isObjSlowPath(obj: PNimTypeV2, subclass: cstring, cache: var ObjCheckCache): bool {.compilerRtl, inline.} =
  203. if endsWith(obj.name, subclass):
  204. cache[1] = obj
  205. result = true
  206. else:
  207. cache[0] = obj
  208. result = false
  209. proc isObjWithCache(obj: PNimTypeV2, subclass: cstring, cache: var ObjCheckCache): bool {.compilerRtl.} =
  210. if cache[0] == obj: result = false
  211. elif cache[1] == obj: result = true
  212. else:
  213. result = isObjSlowPath(obj, subclass, cache)
  214. proc chckObj(obj: PNimTypeV2, subclass: cstring) {.compilerRtl.} =
  215. # checks if obj is of type subclass:
  216. if not isObj(obj, subclass): sysFatal(ObjectConversionDefect, "invalid object conversion")