refs_v2.nim 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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.hasSubstring("|ObjectB|")``. In the actual implementation,
  26. however, we could also use a
  27. hash of ``package & "." & module & "." & name`` to save space.
  28. ]#
  29. const
  30. rcIncrement = 0b1000 # so that lowest 3 bits are not touched
  31. rcMask = 0b111
  32. type
  33. RefHeader = object
  34. rc: int # the object header is now a single RC field.
  35. # we could remove it in non-debug builds for the 'owned ref'
  36. # design but this seems unwise.
  37. Cell = ptr RefHeader
  38. template `+!`(p: pointer, s: int): pointer =
  39. cast[pointer](cast[int](p) +% s)
  40. template `-!`(p: pointer, s: int): pointer =
  41. cast[pointer](cast[int](p) -% s)
  42. template head(p: pointer): Cell =
  43. cast[Cell](cast[int](p) -% sizeof(RefHeader))
  44. const
  45. traceCollector = defined(traceArc)
  46. var allocs*: int
  47. proc nimNewObj(size: int): pointer {.compilerRtl.} =
  48. let s = size + sizeof(RefHeader)
  49. when defined(nimscript):
  50. discard
  51. elif defined(useMalloc):
  52. var orig = c_malloc(cuint s)
  53. nimZeroMem(orig, s)
  54. result = orig +! sizeof(RefHeader)
  55. elif compileOption("threads"):
  56. result = allocShared0(s) +! sizeof(RefHeader)
  57. else:
  58. result = alloc0(s) +! sizeof(RefHeader)
  59. when hasThreadSupport:
  60. atomicInc allocs
  61. else:
  62. inc allocs
  63. when traceCollector:
  64. cprintf("[Allocated] %p\n", result -! sizeof(RefHeader))
  65. proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
  66. dec head(p).rc, rcIncrement
  67. proc nimIncRef(p: pointer) {.compilerRtl, inl.} =
  68. inc head(p).rc, rcIncrement
  69. #cprintf("[INCREF] %p\n", p)
  70. proc nimRawDispose(p: pointer) {.compilerRtl.} =
  71. when not defined(nimscript):
  72. when traceCollector:
  73. cprintf("[Freed] %p\n", p -! sizeof(RefHeader))
  74. when defined(nimOwnedEnabled):
  75. if head(p).rc >= rcIncrement:
  76. cstderr.rawWrite "[FATAL] dangling references exist\n"
  77. quit 1
  78. when defined(useMalloc):
  79. c_free(p -! sizeof(RefHeader))
  80. elif compileOption("threads"):
  81. deallocShared(p -! sizeof(RefHeader))
  82. else:
  83. dealloc(p -! sizeof(RefHeader))
  84. if allocs > 0:
  85. when hasThreadSupport:
  86. discard atomicDec(allocs)
  87. else:
  88. dec allocs
  89. else:
  90. cstderr.rawWrite "[FATAL] unpaired dealloc\n"
  91. quit 1
  92. template dispose*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x))
  93. #proc dispose*(x: pointer) = nimRawDispose(x)
  94. proc nimDestroyAndDispose(p: pointer) {.compilerRtl, raises: [].} =
  95. let d = cast[ptr PNimType](p)[].destructor
  96. if d != nil: cast[DestructorProc](d)(p)
  97. when false:
  98. cstderr.rawWrite cast[ptr PNimType](p)[].name
  99. cstderr.rawWrite "\n"
  100. if d == nil:
  101. cstderr.rawWrite "bah, nil\n"
  102. else:
  103. cstderr.rawWrite "has destructor!\n"
  104. nimRawDispose(p)
  105. when defined(gcOrc):
  106. include cyclicrefs_v2
  107. proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
  108. if p != nil:
  109. var cell = head(p)
  110. if (cell.rc and not rcMask) == 0:
  111. result = true
  112. #cprintf("[DESTROY] %p\n", p)
  113. else:
  114. dec cell.rc, rcIncrement
  115. # According to Lins it's correct to do nothing else here.
  116. #cprintf("[DeCREF] %p\n", p)
  117. proc GC_unref*[T](x: ref T) =
  118. ## New runtime only supports this operation for 'ref T'.
  119. if nimDecRefIsLast(cast[pointer](x)):
  120. # XXX this does NOT work for virtual destructors!
  121. `=destroy`(x[])
  122. nimRawDispose(cast[pointer](x))
  123. proc GC_ref*[T](x: ref T) =
  124. ## New runtime only supports this operation for 'ref T'.
  125. if x != nil: nimIncRef(cast[pointer](x))
  126. template GC_fullCollect* =
  127. ## Forces a full garbage collection pass. With ``--gc:arc`` a nop.
  128. discard
  129. template setupForeignThreadGc* =
  130. ## With ``--gc:arc`` a nop.
  131. discard
  132. template tearDownForeignThreadGc* =
  133. ## With ``--gc:arc`` a nop.
  134. discard
  135. proc isObj(obj: PNimType, subclass: cstring): bool {.compilerRtl, inl.} =
  136. proc strstr(s, sub: cstring): cstring {.header: "<string.h>", importc.}
  137. result = strstr(obj.name, subclass) != nil
  138. proc chckObj(obj: PNimType, subclass: cstring) {.compilerRtl.} =
  139. # checks if obj is of type subclass:
  140. if not isObj(obj, subclass): sysFatal(ObjectConversionError, "invalid object conversion")