gc.nim 31 KB

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