gc.nim 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2016 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # Garbage Collector
  10. #
  11. # Refcounting + Mark&Sweep. Complex algorithms avoided.
  12. # Been there, done that, didn't work.
  13. {.push profiler:off.}
  14. const
  15. CycleIncrease = 2 # is a multiplicative increase
  16. InitialCycleThreshold = when defined(nimCycleBreaker): high(int)
  17. else: 4*1024*1024 # X MB because cycle checking is slow
  18. InitialZctThreshold = 500 # we collect garbage if the ZCT's size
  19. # reaches this threshold
  20. # this seems to be a good value
  21. withRealTime = defined(useRealtimeGC)
  22. when withRealTime and not declared(getTicks):
  23. include "system/timers"
  24. when defined(memProfiler):
  25. proc nimProfile(requestedSize: int) {.benign.}
  26. when hasThreadSupport:
  27. import sharedlist
  28. const
  29. rcIncrement = 0b1000 # so that lowest 3 bits are not touched
  30. rcBlack = 0b000 # cell is colored black; in use or free
  31. rcGray = 0b001 # possible member of a cycle
  32. rcWhite = 0b010 # member of a garbage cycle
  33. rcPurple = 0b011 # possible root of a cycle
  34. ZctFlag = 0b100 # in ZCT
  35. rcShift = 3 # shift by rcShift to get the reference counter
  36. colorMask = 0b011
  37. type
  38. WalkOp = enum
  39. waMarkGlobal, # part of the backup/debug mark&sweep
  40. waMarkPrecise, # part of the backup/debug mark&sweep
  41. waZctDecRef, waPush
  42. #, waDebug
  43. Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
  44. # A ref type can have a finalizer that is called before the object's
  45. # storage is freed.
  46. GcStat {.final, pure.} = object
  47. stackScans: int # number of performed stack scans (for statistics)
  48. cycleCollections: int # number of performed full collections
  49. maxThreshold: int # max threshold that has been set
  50. maxStackSize: int # max stack size
  51. maxStackCells: int # max stack cells in ``decStack``
  52. cycleTableSize: int # max entries in cycle table
  53. maxPause: int64 # max measured GC pause in nanoseconds
  54. GcStack {.final, pure.} = object
  55. when nimCoroutines:
  56. prev: ptr GcStack
  57. next: ptr GcStack
  58. maxStackSize: int # Used to track statistics because we can not use
  59. # GcStat.maxStackSize when multiple stacks exist.
  60. bottom: pointer
  61. when withRealTime or nimCoroutines:
  62. pos: pointer # Used with `withRealTime` only for code clarity, see GC_Step().
  63. when withRealTime:
  64. bottomSaved: pointer
  65. GcHeap {.final, pure.} = object # this contains the zero count and
  66. # non-zero count table
  67. stack: GcStack
  68. when nimCoroutines:
  69. activeStack: ptr GcStack # current executing coroutine stack.
  70. cycleThreshold: int
  71. zctThreshold: int
  72. when useCellIds:
  73. idGenerator: int
  74. zct: CellSeq # the zero count table
  75. decStack: CellSeq # cells in the stack that are to decref again
  76. tempStack: CellSeq # temporary stack for recursion elimination
  77. recGcLock: int # prevent recursion via finalizers; no thread lock
  78. when withRealTime:
  79. maxPause: Nanos # max allowed pause in nanoseconds; active if > 0
  80. region: MemRegion # garbage collected region
  81. stat: GcStat
  82. marked: CellSet
  83. additionalRoots: CellSeq # dummy roots for GC_ref/unref
  84. when hasThreadSupport:
  85. toDispose: SharedList[pointer]
  86. gcThreadId: int
  87. var
  88. gch {.rtlThreadVar.}: GcHeap
  89. when not defined(useNimRtl):
  90. instantiateForRegion(gch.region)
  91. template gcAssert(cond: bool, msg: string) =
  92. when defined(useGcAssert):
  93. if not cond:
  94. cstderr.rawWrite "[GCASSERT] "
  95. cstderr.rawWrite msg
  96. when defined(logGC):
  97. cstderr.rawWrite "[GCASSERT] statistics:\L"
  98. cstderr.rawWrite GC_getStatistics()
  99. GC_disable()
  100. writeStackTrace()
  101. #var x: ptr int
  102. #echo x[]
  103. quit 1
  104. proc addZCT(s: var CellSeq, c: PCell) {.noinline.} =
  105. if (c.refcount and ZctFlag) == 0:
  106. c.refcount = c.refcount or ZctFlag
  107. add(s, c)
  108. proc cellToUsr(cell: PCell): pointer {.inline.} =
  109. # convert object (=pointer to refcount) to pointer to userdata
  110. result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
  111. proc usrToCell(usr: pointer): PCell {.inline.} =
  112. # convert pointer to userdata to object (=pointer to refcount)
  113. result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
  114. proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
  115. # used for code generation concerning debugging
  116. result = usrToCell(c).typ
  117. proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
  118. result = usrToCell(p).refcount shr rcShift
  119. # this that has to equals zero, otherwise we have to round up UnitsPerPage:
  120. when BitsPerPage mod (sizeof(int)*8) != 0:
  121. {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
  122. template color(c): untyped = c.refCount and colorMask
  123. template setColor(c, col) =
  124. when col == rcBlack:
  125. c.refcount = c.refcount and not colorMask
  126. else:
  127. c.refcount = c.refcount and not colorMask or col
  128. when defined(logGC):
  129. proc writeCell(msg: cstring, c: PCell) =
  130. var kind = -1
  131. var typName: cstring = "nil"
  132. if c.typ != nil:
  133. kind = ord(c.typ.kind)
  134. when defined(nimTypeNames):
  135. if not c.typ.name.isNil:
  136. typName = c.typ.name
  137. when leakDetector:
  138. c_printf("[GC] %s: %p %d %s rc=%ld from %s(%ld)\n",
  139. msg, c, kind, typName, c.refcount shr rcShift, c.filename, c.line)
  140. else:
  141. c_printf("[GC] %s: %p %d %s rc=%ld; thread=%ld\n",
  142. msg, c, kind, typName, c.refcount shr rcShift, gch.gcThreadId)
  143. template logCell(msg: cstring, c: PCell) =
  144. when defined(logGC):
  145. writeCell(msg, c)
  146. template gcTrace(cell, state: untyped) =
  147. when traceGC: traceCell(cell, state)
  148. # forward declarations:
  149. proc collectCT(gch: var GcHeap) {.benign.}
  150. proc isOnStack(p: pointer): bool {.noinline, benign.}
  151. proc forAllChildren(cell: PCell, op: WalkOp) {.benign.}
  152. proc doOperation(p: pointer, op: WalkOp) {.benign.}
  153. proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
  154. # we need the prototype here for debugging purposes
  155. proc incRef(c: PCell) {.inline.} =
  156. gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr")
  157. c.refcount = c.refcount +% rcIncrement
  158. # and not colorMask
  159. logCell("incRef", c)
  160. proc nimGCref(p: pointer) {.compilerproc.} =
  161. # we keep it from being collected by pretending it's not even allocated:
  162. let c = usrToCell(p)
  163. add(gch.additionalRoots, c)
  164. incRef(c)
  165. proc rtlAddZCT(c: PCell) {.rtl, inl.} =
  166. # we MUST access gch as a global here, because this crosses DLL boundaries!
  167. addZCT(gch.zct, c)
  168. proc decRef(c: PCell) {.inline.} =
  169. gcAssert(isAllocatedPtr(gch.region, c), "decRef: interiorPtr")
  170. gcAssert(c.refcount >=% rcIncrement, "decRef")
  171. c.refcount = c.refcount -% rcIncrement
  172. if c.refcount <% rcIncrement:
  173. rtlAddZCT(c)
  174. logCell("decRef", c)
  175. proc nimGCunref(p: pointer) {.compilerproc.} =
  176. let cell = usrToCell(p)
  177. var L = gch.additionalRoots.len-1
  178. var i = L
  179. let d = gch.additionalRoots.d
  180. while i >= 0:
  181. if d[i] == cell:
  182. d[i] = d[L]
  183. dec gch.additionalRoots.len
  184. break
  185. dec(i)
  186. decRef(usrToCell(p))
  187. include gc_common
  188. template beforeDealloc(gch: var GcHeap; c: PCell; msg: typed) =
  189. when false:
  190. for i in 0..gch.decStack.len-1:
  191. if gch.decStack.d[i] == c:
  192. sysAssert(false, msg)
  193. proc nimGCunrefNoCycle(p: pointer) {.compilerproc, inline.} =
  194. sysAssert(allocInv(gch.region), "begin nimGCunrefNoCycle")
  195. decRef(usrToCell(p))
  196. sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 5")
  197. proc nimGCunrefRC1(p: pointer) {.compilerproc, inline.} =
  198. decRef(usrToCell(p))
  199. proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
  200. # the code generator calls this proc!
  201. gcAssert(not isOnStack(dest), "asgnRef")
  202. # BUGFIX: first incRef then decRef!
  203. if src != nil: incRef(usrToCell(src))
  204. if dest[] != nil: decRef(usrToCell(dest[]))
  205. dest[] = src
  206. proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline,
  207. deprecated: "old compiler compat".} = asgnRef(dest, src)
  208. proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc.} =
  209. # unsureAsgnRef updates the reference counters only if dest is not on the
  210. # stack. It is used by the code generator if it cannot decide whether a
  211. # reference is in the stack or not (this can happen for var parameters).
  212. if not isOnStack(dest):
  213. if src != nil: incRef(usrToCell(src))
  214. # XXX finally use assembler for the stack checking instead!
  215. # the test for '!= nil' is correct, but I got tired of the segfaults
  216. # resulting from the crappy stack checking:
  217. if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[]))
  218. else:
  219. # can't be an interior pointer if it's a stack location!
  220. gcAssert(interiorAllocatedPtr(gch.region, dest) == nil,
  221. "stack loc AND interior pointer")
  222. dest[] = src
  223. proc initGC() =
  224. when not defined(useNimRtl):
  225. when traceGC:
  226. for i in low(CellState)..high(CellState): init(states[i])
  227. gch.cycleThreshold = InitialCycleThreshold
  228. gch.zctThreshold = InitialZctThreshold
  229. gch.stat.stackScans = 0
  230. gch.stat.cycleCollections = 0
  231. gch.stat.maxThreshold = 0
  232. gch.stat.maxStackSize = 0
  233. gch.stat.maxStackCells = 0
  234. gch.stat.cycleTableSize = 0
  235. # init the rt
  236. init(gch.zct)
  237. init(gch.tempStack)
  238. init(gch.decStack)
  239. init(gch.marked)
  240. init(gch.additionalRoots)
  241. when hasThreadSupport:
  242. init(gch.toDispose)
  243. gch.gcThreadId = atomicInc(gHeapidGenerator) - 1
  244. gcAssert(gch.gcThreadId >= 0, "invalid computed thread ID")
  245. proc cellsetReset(s: var CellSet) =
  246. deinit(s)
  247. init(s)
  248. {.push stacktrace:off.}
  249. proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
  250. var d = cast[ByteAddress](dest)
  251. case n.kind
  252. of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
  253. of nkList:
  254. for i in 0..n.len-1:
  255. # inlined for speed
  256. if n.sons[i].kind == nkSlot:
  257. if n.sons[i].typ.kind in {tyRef, tyString, tySequence}:
  258. doOperation(cast[PPointer](d +% n.sons[i].offset)[], op)
  259. else:
  260. forAllChildrenAux(cast[pointer](d +% n.sons[i].offset),
  261. n.sons[i].typ, op)
  262. else:
  263. forAllSlotsAux(dest, n.sons[i], op)
  264. of nkCase:
  265. var m = selectBranch(dest, n)
  266. if m != nil: forAllSlotsAux(dest, m, op)
  267. of nkNone: sysAssert(false, "forAllSlotsAux")
  268. proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
  269. var d = cast[ByteAddress](dest)
  270. if dest == nil: return # nothing to do
  271. if ntfNoRefs notin mt.flags:
  272. case mt.kind
  273. of tyRef, tyString, tySequence: # leaf:
  274. doOperation(cast[PPointer](d)[], op)
  275. of tyObject, tyTuple:
  276. forAllSlotsAux(dest, mt.node, op)
  277. of tyArray, tyArrayConstr, tyOpenArray:
  278. for i in 0..(mt.size div mt.base.size)-1:
  279. forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
  280. else: discard
  281. proc forAllChildren(cell: PCell, op: WalkOp) =
  282. gcAssert(cell != nil, "forAllChildren: cell is nil")
  283. gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: pointer not part of the heap")
  284. gcAssert(cell.typ != nil, "forAllChildren: cell.typ is nil")
  285. gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: unknown GC'ed type"
  286. let marker = cell.typ.marker
  287. if marker != nil:
  288. marker(cellToUsr(cell), op.int)
  289. else:
  290. case cell.typ.kind
  291. of tyRef: # common case
  292. forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
  293. of tySequence:
  294. var d = cast[ByteAddress](cellToUsr(cell))
  295. var s = cast[PGenericSeq](d)
  296. if s != nil:
  297. for i in 0..s.len-1:
  298. forAllChildrenAux(cast[pointer](d +% align(GenericSeqSize, cell.typ.base.align) +% i *% cell.typ.base.size), cell.typ.base, op)
  299. else: discard
  300. proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
  301. # we check the last 8 entries (cache line) for a slot that could be reused.
  302. # In 63% of all cases we succeed here! But we have to optimize the heck
  303. # out of this small linear search so that ``newObj`` is not slowed down.
  304. #
  305. # Slots to try cache hit
  306. # 1 32%
  307. # 4 59%
  308. # 8 63%
  309. # 16 66%
  310. # all slots 68%
  311. var L = gch.zct.len
  312. var d = gch.zct.d
  313. when true:
  314. # loop unrolled for performance:
  315. template replaceZctEntry(i: untyped) =
  316. c = d[i]
  317. if c.refcount >=% rcIncrement:
  318. c.refcount = c.refcount and not ZctFlag
  319. d[i] = res
  320. return
  321. if L > 8:
  322. var c: PCell
  323. replaceZctEntry(L-1)
  324. replaceZctEntry(L-2)
  325. replaceZctEntry(L-3)
  326. replaceZctEntry(L-4)
  327. replaceZctEntry(L-5)
  328. replaceZctEntry(L-6)
  329. replaceZctEntry(L-7)
  330. replaceZctEntry(L-8)
  331. add(gch.zct, res)
  332. else:
  333. d[L] = res
  334. inc(gch.zct.len)
  335. else:
  336. for i in countdown(L-1, max(0, L-8)):
  337. var c = d[i]
  338. if c.refcount >=% rcIncrement:
  339. c.refcount = c.refcount and not ZctFlag
  340. d[i] = res
  341. return
  342. add(gch.zct, res)
  343. {.push stackTrace: off, profiler:off.}
  344. proc gcInvariant*() =
  345. sysAssert(allocInv(gch.region), "injected")
  346. when declared(markForDebug):
  347. markForDebug(gch)
  348. {.pop.}
  349. template setFrameInfo(c: PCell) =
  350. when leakDetector:
  351. if framePtr != nil and framePtr.prev != nil:
  352. c.filename = framePtr.prev.filename
  353. c.line = framePtr.prev.line
  354. else:
  355. c.filename = nil
  356. c.line = 0
  357. proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
  358. # generates a new object and sets its reference counter to 0
  359. incTypeSize typ, size
  360. sysAssert(allocInv(gch.region), "rawNewObj begin")
  361. gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
  362. collectCT(gch)
  363. var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
  364. #gcAssert typ.kind in {tyString, tySequence} or size >= typ.base.size, "size too small"
  365. gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
  366. # now it is buffered in the ZCT
  367. res.typ = typ
  368. setFrameInfo(res)
  369. # refcount is zero, color is black, but mark it to be in the ZCT
  370. res.refcount = ZctFlag
  371. sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
  372. # its refcount is zero, so add it to the ZCT:
  373. addNewObjToZCT(res, gch)
  374. logCell("new cell", res)
  375. track("rawNewObj", res, size)
  376. gcTrace(res, csAllocated)
  377. when useCellIds:
  378. inc gch.idGenerator
  379. res.id = gch.idGenerator * 1000_000 + gch.gcThreadId
  380. result = cellToUsr(res)
  381. sysAssert(allocInv(gch.region), "rawNewObj end")
  382. {.pop.} # .stackTrace off
  383. {.pop.} # .profiler off
  384. proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
  385. result = rawNewObj(typ, size, gch)
  386. when defined(memProfiler): nimProfile(size)
  387. proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
  388. result = rawNewObj(typ, size, gch)
  389. zeroMem(result, size)
  390. when defined(memProfiler): nimProfile(size)
  391. {.push overflowChecks: on.}
  392. proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
  393. # `newObj` already uses locks, so no need for them here.
  394. let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
  395. result = newObj(typ, size)
  396. cast[PGenericSeq](result).len = len
  397. cast[PGenericSeq](result).reserved = len
  398. when defined(memProfiler): nimProfile(size)
  399. {.pop.}
  400. proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
  401. # generates a new object and sets its reference counter to 1
  402. incTypeSize typ, size
  403. sysAssert(allocInv(gch.region), "newObjRC1 begin")
  404. gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
  405. collectCT(gch)
  406. sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
  407. var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
  408. sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
  409. sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
  410. # now it is buffered in the ZCT
  411. res.typ = typ
  412. setFrameInfo(res)
  413. res.refcount = rcIncrement # refcount is 1
  414. sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
  415. logCell("new cell", res)
  416. track("newObjRC1", res, size)
  417. gcTrace(res, csAllocated)
  418. when useCellIds:
  419. inc gch.idGenerator
  420. res.id = gch.idGenerator * 1000_000 + gch.gcThreadId
  421. result = cellToUsr(res)
  422. zeroMem(result, size)
  423. sysAssert(allocInv(gch.region), "newObjRC1 end")
  424. when defined(memProfiler): nimProfile(size)
  425. {.push overflowChecks: on.}
  426. proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
  427. let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
  428. result = newObjRC1(typ, size)
  429. cast[PGenericSeq](result).len = len
  430. cast[PGenericSeq](result).reserved = len
  431. when defined(memProfiler): nimProfile(size)
  432. {.pop.}
  433. proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
  434. collectCT(gch)
  435. var ol = usrToCell(old)
  436. sysAssert(ol.typ != nil, "growObj: 1")
  437. gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
  438. sysAssert(allocInv(gch.region), "growObj begin")
  439. var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
  440. var elemSize,elemAlign = 1
  441. if ol.typ.kind != tyString:
  442. elemSize = ol.typ.base.size
  443. elemAlign = ol.typ.base.align
  444. incTypeSize ol.typ, newsize
  445. var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len * elemSize
  446. copyMem(res, ol, oldsize + sizeof(Cell))
  447. zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)),
  448. newsize-oldsize)
  449. sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
  450. # This can be wrong for intermediate temps that are nevertheless on the
  451. # heap because of lambda lifting:
  452. #gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4")
  453. logCell("growObj old cell", ol)
  454. logCell("growObj new cell", res)
  455. gcTrace(ol, csZctFreed)
  456. gcTrace(res, csAllocated)
  457. track("growObj old", ol, 0)
  458. track("growObj new", res, newsize)
  459. when defined(nimIncrSeqV3):
  460. # since we steal the old seq's contents, we set the old length to 0.
  461. cast[PGenericSeq](old).len = 0
  462. elif reallyDealloc:
  463. sysAssert(allocInv(gch.region), "growObj before dealloc")
  464. if ol.refcount shr rcShift <=% 1:
  465. # free immediately to save space:
  466. if (ol.refcount and ZctFlag) != 0:
  467. var j = gch.zct.len-1
  468. var d = gch.zct.d
  469. while j >= 0:
  470. if d[j] == ol:
  471. d[j] = res
  472. break
  473. dec(j)
  474. beforeDealloc(gch, ol, "growObj stack trash")
  475. decTypeSize(ol, ol.typ)
  476. rawDealloc(gch.region, ol)
  477. else:
  478. # we split the old refcount in 2 parts. XXX This is still not entirely
  479. # correct if the pointer that receives growObj's result is on the stack.
  480. # A better fix would be to emit the location specific write barrier for
  481. # 'growObj', but this is lots of more work and who knows what new problems
  482. # this would create.
  483. res.refcount = rcIncrement
  484. decRef(ol)
  485. else:
  486. sysAssert(ol.typ != nil, "growObj: 5")
  487. zeroMem(ol, sizeof(Cell))
  488. when useCellIds:
  489. inc gch.idGenerator
  490. res.id = gch.idGenerator * 1000_000 + gch.gcThreadId
  491. result = cellToUsr(res)
  492. sysAssert(allocInv(gch.region), "growObj end")
  493. when defined(memProfiler): nimProfile(newsize-oldsize)
  494. proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
  495. result = growObj(old, newsize, gch)
  496. {.push profiler:off, stackTrace:off.}
  497. # ---------------- cycle collector -------------------------------------------
  498. proc freeCyclicCell(gch: var GcHeap, c: PCell) =
  499. prepareDealloc(c)
  500. gcTrace(c, csCycFreed)
  501. track("cycle collector dealloc cell", c, 0)
  502. logCell("cycle collector dealloc cell", c)
  503. when reallyDealloc:
  504. sysAssert(allocInv(gch.region), "free cyclic cell")
  505. beforeDealloc(gch, c, "freeCyclicCell: stack trash")
  506. rawDealloc(gch.region, c)
  507. else:
  508. gcAssert(c.typ != nil, "freeCyclicCell")
  509. zeroMem(c, sizeof(Cell))
  510. proc sweep(gch: var GcHeap) =
  511. for x in allObjects(gch.region):
  512. if isCell(x):
  513. # cast to PCell is correct here:
  514. var c = cast[PCell](x)
  515. if c notin gch.marked: freeCyclicCell(gch, c)
  516. proc markS(gch: var GcHeap, c: PCell) =
  517. gcAssert isAllocatedPtr(gch.region, c), "markS: foreign heap root detected A!"
  518. incl(gch.marked, c)
  519. gcAssert gch.tempStack.len == 0, "stack not empty!"
  520. forAllChildren(c, waMarkPrecise)
  521. while gch.tempStack.len > 0:
  522. dec gch.tempStack.len
  523. var d = gch.tempStack.d[gch.tempStack.len]
  524. gcAssert isAllocatedPtr(gch.region, d), "markS: foreign heap root detected B!"
  525. if not containsOrIncl(gch.marked, d):
  526. forAllChildren(d, waMarkPrecise)
  527. proc markGlobals(gch: var GcHeap) =
  528. if gch.gcThreadId == 0:
  529. for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
  530. for i in 0 .. threadLocalMarkersLen-1: threadLocalMarkers[i]()
  531. let d = gch.additionalRoots.d
  532. for i in 0 .. gch.additionalRoots.len-1: markS(gch, d[i])
  533. when logGC:
  534. var
  535. cycleCheckA: array[100, PCell]
  536. cycleCheckALen = 0
  537. proc alreadySeen(c: PCell): bool =
  538. for i in 0 .. cycleCheckALen-1:
  539. if cycleCheckA[i] == c: return true
  540. if cycleCheckALen == len(cycleCheckA):
  541. gcAssert(false, "cycle detection overflow")
  542. quit 1
  543. cycleCheckA[cycleCheckALen] = c
  544. inc cycleCheckALen
  545. proc debugGraph(s: PCell) =
  546. if alreadySeen(s):
  547. writeCell("child cell (already seen) ", s)
  548. else:
  549. writeCell("cell {", s)
  550. forAllChildren(s, waDebug)
  551. c_printf("}\n")
  552. proc doOperation(p: pointer, op: WalkOp) =
  553. if p == nil: return
  554. var c: PCell = usrToCell(p)
  555. gcAssert(c != nil, "doOperation: 1")
  556. # the 'case' should be faster than function pointers because of easy
  557. # prediction:
  558. case op
  559. of waZctDecRef:
  560. #if not isAllocatedPtr(gch.region, c):
  561. # c_printf("[GC] decref bug: %p", c)
  562. gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
  563. gcAssert(c.refcount >=% rcIncrement, "doOperation 2")
  564. logCell("decref (from doOperation)", c)
  565. track("waZctDecref", p, 0)
  566. decRef(c)
  567. of waPush:
  568. add(gch.tempStack, c)
  569. of waMarkGlobal:
  570. markS(gch, c)
  571. of waMarkPrecise:
  572. add(gch.tempStack, c)
  573. #of waDebug: debugGraph(c)
  574. proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
  575. doOperation(d, WalkOp(op))
  576. proc collectZCT(gch: var GcHeap): bool {.benign.}
  577. proc collectCycles(gch: var GcHeap) =
  578. when hasThreadSupport:
  579. for c in gch.toDispose:
  580. nimGCunref(c)
  581. # ensure the ZCT 'color' is not used:
  582. while gch.zct.len > 0: discard collectZCT(gch)
  583. cellsetReset(gch.marked)
  584. var d = gch.decStack.d
  585. for i in 0..gch.decStack.len-1:
  586. sysAssert isAllocatedPtr(gch.region, d[i]), "collectCycles"
  587. markS(gch, d[i])
  588. markGlobals(gch)
  589. sweep(gch)
  590. proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
  591. # the addresses are not as cells on the stack, so turn them to cells:
  592. sysAssert(allocInv(gch.region), "gcMark begin")
  593. var cell = usrToCell(p)
  594. var c = cast[ByteAddress](cell)
  595. if c >% PageSize:
  596. # fast check: does it look like a cell?
  597. var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
  598. if objStart != nil:
  599. # mark the cell:
  600. incRef(objStart)
  601. add(gch.decStack, objStart)
  602. when false:
  603. if isAllocatedPtr(gch.region, cell):
  604. sysAssert false, "allocated pointer but not interior?"
  605. # mark the cell:
  606. incRef(cell)
  607. add(gch.decStack, cell)
  608. sysAssert(allocInv(gch.region), "gcMark end")
  609. #[
  610. This method is conditionally marked with an attribute so that it gets ignored by the LLVM ASAN
  611. (Address SANitizer) intrumentation as it will raise false errors due to the implementation of
  612. garbage collection that is used by Nim. For more information, please see the documentation of
  613. `CLANG_NO_SANITIZE_ADDRESS` in `lib/nimbase.h`.
  614. ]#
  615. proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl,
  616. codegenDecl: "CLANG_NO_SANITIZE_ADDRESS N_LIB_PRIVATE $# $#$#".} =
  617. forEachStackSlot(gch, gcMark)
  618. proc collectZCT(gch: var GcHeap): bool =
  619. # Note: Freeing may add child objects to the ZCT! So essentially we do
  620. # deep freeing, which is bad for incremental operation. In order to
  621. # avoid a deep stack, we move objects to keep the ZCT small.
  622. # This is performance critical!
  623. const workPackage = 100
  624. var L = addr(gch.zct.len)
  625. when withRealTime:
  626. var steps = workPackage
  627. var t0: Ticks
  628. if gch.maxPause > 0: t0 = getticks()
  629. while L[] > 0:
  630. var c = gch.zct.d[0]
  631. sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr")
  632. # remove from ZCT:
  633. gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT")
  634. c.refcount = c.refcount and not ZctFlag
  635. gch.zct.d[0] = gch.zct.d[L[] - 1]
  636. dec(L[])
  637. when withRealTime: dec steps
  638. if c.refcount <% rcIncrement:
  639. # It may have a RC > 0, if it is in the hardware stack or
  640. # it has not been removed yet from the ZCT. This is because
  641. # ``incref`` does not bother to remove the cell from the ZCT
  642. # as this might be too slow.
  643. # In any case, it should be removed from the ZCT. But not
  644. # freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
  645. logCell("zct dealloc cell", c)
  646. track("zct dealloc cell", c, 0)
  647. gcTrace(c, csZctFreed)
  648. # We are about to free the object, call the finalizer BEFORE its
  649. # children are deleted as well, because otherwise the finalizer may
  650. # access invalid memory. This is done by prepareDealloc():
  651. prepareDealloc(c)
  652. forAllChildren(c, waZctDecRef)
  653. when reallyDealloc:
  654. sysAssert(allocInv(gch.region), "collectZCT: rawDealloc")
  655. beforeDealloc(gch, c, "collectZCT: stack trash")
  656. rawDealloc(gch.region, c)
  657. else:
  658. sysAssert(c.typ != nil, "collectZCT 2")
  659. zeroMem(c, sizeof(Cell))
  660. when withRealTime:
  661. if steps == 0:
  662. steps = workPackage
  663. if gch.maxPause > 0:
  664. let duration = getticks() - t0
  665. # the GC's measuring is not accurate and needs some cleanup actions
  666. # (stack unmarking), so subtract some short amount of time in
  667. # order to miss deadlines less often:
  668. if duration >= gch.maxPause - 50_000:
  669. return false
  670. result = true
  671. proc unmarkStackAndRegisters(gch: var GcHeap) =
  672. var d = gch.decStack.d
  673. for i in 0..gch.decStack.len-1:
  674. sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
  675. decRef(d[i])
  676. gch.decStack.len = 0
  677. proc collectCTBody(gch: var GcHeap) =
  678. when withRealTime:
  679. let t0 = getticks()
  680. sysAssert(allocInv(gch.region), "collectCT: begin")
  681. when nimCoroutines:
  682. for stack in gch.stack.items():
  683. gch.stat.maxStackSize = max(gch.stat.maxStackSize, stack.stackSize())
  684. else:
  685. gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
  686. sysAssert(gch.decStack.len == 0, "collectCT")
  687. prepareForInteriorPointerChecking(gch.region)
  688. markStackAndRegisters(gch)
  689. gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
  690. inc(gch.stat.stackScans)
  691. if collectZCT(gch):
  692. when cycleGC:
  693. if getOccupiedMem(gch.region) >= gch.cycleThreshold or alwaysCycleGC:
  694. collectCycles(gch)
  695. #discard collectZCT(gch)
  696. inc(gch.stat.cycleCollections)
  697. gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
  698. CycleIncrease)
  699. gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
  700. unmarkStackAndRegisters(gch)
  701. sysAssert(allocInv(gch.region), "collectCT: end")
  702. when withRealTime:
  703. let duration = getticks() - t0
  704. gch.stat.maxPause = max(gch.stat.maxPause, duration)
  705. when defined(reportMissedDeadlines):
  706. if gch.maxPause > 0 and duration > gch.maxPause:
  707. c_printf("[GC] missed deadline: %ld\n", duration)
  708. proc collectCT(gch: var GcHeap) =
  709. if (gch.zct.len >= gch.zctThreshold or (cycleGC and
  710. getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
  711. gch.recGcLock == 0:
  712. when false:
  713. prepareForInteriorPointerChecking(gch.region)
  714. cellsetReset(gch.marked)
  715. markForDebug(gch)
  716. collectCTBody(gch)
  717. gch.zctThreshold = max(InitialZctThreshold, gch.zct.len * CycleIncrease)
  718. proc GC_collectZct*() =
  719. ## Collect the ZCT (zero count table). Unstable, experimental API for
  720. ## testing purposes.
  721. ## DO NOT USE!
  722. collectCTBody(gch)
  723. when withRealTime:
  724. proc toNano(x: int): Nanos {.inline.} =
  725. result = x * 1000
  726. proc GC_setMaxPause*(MaxPauseInUs: int) =
  727. gch.maxPause = MaxPauseInUs.toNano
  728. proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
  729. gch.maxPause = us.toNano
  730. if (gch.zct.len >= gch.zctThreshold or (cycleGC and
  731. getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
  732. strongAdvice:
  733. collectCTBody(gch)
  734. gch.zctThreshold = max(InitialZctThreshold, gch.zct.len * CycleIncrease)
  735. proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
  736. if stackSize >= 0:
  737. var stackTop {.volatile.}: pointer
  738. gch.getActiveStack().pos = addr(stackTop)
  739. for stack in gch.stack.items():
  740. stack.bottomSaved = stack.bottom
  741. when stackIncreases:
  742. stack.bottom = cast[pointer](
  743. cast[ByteAddress](stack.pos) - sizeof(pointer) * 6 - stackSize)
  744. else:
  745. stack.bottom = cast[pointer](
  746. cast[ByteAddress](stack.pos) + sizeof(pointer) * 6 + stackSize)
  747. GC_step(gch, us, strongAdvice)
  748. if stackSize >= 0:
  749. for stack in gch.stack.items():
  750. stack.bottom = stack.bottomSaved
  751. when not defined(useNimRtl):
  752. proc GC_disable() =
  753. inc(gch.recGcLock)
  754. proc GC_enable() =
  755. when defined(nimDoesntTrackDefects):
  756. if gch.recGcLock <= 0:
  757. raise newException(AssertionDefect,
  758. "API usage error: GC_enable called but GC is already enabled")
  759. dec(gch.recGcLock)
  760. proc GC_setStrategy(strategy: GC_Strategy) =
  761. discard
  762. proc GC_enableMarkAndSweep() =
  763. gch.cycleThreshold = InitialCycleThreshold
  764. proc GC_disableMarkAndSweep() =
  765. gch.cycleThreshold = high(gch.cycleThreshold)-1
  766. # set to the max value to suppress the cycle detector
  767. proc GC_fullCollect() =
  768. var oldThreshold = gch.cycleThreshold
  769. gch.cycleThreshold = 0 # forces cycle collection
  770. collectCT(gch)
  771. gch.cycleThreshold = oldThreshold
  772. proc GC_getStatistics(): string =
  773. result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
  774. "[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
  775. "[GC] stack scans: " & $gch.stat.stackScans & "\n" &
  776. "[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
  777. "[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
  778. "[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
  779. "[GC] zct capacity: " & $gch.zct.cap & "\n" &
  780. "[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
  781. "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n"
  782. when nimCoroutines:
  783. result.add "[GC] number of stacks: " & $gch.stack.len & "\n"
  784. for stack in items(gch.stack):
  785. result.add "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & cast[pointer](stack.maxStackSize).repr & "\n"
  786. else:
  787. # this caused memory leaks, see #10488 ; find a way without `repr`
  788. # maybe using a local copy of strutils.toHex or snprintf
  789. when defined(logGC):
  790. result.add "[GC] stack bottom: " & gch.stack.bottom.repr
  791. result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
  792. {.pop.} # profiler: off, stackTrace: off