gc_common.nim 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2015 Rokas Kupstys
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. type
  10. ForeignCell* = object
  11. data*: pointer
  12. owner: ptr GcHeap
  13. proc protect*(x: pointer): ForeignCell =
  14. nimGCref(x)
  15. result.data = x
  16. result.owner = addr(gch)
  17. when defined(nimTypeNames):
  18. proc dumpNumberOfInstances* =
  19. var it = nimTypeRoot
  20. while it != nil:
  21. if it.instances > 0:
  22. c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", it.name, it.instances, it.sizes)
  23. it = it.nextType
  24. when defined(nimGcRefLeak):
  25. proc oomhandler() =
  26. c_fprintf(stdout, "[Heap] ROOTS: #%ld\n", gch.additionalRoots.len)
  27. writeLeaks()
  28. outOfMemHook = oomhandler
  29. template decTypeSize(cell, t) =
  30. # XXX this needs to use atomics for multithreaded apps!
  31. when defined(nimTypeNames):
  32. if t.kind in {tyString, tySequence}:
  33. let len = cast[PGenericSeq](cellToUsr(cell)).len
  34. let base = if t.kind == tyString: 1 else: t.base.size
  35. let size = addInt(mulInt(len, base), GenericSeqSize)
  36. dec t.sizes, size+sizeof(Cell)
  37. else:
  38. dec t.sizes, t.size+sizeof(Cell)
  39. dec t.instances
  40. template incTypeSize(typ, size) =
  41. when defined(nimTypeNames):
  42. inc typ.instances
  43. inc typ.sizes, size+sizeof(Cell)
  44. proc dispose*(x: ForeignCell) =
  45. when hasThreadSupport:
  46. # if we own it we can free it directly:
  47. if x.owner == addr(gch):
  48. nimGCunref(x.data)
  49. else:
  50. x.owner.toDispose.add(x.data)
  51. else:
  52. nimGCunref(x.data)
  53. proc isNotForeign*(x: ForeignCell): bool =
  54. ## returns true if 'x' belongs to the calling thread.
  55. ## No deep copy has to be performed then.
  56. x.owner == addr(gch)
  57. when nimCoroutines:
  58. iterator items(first: var GcStack): ptr GcStack =
  59. var item = addr(first)
  60. while true:
  61. yield item
  62. item = item.next
  63. if item == addr(first):
  64. break
  65. proc append(first: var GcStack, stack: ptr GcStack) =
  66. ## Append stack to the ring of stacks.
  67. first.prev.next = stack
  68. stack.prev = first.prev
  69. first.prev = stack
  70. stack.next = addr(first)
  71. proc append(first: var GcStack): ptr GcStack =
  72. ## Allocate new GcStack object, append it to the ring of stacks and return it.
  73. result = cast[ptr GcStack](alloc0(sizeof(GcStack)))
  74. first.append(result)
  75. proc remove(first: var GcStack, stack: ptr GcStack) =
  76. ## Remove stack from ring of stacks.
  77. gcAssert(addr(first) != stack, "Main application stack can not be removed")
  78. if addr(first) == stack or stack == nil:
  79. return
  80. stack.prev.next = stack.next
  81. stack.next.prev = stack.prev
  82. dealloc(stack)
  83. proc remove(stack: ptr GcStack) =
  84. gch.stack.remove(stack)
  85. proc find(first: var GcStack, bottom: pointer): ptr GcStack =
  86. ## Find stack struct based on bottom pointer. If `bottom` is nil then main
  87. ## thread stack is is returned.
  88. if bottom == nil:
  89. return addr(gch.stack)
  90. for stack in first.items():
  91. if stack.bottom == bottom:
  92. return stack
  93. proc len(stack: var GcStack): int =
  94. for _ in stack.items():
  95. result = result + 1
  96. else:
  97. # This iterator gets optimized out in forEachStackSlot().
  98. iterator items(first: var GcStack): ptr GcStack = yield addr(first)
  99. proc len(stack: var GcStack): int = 1
  100. proc stackSize(stack: ptr GcStack): int {.noinline.} =
  101. when nimCoroutines:
  102. var pos = stack.pos
  103. else:
  104. var pos {.volatile.}: pointer
  105. pos = addr(pos)
  106. if pos != nil:
  107. when defined(stackIncreases):
  108. result = cast[ByteAddress](pos) -% cast[ByteAddress](stack.bottom)
  109. else:
  110. result = cast[ByteAddress](stack.bottom) -% cast[ByteAddress](pos)
  111. else:
  112. result = 0
  113. proc stackSize(): int {.noinline.} =
  114. for stack in gch.stack.items():
  115. result = result + stack.stackSize()
  116. when nimCoroutines:
  117. proc setPosition(stack: ptr GcStack, position: pointer) =
  118. stack.pos = position
  119. stack.maxStackSize = max(stack.maxStackSize, stack.stackSize())
  120. proc setPosition(stack: var GcStack, position: pointer) =
  121. setPosition(addr(stack), position)
  122. proc getActiveStack(gch: var GcHeap): ptr GcStack =
  123. return gch.activeStack
  124. proc isActiveStack(stack: ptr GcStack): bool =
  125. return gch.activeStack == stack
  126. else:
  127. # Stack positions do not need to be tracked if coroutines are not used.
  128. proc setPosition(stack: ptr GcStack, position: pointer) = discard
  129. proc setPosition(stack: var GcStack, position: pointer) = discard
  130. # There is just one stack - main stack of the thread. It is active always.
  131. proc getActiveStack(gch: var GcHeap): ptr GcStack = addr(gch.stack)
  132. proc isActiveStack(stack: ptr GcStack): bool = true
  133. when declared(threadType):
  134. proc setupForeignThreadGc*() {.gcsafe.} =
  135. ## Call this if you registered a callback that will be run from a thread not
  136. ## under your control. This has a cheap thread-local guard, so the GC for
  137. ## this thread will only be initialized once per thread, no matter how often
  138. ## it is called.
  139. ##
  140. ## This function is available only when ``--threads:on`` and ``--tlsEmulation:off``
  141. ## switches are used
  142. if threadType == ThreadType.None:
  143. initAllocator()
  144. var stackTop {.volatile.}: pointer
  145. setStackBottom(addr(stackTop))
  146. initGC()
  147. threadType = ThreadType.ForeignThread
  148. proc tearDownForeignThreadGc*() {.gcsafe.} =
  149. ## Call this to tear down the GC, previously initialized by ``setupForeignThreadGc``.
  150. ## If GC has not been previously initialized, or has already been torn down, the
  151. ## call does nothing.
  152. ##
  153. ## This function is available only when ``--threads:on`` and ``--tlsEmulation:off``
  154. ## switches are used
  155. if threadType != ThreadType.ForeignThread:
  156. return
  157. when declared(deallocOsPages): deallocOsPages()
  158. threadType = ThreadType.None
  159. when declared(gch): zeroMem(addr gch, sizeof(gch))
  160. else:
  161. template setupForeignThreadGc*() =
  162. {.error: "setupForeignThreadGc is available only when ``--threads:on`` and ``--tlsEmulation:off`` are used".}
  163. template tearDownForeignThreadGc*() =
  164. {.error: "tearDownForeignThreadGc is available only when ``--threads:on`` and ``--tlsEmulation:off`` are used".}
  165. # ----------------- stack management --------------------------------------
  166. # inspired from Smart Eiffel
  167. when defined(emscripten):
  168. const stackIncreases = true
  169. elif defined(sparc):
  170. const stackIncreases = false
  171. elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or
  172. defined(hp9000s700) or defined(hp9000s800) or defined(hp9000s820):
  173. const stackIncreases = true
  174. else:
  175. const stackIncreases = false
  176. {.push stack_trace: off.}
  177. when nimCoroutines:
  178. proc GC_addStack(bottom: pointer) {.cdecl, exportc.} =
  179. # c_fprintf(stdout, "GC_addStack: %p;\n", bottom)
  180. var stack = gch.stack.append()
  181. stack.bottom = bottom
  182. stack.setPosition(bottom)
  183. proc GC_removeStack(bottom: pointer) {.cdecl, exportc.} =
  184. # c_fprintf(stdout, "GC_removeStack: %p;\n", bottom)
  185. gch.stack.find(bottom).remove()
  186. proc GC_setActiveStack(bottom: pointer) {.cdecl, exportc.} =
  187. ## Sets active stack and updates current stack position.
  188. # c_fprintf(stdout, "GC_setActiveStack: %p;\n", bottom)
  189. var sp {.volatile.}: pointer
  190. gch.activeStack = gch.stack.find(bottom)
  191. gch.activeStack.setPosition(addr(sp))
  192. when not defined(useNimRtl):
  193. proc setStackBottom(theStackBottom: pointer) =
  194. # Initializes main stack of the thread.
  195. when nimCoroutines:
  196. if gch.stack.next == nil:
  197. # Main stack was not initialized yet
  198. gch.stack.next = addr(gch.stack)
  199. gch.stack.prev = addr(gch.stack)
  200. gch.stack.bottom = theStackBottom
  201. gch.stack.maxStackSize = 0
  202. gch.activeStack = addr(gch.stack)
  203. if gch.stack.bottom == nil:
  204. # This branch will not be called when -d:nimCoroutines - it is fine,
  205. # because same thing is done just above.
  206. #c_fprintf(stdout, "stack bottom: %p;\n", theStackBottom)
  207. # the first init must be the one that defines the stack bottom:
  208. gch.stack.bottom = theStackBottom
  209. elif theStackBottom != gch.stack.bottom:
  210. var a = cast[ByteAddress](theStackBottom) # and not PageMask - PageSize*2
  211. var b = cast[ByteAddress](gch.stack.bottom)
  212. #c_fprintf(stdout, "old: %p new: %p;\n",gch.stack.bottom,theStackBottom)
  213. when stackIncreases:
  214. gch.stack.bottom = cast[pointer](min(a, b))
  215. else:
  216. gch.stack.bottom = cast[pointer](max(a, b))
  217. gch.stack.setPosition(theStackBottom)
  218. {.pop.}
  219. proc isOnStack(p: pointer): bool =
  220. var stackTop {.volatile.}: pointer
  221. stackTop = addr(stackTop)
  222. var a = cast[ByteAddress](gch.getActiveStack().bottom)
  223. var b = cast[ByteAddress](stackTop)
  224. when not stackIncreases:
  225. swap(a, b)
  226. var x = cast[ByteAddress](p)
  227. result = a <=% x and x <=% b
  228. when defined(sparc): # For SPARC architecture.
  229. when nimCoroutines:
  230. {.error: "Nim coroutines are not supported on this platform."}
  231. template forEachStackSlot(gch, gcMark: untyped) {.dirty.} =
  232. when defined(sparcv9):
  233. asm """"flushw \n" """
  234. else:
  235. asm """"ta 0x3 ! ST_FLUSH_WINDOWS\n" """
  236. var
  237. max = gch.stack.bottom
  238. sp: PPointer
  239. stackTop: array[0..1, pointer]
  240. sp = addr(stackTop[0])
  241. # Addresses decrease as the stack grows.
  242. while sp <= max:
  243. gcMark(gch, sp[])
  244. sp = cast[PPointer](cast[ByteAddress](sp) +% sizeof(pointer))
  245. elif defined(ELATE):
  246. {.error: "stack marking code is to be written for this architecture".}
  247. elif stackIncreases:
  248. # ---------------------------------------------------------------------------
  249. # Generic code for architectures where addresses increase as the stack grows.
  250. # ---------------------------------------------------------------------------
  251. var
  252. jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
  253. # a little hack to get the size of a JmpBuf in the generated C code
  254. # in a platform independent way
  255. template forEachStackSlot(gch, gcMark: untyped) {.dirty.} =
  256. var registers {.noinit.}: C_JmpBuf
  257. if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
  258. for stack in gch.stack.items():
  259. var max = cast[ByteAddress](gch.stack.bottom)
  260. var sp = cast[ByteAddress](addr(registers)) -% sizeof(pointer)
  261. while sp >=% max:
  262. gcMark(gch, cast[PPointer](sp)[])
  263. sp = sp -% sizeof(pointer)
  264. else:
  265. # ---------------------------------------------------------------------------
  266. # Generic code for architectures where addresses decrease as the stack grows.
  267. # ---------------------------------------------------------------------------
  268. template forEachStackSlot(gch, gcMark: untyped) {.dirty.} =
  269. # We use a jmp_buf buffer that is in the C stack.
  270. # Used to traverse the stack and registers assuming
  271. # that 'setjmp' will save registers in the C stack.
  272. type PStackSlice = ptr array[0..7, pointer]
  273. var registers {.noinit.}: C_JmpBuf
  274. # Update position of stack gc is executing in.
  275. gch.getActiveStack().setPosition(addr(registers))
  276. if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
  277. for stack in gch.stack.items():
  278. var max = cast[ByteAddress](stack.bottom)
  279. var sp = cast[ByteAddress](addr(registers))
  280. when defined(amd64):
  281. if stack.isActiveStack():
  282. # words within the jmp_buf structure may not be properly aligned.
  283. let regEnd = sp +% sizeof(registers)
  284. while sp <% regEnd:
  285. gcMark(gch, cast[PPointer](sp)[])
  286. gcMark(gch, cast[PPointer](sp +% sizeof(pointer) div 2)[])
  287. sp = sp +% sizeof(pointer)
  288. # Make sure sp is word-aligned
  289. sp = sp and not (sizeof(pointer) - 1)
  290. # loop unrolled:
  291. while sp <% max - 8*sizeof(pointer):
  292. gcMark(gch, cast[PStackSlice](sp)[0])
  293. gcMark(gch, cast[PStackSlice](sp)[1])
  294. gcMark(gch, cast[PStackSlice](sp)[2])
  295. gcMark(gch, cast[PStackSlice](sp)[3])
  296. gcMark(gch, cast[PStackSlice](sp)[4])
  297. gcMark(gch, cast[PStackSlice](sp)[5])
  298. gcMark(gch, cast[PStackSlice](sp)[6])
  299. gcMark(gch, cast[PStackSlice](sp)[7])
  300. sp = sp +% sizeof(pointer)*8
  301. # last few entries:
  302. while sp <=% max:
  303. gcMark(gch, cast[PPointer](sp)[])
  304. sp = sp +% sizeof(pointer)
  305. # ----------------------------------------------------------------------------
  306. # end of non-portable code
  307. # ----------------------------------------------------------------------------
  308. proc prepareDealloc(cell: PCell) =
  309. when declared(useMarkForDebug):
  310. when useMarkForDebug:
  311. gcAssert(cell notin gch.marked, "Cell still alive!")
  312. let t = cell.typ
  313. if t.finalizer != nil:
  314. # the finalizer could invoke something that
  315. # allocates memory; this could trigger a garbage
  316. # collection. Since we are already collecting we
  317. # prevend recursive entering here by a lock.
  318. # XXX: we should set the cell's children to nil!
  319. inc(gch.recGcLock)
  320. (cast[Finalizer](t.finalizer))(cellToUsr(cell))
  321. dec(gch.recGcLock)
  322. decTypeSize(cell, t)
  323. proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) =
  324. ## Frees the thread local heap. Runs every finalizer if ``runFinalizers```
  325. ## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation
  326. ## happens to ensure the GC can continue to work after the call
  327. ## to ``deallocHeap``.
  328. template deallocCell(x) =
  329. if isCell(x):
  330. # cast to PCell is correct here:
  331. prepareDealloc(cast[PCell](x))
  332. if runFinalizers:
  333. when not declared(allObjectsAsProc):
  334. for x in allObjects(gch.region):
  335. deallocCell(x)
  336. else:
  337. var spaceIter: ObjectSpaceIter
  338. while true:
  339. let x = allObjectsAsProc(gch.region, addr spaceIter)
  340. if spaceIter.state < 0: break
  341. deallocCell(x)
  342. deallocOsPages(gch.region)
  343. zeroMem(addr gch.region, sizeof(gch.region))
  344. if allowGcAfterwards:
  345. initGC()