gc_common.nim 16 KB

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