gc_common.nim 16 KB

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