gc.nim 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884
  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 +% i *% cell.typ.base.size +%
  299. GenericSeqSize), cell.typ.base, op)
  300. else: discard
  301. proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} =
  302. # we check the last 8 entries (cache line) for a slot that could be reused.
  303. # In 63% of all cases we succeed here! But we have to optimize the heck
  304. # out of this small linear search so that ``newObj`` is not slowed down.
  305. #
  306. # Slots to try cache hit
  307. # 1 32%
  308. # 4 59%
  309. # 8 63%
  310. # 16 66%
  311. # all slots 68%
  312. var L = gch.zct.len
  313. var d = gch.zct.d
  314. when true:
  315. # loop unrolled for performance:
  316. template replaceZctEntry(i: untyped) =
  317. c = d[i]
  318. if c.refcount >=% rcIncrement:
  319. c.refcount = c.refcount and not ZctFlag
  320. d[i] = res
  321. return
  322. if L > 8:
  323. var c: PCell
  324. replaceZctEntry(L-1)
  325. replaceZctEntry(L-2)
  326. replaceZctEntry(L-3)
  327. replaceZctEntry(L-4)
  328. replaceZctEntry(L-5)
  329. replaceZctEntry(L-6)
  330. replaceZctEntry(L-7)
  331. replaceZctEntry(L-8)
  332. add(gch.zct, res)
  333. else:
  334. d[L] = res
  335. inc(gch.zct.len)
  336. else:
  337. for i in countdown(L-1, max(0, L-8)):
  338. var c = d[i]
  339. if c.refcount >=% rcIncrement:
  340. c.refcount = c.refcount and not ZctFlag
  341. d[i] = res
  342. return
  343. add(gch.zct, res)
  344. {.push stackTrace: off, profiler:off.}
  345. proc gcInvariant*() =
  346. sysAssert(allocInv(gch.region), "injected")
  347. when declared(markForDebug):
  348. markForDebug(gch)
  349. {.pop.}
  350. template setFrameInfo(c: PCell) =
  351. when leakDetector:
  352. if framePtr != nil and framePtr.prev != nil:
  353. c.filename = framePtr.prev.filename
  354. c.line = framePtr.prev.line
  355. else:
  356. c.filename = nil
  357. c.line = 0
  358. proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
  359. # generates a new object and sets its reference counter to 0
  360. incTypeSize typ, size
  361. sysAssert(allocInv(gch.region), "rawNewObj begin")
  362. gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
  363. collectCT(gch)
  364. var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
  365. #gcAssert typ.kind in {tyString, tySequence} or size >= typ.base.size, "size too small"
  366. gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
  367. # now it is buffered in the ZCT
  368. res.typ = typ
  369. setFrameInfo(res)
  370. # refcount is zero, color is black, but mark it to be in the ZCT
  371. res.refcount = ZctFlag
  372. sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
  373. # its refcount is zero, so add it to the ZCT:
  374. addNewObjToZCT(res, gch)
  375. logCell("new cell", res)
  376. track("rawNewObj", res, size)
  377. gcTrace(res, csAllocated)
  378. when useCellIds:
  379. inc gch.idGenerator
  380. res.id = gch.idGenerator * 1000_000 + gch.gcThreadId
  381. result = cellToUsr(res)
  382. sysAssert(allocInv(gch.region), "rawNewObj end")
  383. {.pop.} # .stackTrace off
  384. {.pop.} # .profiler off
  385. proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
  386. result = rawNewObj(typ, size, gch)
  387. when defined(memProfiler): nimProfile(size)
  388. proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
  389. result = rawNewObj(typ, size, gch)
  390. zeroMem(result, size)
  391. when defined(memProfiler): nimProfile(size)
  392. proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
  393. # `newObj` already uses locks, so no need for them here.
  394. let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
  395. result = newObj(typ, size)
  396. cast[PGenericSeq](result).len = len
  397. cast[PGenericSeq](result).reserved = len
  398. when defined(memProfiler): nimProfile(size)
  399. proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
  400. # generates a new object and sets its reference counter to 1
  401. incTypeSize typ, size
  402. sysAssert(allocInv(gch.region), "newObjRC1 begin")
  403. gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
  404. collectCT(gch)
  405. sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
  406. var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
  407. sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
  408. sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
  409. # now it is buffered in the ZCT
  410. res.typ = typ
  411. setFrameInfo(res)
  412. res.refcount = rcIncrement # refcount is 1
  413. sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
  414. logCell("new cell", res)
  415. track("newObjRC1", res, size)
  416. gcTrace(res, csAllocated)
  417. when useCellIds:
  418. inc gch.idGenerator
  419. res.id = gch.idGenerator * 1000_000 + gch.gcThreadId
  420. result = cellToUsr(res)
  421. zeroMem(result, size)
  422. sysAssert(allocInv(gch.region), "newObjRC1 end")
  423. when defined(memProfiler): nimProfile(size)
  424. proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
  425. let size = addInt(mulInt(len, typ.base.size), GenericSeqSize)
  426. result = newObjRC1(typ, size)
  427. cast[PGenericSeq](result).len = len
  428. cast[PGenericSeq](result).reserved = len
  429. when defined(memProfiler): nimProfile(size)
  430. proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
  431. collectCT(gch)
  432. var ol = usrToCell(old)
  433. sysAssert(ol.typ != nil, "growObj: 1")
  434. gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
  435. sysAssert(allocInv(gch.region), "growObj begin")
  436. var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
  437. var elemSize = 1
  438. if ol.typ.kind != tyString: elemSize = ol.typ.base.size
  439. incTypeSize ol.typ, newsize
  440. var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
  441. copyMem(res, ol, oldsize + sizeof(Cell))
  442. zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)),
  443. newsize-oldsize)
  444. sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
  445. # This can be wrong for intermediate temps that are nevertheless on the
  446. # heap because of lambda lifting:
  447. #gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4")
  448. logCell("growObj old cell", ol)
  449. logCell("growObj new cell", res)
  450. gcTrace(ol, csZctFreed)
  451. gcTrace(res, csAllocated)
  452. track("growObj old", ol, 0)
  453. track("growObj new", res, newsize)
  454. when defined(nimIncrSeqV3):
  455. # since we steal the old seq's contents, we set the old length to 0.
  456. cast[PGenericSeq](old).len = 0
  457. elif reallyDealloc:
  458. sysAssert(allocInv(gch.region), "growObj before dealloc")
  459. if ol.refcount shr rcShift <=% 1:
  460. # free immediately to save space:
  461. if (ol.refcount and ZctFlag) != 0:
  462. var j = gch.zct.len-1
  463. var d = gch.zct.d
  464. while j >= 0:
  465. if d[j] == ol:
  466. d[j] = res
  467. break
  468. dec(j)
  469. beforeDealloc(gch, ol, "growObj stack trash")
  470. decTypeSize(ol, ol.typ)
  471. rawDealloc(gch.region, ol)
  472. else:
  473. # we split the old refcount in 2 parts. XXX This is still not entirely
  474. # correct if the pointer that receives growObj's result is on the stack.
  475. # A better fix would be to emit the location specific write barrier for
  476. # 'growObj', but this is lots of more work and who knows what new problems
  477. # this would create.
  478. res.refcount = rcIncrement
  479. decRef(ol)
  480. else:
  481. sysAssert(ol.typ != nil, "growObj: 5")
  482. zeroMem(ol, sizeof(Cell))
  483. when useCellIds:
  484. inc gch.idGenerator
  485. res.id = gch.idGenerator * 1000_000 + gch.gcThreadId
  486. result = cellToUsr(res)
  487. sysAssert(allocInv(gch.region), "growObj end")
  488. when defined(memProfiler): nimProfile(newsize-oldsize)
  489. proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
  490. result = growObj(old, newsize, gch)
  491. {.push profiler:off, stackTrace:off.}
  492. # ---------------- cycle collector -------------------------------------------
  493. proc freeCyclicCell(gch: var GcHeap, c: PCell) =
  494. prepareDealloc(c)
  495. gcTrace(c, csCycFreed)
  496. track("cycle collector dealloc cell", c, 0)
  497. logCell("cycle collector dealloc cell", c)
  498. when reallyDealloc:
  499. sysAssert(allocInv(gch.region), "free cyclic cell")
  500. beforeDealloc(gch, c, "freeCyclicCell: stack trash")
  501. rawDealloc(gch.region, c)
  502. else:
  503. gcAssert(c.typ != nil, "freeCyclicCell")
  504. zeroMem(c, sizeof(Cell))
  505. proc sweep(gch: var GcHeap) =
  506. for x in allObjects(gch.region):
  507. if isCell(x):
  508. # cast to PCell is correct here:
  509. var c = cast[PCell](x)
  510. if c notin gch.marked: freeCyclicCell(gch, c)
  511. proc markS(gch: var GcHeap, c: PCell) =
  512. gcAssert isAllocatedPtr(gch.region, c), "markS: foreign heap root detected A!"
  513. incl(gch.marked, c)
  514. gcAssert gch.tempStack.len == 0, "stack not empty!"
  515. forAllChildren(c, waMarkPrecise)
  516. while gch.tempStack.len > 0:
  517. dec gch.tempStack.len
  518. var d = gch.tempStack.d[gch.tempStack.len]
  519. gcAssert isAllocatedPtr(gch.region, d), "markS: foreign heap root detected B!"
  520. if not containsOrIncl(gch.marked, d):
  521. forAllChildren(d, waMarkPrecise)
  522. proc markGlobals(gch: var GcHeap) =
  523. if gch.gcThreadId == 0:
  524. for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
  525. for i in 0 .. threadLocalMarkersLen-1: threadLocalMarkers[i]()
  526. let d = gch.additionalRoots.d
  527. for i in 0 .. gch.additionalRoots.len-1: markS(gch, d[i])
  528. when logGC:
  529. var
  530. cycleCheckA: array[100, PCell]
  531. cycleCheckALen = 0
  532. proc alreadySeen(c: PCell): bool =
  533. for i in 0 .. cycleCheckALen-1:
  534. if cycleCheckA[i] == c: return true
  535. if cycleCheckALen == len(cycleCheckA):
  536. gcAssert(false, "cycle detection overflow")
  537. quit 1
  538. cycleCheckA[cycleCheckALen] = c
  539. inc cycleCheckALen
  540. proc debugGraph(s: PCell) =
  541. if alreadySeen(s):
  542. writeCell("child cell (already seen) ", s)
  543. else:
  544. writeCell("cell {", s)
  545. forAllChildren(s, waDebug)
  546. c_printf("}\n")
  547. proc doOperation(p: pointer, op: WalkOp) =
  548. if p == nil: return
  549. var c: PCell = usrToCell(p)
  550. gcAssert(c != nil, "doOperation: 1")
  551. # the 'case' should be faster than function pointers because of easy
  552. # prediction:
  553. case op
  554. of waZctDecRef:
  555. #if not isAllocatedPtr(gch.region, c):
  556. # c_printf("[GC] decref bug: %p", c)
  557. gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
  558. gcAssert(c.refcount >=% rcIncrement, "doOperation 2")
  559. logCell("decref (from doOperation)", c)
  560. track("waZctDecref", p, 0)
  561. decRef(c)
  562. of waPush:
  563. add(gch.tempStack, c)
  564. of waMarkGlobal:
  565. markS(gch, c)
  566. of waMarkPrecise:
  567. add(gch.tempStack, c)
  568. #of waDebug: debugGraph(c)
  569. proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
  570. doOperation(d, WalkOp(op))
  571. proc collectZCT(gch: var GcHeap): bool {.benign.}
  572. proc collectCycles(gch: var GcHeap) =
  573. when hasThreadSupport:
  574. for c in gch.toDispose:
  575. nimGCunref(c)
  576. # ensure the ZCT 'color' is not used:
  577. while gch.zct.len > 0: discard collectZCT(gch)
  578. cellsetReset(gch.marked)
  579. var d = gch.decStack.d
  580. for i in 0..gch.decStack.len-1:
  581. sysAssert isAllocatedPtr(gch.region, d[i]), "collectCycles"
  582. markS(gch, d[i])
  583. markGlobals(gch)
  584. sweep(gch)
  585. proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
  586. # the addresses are not as cells on the stack, so turn them to cells:
  587. sysAssert(allocInv(gch.region), "gcMark begin")
  588. var cell = usrToCell(p)
  589. var c = cast[ByteAddress](cell)
  590. if c >% PageSize:
  591. # fast check: does it look like a cell?
  592. var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
  593. if objStart != nil:
  594. # mark the cell:
  595. incRef(objStart)
  596. add(gch.decStack, objStart)
  597. when false:
  598. if isAllocatedPtr(gch.region, cell):
  599. sysAssert false, "allocated pointer but not interior?"
  600. # mark the cell:
  601. incRef(cell)
  602. add(gch.decStack, cell)
  603. sysAssert(allocInv(gch.region), "gcMark end")
  604. #[
  605. This method is conditionally marked with an attribute so that it gets ignored by the LLVM ASAN
  606. (Address SANitizer) intrumentation as it will raise false errors due to the implementation of
  607. garbage collection that is used by Nim. For more information, please see the documentation of
  608. `CLANG_NO_SANITIZE_ADDRESS` in `lib/nimbase.h`.
  609. ]#
  610. proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl,
  611. codegenDecl: "CLANG_NO_SANITIZE_ADDRESS $# $#$#".} =
  612. forEachStackSlot(gch, gcMark)
  613. proc collectZCT(gch: var GcHeap): bool =
  614. # Note: Freeing may add child objects to the ZCT! So essentially we do
  615. # deep freeing, which is bad for incremental operation. In order to
  616. # avoid a deep stack, we move objects to keep the ZCT small.
  617. # This is performance critical!
  618. const workPackage = 100
  619. var L = addr(gch.zct.len)
  620. when withRealTime:
  621. var steps = workPackage
  622. var t0: Ticks
  623. if gch.maxPause > 0: t0 = getticks()
  624. while L[] > 0:
  625. var c = gch.zct.d[0]
  626. sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr")
  627. # remove from ZCT:
  628. gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT")
  629. c.refcount = c.refcount and not ZctFlag
  630. gch.zct.d[0] = gch.zct.d[L[] - 1]
  631. dec(L[])
  632. when withRealTime: dec steps
  633. if c.refcount <% rcIncrement:
  634. # It may have a RC > 0, if it is in the hardware stack or
  635. # it has not been removed yet from the ZCT. This is because
  636. # ``incref`` does not bother to remove the cell from the ZCT
  637. # as this might be too slow.
  638. # In any case, it should be removed from the ZCT. But not
  639. # freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
  640. logCell("zct dealloc cell", c)
  641. track("zct dealloc cell", c, 0)
  642. gcTrace(c, csZctFreed)
  643. # We are about to free the object, call the finalizer BEFORE its
  644. # children are deleted as well, because otherwise the finalizer may
  645. # access invalid memory. This is done by prepareDealloc():
  646. prepareDealloc(c)
  647. forAllChildren(c, waZctDecRef)
  648. when reallyDealloc:
  649. sysAssert(allocInv(gch.region), "collectZCT: rawDealloc")
  650. beforeDealloc(gch, c, "collectZCT: stack trash")
  651. rawDealloc(gch.region, c)
  652. else:
  653. sysAssert(c.typ != nil, "collectZCT 2")
  654. zeroMem(c, sizeof(Cell))
  655. when withRealTime:
  656. if steps == 0:
  657. steps = workPackage
  658. if gch.maxPause > 0:
  659. let duration = getticks() - t0
  660. # the GC's measuring is not accurate and needs some cleanup actions
  661. # (stack unmarking), so subtract some short amount of time in
  662. # order to miss deadlines less often:
  663. if duration >= gch.maxPause - 50_000:
  664. return false
  665. result = true
  666. proc unmarkStackAndRegisters(gch: var GcHeap) =
  667. var d = gch.decStack.d
  668. for i in 0..gch.decStack.len-1:
  669. sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
  670. decRef(d[i])
  671. gch.decStack.len = 0
  672. proc collectCTBody(gch: var GcHeap) =
  673. when withRealTime:
  674. let t0 = getticks()
  675. sysAssert(allocInv(gch.region), "collectCT: begin")
  676. when nimCoroutines:
  677. for stack in gch.stack.items():
  678. gch.stat.maxStackSize = max(gch.stat.maxStackSize, stack.stackSize())
  679. else:
  680. gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
  681. sysAssert(gch.decStack.len == 0, "collectCT")
  682. prepareForInteriorPointerChecking(gch.region)
  683. markStackAndRegisters(gch)
  684. gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
  685. inc(gch.stat.stackScans)
  686. if collectZCT(gch):
  687. when cycleGC:
  688. if getOccupiedMem(gch.region) >= gch.cycleThreshold or alwaysCycleGC:
  689. collectCycles(gch)
  690. #discard collectZCT(gch)
  691. inc(gch.stat.cycleCollections)
  692. gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
  693. CycleIncrease)
  694. gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
  695. unmarkStackAndRegisters(gch)
  696. sysAssert(allocInv(gch.region), "collectCT: end")
  697. when withRealTime:
  698. let duration = getticks() - t0
  699. gch.stat.maxPause = max(gch.stat.maxPause, duration)
  700. when defined(reportMissedDeadlines):
  701. if gch.maxPause > 0 and duration > gch.maxPause:
  702. c_printf("[GC] missed deadline: %ld\n", duration)
  703. proc collectCT(gch: var GcHeap) =
  704. if (gch.zct.len >= gch.zctThreshold or (cycleGC and
  705. getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
  706. gch.recGcLock == 0:
  707. when false:
  708. prepareForInteriorPointerChecking(gch.region)
  709. cellsetReset(gch.marked)
  710. markForDebug(gch)
  711. collectCTBody(gch)
  712. gch.zctThreshold = max(InitialZctThreshold, gch.zct.len * CycleIncrease)
  713. proc GC_collectZct*() =
  714. ## Collect the ZCT (zero count table). Unstable, experimental API for
  715. ## testing purposes.
  716. ## DO NOT USE!
  717. collectCTBody(gch)
  718. when withRealTime:
  719. proc toNano(x: int): Nanos {.inline.} =
  720. result = x * 1000
  721. proc GC_setMaxPause*(MaxPauseInUs: int) =
  722. gch.maxPause = MaxPauseInUs.toNano
  723. proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
  724. gch.maxPause = us.toNano
  725. if (gch.zct.len >= gch.zctThreshold or (cycleGC and
  726. getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or
  727. strongAdvice:
  728. collectCTBody(gch)
  729. gch.zctThreshold = max(InitialZctThreshold, gch.zct.len * CycleIncrease)
  730. proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
  731. if stackSize >= 0:
  732. var stackTop {.volatile.}: pointer
  733. gch.getActiveStack().pos = addr(stackTop)
  734. for stack in gch.stack.items():
  735. stack.bottomSaved = stack.bottom
  736. when stackIncreases:
  737. stack.bottom = cast[pointer](
  738. cast[ByteAddress](stack.pos) - sizeof(pointer) * 6 - stackSize)
  739. else:
  740. stack.bottom = cast[pointer](
  741. cast[ByteAddress](stack.pos) + sizeof(pointer) * 6 + stackSize)
  742. GC_step(gch, us, strongAdvice)
  743. if stackSize >= 0:
  744. for stack in gch.stack.items():
  745. stack.bottom = stack.bottomSaved
  746. when not defined(useNimRtl):
  747. proc GC_disable() =
  748. inc(gch.recGcLock)
  749. proc GC_enable() =
  750. if gch.recGcLock <= 0:
  751. raise newException(AssertionError,
  752. "API usage error: GC_enable called but GC is already enabled")
  753. dec(gch.recGcLock)
  754. proc GC_setStrategy(strategy: GC_Strategy) =
  755. discard
  756. proc GC_enableMarkAndSweep() =
  757. gch.cycleThreshold = InitialCycleThreshold
  758. proc GC_disableMarkAndSweep() =
  759. gch.cycleThreshold = high(gch.cycleThreshold)-1
  760. # set to the max value to suppress the cycle detector
  761. proc GC_fullCollect() =
  762. var oldThreshold = gch.cycleThreshold
  763. gch.cycleThreshold = 0 # forces cycle collection
  764. collectCT(gch)
  765. gch.cycleThreshold = oldThreshold
  766. proc GC_getStatistics(): string =
  767. result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
  768. "[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
  769. "[GC] stack scans: " & $gch.stat.stackScans & "\n" &
  770. "[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
  771. "[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
  772. "[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
  773. "[GC] zct capacity: " & $gch.zct.cap & "\n" &
  774. "[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
  775. "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n"
  776. when nimCoroutines:
  777. result.add "[GC] number of stacks: " & $gch.stack.len & "\n"
  778. for stack in items(gch.stack):
  779. result.add "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & cast[pointer](stack.maxStackSize).repr & "\n"
  780. else:
  781. # this caused memory leaks, see #10488 ; find a way without `repr`
  782. # maybe using a local copy of strutils.toHex or snprintf
  783. when defined(logGC):
  784. result.add "[GC] stack bottom: " & gch.stack.bottom.repr
  785. result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
  786. {.pop.} # profiler: off, stackTrace: off