gc_common.nim 16 KB

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