alloc.nim 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # Low level allocator for Nim. Has been designed to support the GC.
  10. {.push profiler:off.}
  11. include osalloc
  12. import std/private/syslocks
  13. import std/sysatomics
  14. template track(op, address, size) =
  15. when defined(memTracker):
  16. memTrackerOp(op, address, size)
  17. # We manage *chunks* of memory. Each chunk is a multiple of the page size.
  18. # Each chunk starts at an address that is divisible by the page size.
  19. const
  20. nimMinHeapPages {.intdefine.} = 128 # 0.5 MB
  21. SmallChunkSize = PageSize
  22. MaxFli = when sizeof(int) > 2: 30 else: 14
  23. MaxLog2Sli = 5 # 32, this cannot be increased without changing 'uint32'
  24. # everywhere!
  25. MaxSli = 1 shl MaxLog2Sli
  26. FliOffset = 6
  27. RealFli = MaxFli - FliOffset
  28. # size of chunks in last matrix bin
  29. MaxBigChunkSize = int(1'i32 shl MaxFli - 1'i32 shl (MaxFli-MaxLog2Sli-1))
  30. HugeChunkSize = MaxBigChunkSize + 1
  31. type
  32. PTrunk = ptr Trunk
  33. Trunk = object
  34. next: PTrunk # all nodes are connected with this pointer
  35. key: int # start address at bit 0
  36. bits: array[0..IntsPerTrunk-1, uint] # a bit vector
  37. TrunkBuckets = array[0..255, PTrunk]
  38. IntSet = object
  39. data: TrunkBuckets
  40. # ------------- chunk table ---------------------------------------------------
  41. # We use a PtrSet of chunk starts and a table[Page, chunksize] for chunk
  42. # endings of big chunks. This is needed by the merging operation. The only
  43. # remaining operation is best-fit for big chunks. Since there is a size-limit
  44. # for big chunks (because greater than the limit means they are returned back
  45. # to the OS), a fixed size array can be used.
  46. type
  47. PLLChunk = ptr LLChunk
  48. LLChunk = object ## *low-level* chunk
  49. size: int # remaining size
  50. acc: int # accumulator
  51. next: PLLChunk # next low-level chunk; only needed for dealloc
  52. PAvlNode = ptr AvlNode
  53. AvlNode = object
  54. link: array[0..1, PAvlNode] # Left (0) and right (1) links
  55. key, upperBound: int
  56. level: int
  57. const
  58. RegionHasLock = false # hasThreadSupport and defined(gcDestructors)
  59. type
  60. FreeCell {.final, pure.} = object
  61. next: ptr FreeCell # next free cell in chunk (overlaid with refcount)
  62. when not defined(gcDestructors):
  63. zeroField: int # 0 means cell is not used (overlaid with typ field)
  64. # 1 means cell is manually managed pointer
  65. # otherwise a PNimType is stored in there
  66. else:
  67. alignment: int
  68. PChunk = ptr BaseChunk
  69. PBigChunk = ptr BigChunk
  70. PSmallChunk = ptr SmallChunk
  71. BaseChunk {.pure, inheritable.} = object
  72. prevSize: int # size of previous chunk; for coalescing
  73. # 0th bit == 1 if 'used
  74. size: int # if < PageSize it is a small chunk
  75. owner: ptr MemRegion
  76. SmallChunk = object of BaseChunk
  77. next, prev: PSmallChunk # chunks of the same size
  78. freeList: ptr FreeCell
  79. free: int # how many bytes remain
  80. acc: int # accumulator for small object allocation
  81. when defined(gcDestructors):
  82. sharedFreeList: ptr FreeCell # make no attempt at avoiding false sharing for now for this object field
  83. data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory
  84. BigChunk = object of BaseChunk # not necessarily > PageSize!
  85. next, prev: PBigChunk # chunks of the same (or bigger) size
  86. data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory
  87. HeapLinks = object
  88. len: int
  89. chunks: array[30, (PBigChunk, int)]
  90. next: ptr HeapLinks
  91. MemRegion = object
  92. when not defined(gcDestructors):
  93. minLargeObj, maxLargeObj: int
  94. freeSmallChunks: array[0..max(1,SmallChunkSize div MemAlign-1), PSmallChunk]
  95. flBitmap: uint32
  96. slBitmap: array[RealFli, uint32]
  97. matrix: array[RealFli, array[MaxSli, PBigChunk]]
  98. llmem: PLLChunk
  99. currMem, maxMem, freeMem, occ: int # memory sizes (allocated from OS)
  100. lastSize: int # needed for the case that OS gives us pages linearly
  101. when RegionHasLock:
  102. lock: SysLock
  103. when defined(gcDestructors):
  104. sharedFreeListBigChunks: PBigChunk # make no attempt at avoiding false sharing for now for this object field
  105. chunkStarts: IntSet
  106. when not defined(gcDestructors):
  107. root, deleted, last, freeAvlNodes: PAvlNode
  108. lockActive, locked, blockChunkSizeIncrease: bool # if locked, we cannot free pages.
  109. nextChunkSize: int
  110. when not defined(gcDestructors):
  111. bottomData: AvlNode
  112. heapLinks: HeapLinks
  113. when defined(nimTypeNames):
  114. allocCounter, deallocCounter: int
  115. template smallChunkOverhead(): untyped = sizeof(SmallChunk)
  116. template bigChunkOverhead(): untyped = sizeof(BigChunk)
  117. when hasThreadSupport:
  118. template loada(x: untyped): untyped = atomicLoadN(unsafeAddr x, ATOMIC_RELAXED)
  119. template storea(x, y: untyped) = atomicStoreN(unsafeAddr x, y, ATOMIC_RELAXED)
  120. when false:
  121. # not yet required
  122. template atomicStatDec(x, diff: untyped) = discard atomicSubFetch(unsafeAddr x, diff, ATOMIC_RELAXED)
  123. template atomicStatInc(x, diff: untyped) = discard atomicAddFetch(unsafeAddr x, diff, ATOMIC_RELAXED)
  124. else:
  125. template loada(x: untyped): untyped = x
  126. template storea(x, y: untyped) = x = y
  127. template atomicStatDec(x, diff: untyped) = dec x, diff
  128. template atomicStatInc(x, diff: untyped) = inc x, diff
  129. const
  130. fsLookupTable: array[byte, int8] = [
  131. -1'i8, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
  132. 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
  133. 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
  134. 5, 5, 5, 5, 5, 5, 5, 5,
  135. 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
  136. 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
  137. 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
  138. 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  139. 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  140. 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  141. 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  142. 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  143. 7, 7, 7, 7, 7, 7, 7, 7
  144. ]
  145. proc msbit(x: uint32): int {.inline.} =
  146. let a = if x <= 0xff_ff'u32:
  147. (if x <= 0xff: 0 else: 8)
  148. else:
  149. (if x <= 0xff_ff_ff'u32: 16 else: 24)
  150. result = int(fsLookupTable[byte(x shr a)]) + a
  151. proc lsbit(x: uint32): int {.inline.} =
  152. msbit(x and ((not x) + 1))
  153. proc setBit(nr: int; dest: var uint32) {.inline.} =
  154. dest = dest or (1u32 shl (nr and 0x1f))
  155. proc clearBit(nr: int; dest: var uint32) {.inline.} =
  156. dest = dest and not (1u32 shl (nr and 0x1f))
  157. proc mappingSearch(r, fl, sl: var int) {.inline.} =
  158. #let t = (1 shl (msbit(uint32 r) - MaxLog2Sli)) - 1
  159. # This diverges from the standard TLSF algorithm because we need to ensure
  160. # PageSize alignment:
  161. let t = roundup((1 shl (msbit(uint32 r) - MaxLog2Sli)), PageSize) - 1
  162. r = r + t
  163. r = r and not t
  164. r = min(r, MaxBigChunkSize).int
  165. fl = msbit(uint32 r)
  166. sl = (r shr (fl - MaxLog2Sli)) - MaxSli
  167. dec fl, FliOffset
  168. sysAssert((r and PageMask) == 0, "mappingSearch: still not aligned")
  169. # See http://www.gii.upv.es/tlsf/files/papers/tlsf_desc.pdf for details of
  170. # this algorithm.
  171. proc mappingInsert(r: int): tuple[fl, sl: int] {.inline.} =
  172. sysAssert((r and PageMask) == 0, "mappingInsert: still not aligned")
  173. result.fl = msbit(uint32 r)
  174. result.sl = (r shr (result.fl - MaxLog2Sli)) - MaxSli
  175. dec result.fl, FliOffset
  176. template mat(): untyped = a.matrix[fl][sl]
  177. proc findSuitableBlock(a: MemRegion; fl, sl: var int): PBigChunk {.inline.} =
  178. let tmp = a.slBitmap[fl] and (not 0u32 shl sl)
  179. result = nil
  180. if tmp != 0:
  181. sl = lsbit(tmp)
  182. result = mat()
  183. else:
  184. fl = lsbit(a.flBitmap and (not 0u32 shl (fl + 1)))
  185. if fl > 0:
  186. sl = lsbit(a.slBitmap[fl])
  187. result = mat()
  188. template clearBits(sl, fl) =
  189. clearBit(sl, a.slBitmap[fl])
  190. if a.slBitmap[fl] == 0u32:
  191. # do not forget to cascade:
  192. clearBit(fl, a.flBitmap)
  193. proc removeChunkFromMatrix(a: var MemRegion; b: PBigChunk) =
  194. let (fl, sl) = mappingInsert(b.size)
  195. if b.next != nil: b.next.prev = b.prev
  196. if b.prev != nil: b.prev.next = b.next
  197. if mat() == b:
  198. mat() = b.next
  199. if mat() == nil:
  200. clearBits(sl, fl)
  201. b.prev = nil
  202. b.next = nil
  203. proc removeChunkFromMatrix2(a: var MemRegion; b: PBigChunk; fl, sl: int) =
  204. mat() = b.next
  205. if mat() != nil:
  206. mat().prev = nil
  207. else:
  208. clearBits(sl, fl)
  209. b.prev = nil
  210. b.next = nil
  211. proc addChunkToMatrix(a: var MemRegion; b: PBigChunk) =
  212. let (fl, sl) = mappingInsert(b.size)
  213. b.prev = nil
  214. b.next = mat()
  215. if mat() != nil:
  216. mat().prev = b
  217. mat() = b
  218. setBit(sl, a.slBitmap[fl])
  219. setBit(fl, a.flBitmap)
  220. proc incCurrMem(a: var MemRegion, bytes: int) {.inline.} =
  221. atomicStatInc(a.currMem, bytes)
  222. proc decCurrMem(a: var MemRegion, bytes: int) {.inline.} =
  223. a.maxMem = max(a.maxMem, a.currMem)
  224. atomicStatDec(a.currMem, bytes)
  225. proc getMaxMem(a: var MemRegion): int =
  226. # Since we update maxPagesCount only when freeing pages,
  227. # maxPagesCount may not be up to date. Thus we use the
  228. # maximum of these both values here:
  229. result = max(a.currMem, a.maxMem)
  230. const nimMaxHeap {.intdefine.} = 0
  231. proc allocPages(a: var MemRegion, size: int): pointer =
  232. when nimMaxHeap != 0:
  233. if a.occ + size > nimMaxHeap * 1024 * 1024:
  234. raiseOutOfMem()
  235. osAllocPages(size)
  236. proc tryAllocPages(a: var MemRegion, size: int): pointer =
  237. when nimMaxHeap != 0:
  238. if a.occ + size > nimMaxHeap * 1024 * 1024:
  239. raiseOutOfMem()
  240. osTryAllocPages(size)
  241. proc llAlloc(a: var MemRegion, size: int): pointer =
  242. # *low-level* alloc for the memory managers data structures. Deallocation
  243. # is done at the end of the allocator's life time.
  244. if a.llmem == nil or size > a.llmem.size:
  245. # the requested size is ``roundup(size+sizeof(LLChunk), PageSize)``, but
  246. # since we know ``size`` is a (small) constant, we know the requested size
  247. # is one page:
  248. sysAssert roundup(size+sizeof(LLChunk), PageSize) == PageSize, "roundup 6"
  249. var old = a.llmem # can be nil and is correct with nil
  250. a.llmem = cast[PLLChunk](allocPages(a, PageSize))
  251. when defined(nimAvlcorruption):
  252. trackLocation(a.llmem, PageSize)
  253. incCurrMem(a, PageSize)
  254. a.llmem.size = PageSize - sizeof(LLChunk)
  255. a.llmem.acc = sizeof(LLChunk)
  256. a.llmem.next = old
  257. result = cast[pointer](cast[int](a.llmem) + a.llmem.acc)
  258. dec(a.llmem.size, size)
  259. inc(a.llmem.acc, size)
  260. zeroMem(result, size)
  261. when not defined(gcDestructors):
  262. proc getBottom(a: var MemRegion): PAvlNode =
  263. result = addr(a.bottomData)
  264. if result.link[0] == nil:
  265. result.link[0] = result
  266. result.link[1] = result
  267. proc allocAvlNode(a: var MemRegion, key, upperBound: int): PAvlNode =
  268. if a.freeAvlNodes != nil:
  269. result = a.freeAvlNodes
  270. a.freeAvlNodes = a.freeAvlNodes.link[0]
  271. else:
  272. result = cast[PAvlNode](llAlloc(a, sizeof(AvlNode)))
  273. when defined(nimAvlcorruption):
  274. cprintf("tracking location: %p\n", result)
  275. result.key = key
  276. result.upperBound = upperBound
  277. let bottom = getBottom(a)
  278. result.link[0] = bottom
  279. result.link[1] = bottom
  280. result.level = 1
  281. #when defined(nimAvlcorruption):
  282. # track("allocAvlNode", result, sizeof(AvlNode))
  283. sysAssert(bottom == addr(a.bottomData), "bottom data")
  284. sysAssert(bottom.link[0] == bottom, "bottom link[0]")
  285. sysAssert(bottom.link[1] == bottom, "bottom link[1]")
  286. proc deallocAvlNode(a: var MemRegion, n: PAvlNode) {.inline.} =
  287. n.link[0] = a.freeAvlNodes
  288. a.freeAvlNodes = n
  289. proc addHeapLink(a: var MemRegion; p: PBigChunk, size: int) =
  290. var it = addr(a.heapLinks)
  291. while it != nil and it.len >= it.chunks.len: it = it.next
  292. if it == nil:
  293. var n = cast[ptr HeapLinks](llAlloc(a, sizeof(HeapLinks)))
  294. n.next = a.heapLinks.next
  295. a.heapLinks.next = n
  296. n.chunks[0] = (p, size)
  297. n.len = 1
  298. else:
  299. let L = it.len
  300. it.chunks[L] = (p, size)
  301. inc it.len
  302. when not defined(gcDestructors):
  303. include "system/avltree"
  304. proc llDeallocAll(a: var MemRegion) =
  305. var it = a.llmem
  306. while it != nil:
  307. # we know each block in the list has the size of 1 page:
  308. var next = it.next
  309. osDeallocPages(it, PageSize)
  310. it = next
  311. a.llmem = nil
  312. proc intSetGet(t: IntSet, key: int): PTrunk =
  313. var it = t.data[key and high(t.data)]
  314. while it != nil:
  315. if it.key == key: return it
  316. it = it.next
  317. result = nil
  318. proc intSetPut(a: var MemRegion, t: var IntSet, key: int): PTrunk =
  319. result = intSetGet(t, key)
  320. if result == nil:
  321. result = cast[PTrunk](llAlloc(a, sizeof(result[])))
  322. result.next = t.data[key and high(t.data)]
  323. t.data[key and high(t.data)] = result
  324. result.key = key
  325. proc contains(s: IntSet, key: int): bool =
  326. var t = intSetGet(s, key shr TrunkShift)
  327. if t != nil:
  328. var u = key and TrunkMask
  329. result = (t.bits[u shr IntShift] and (uint(1) shl (u and IntMask))) != 0
  330. else:
  331. result = false
  332. proc incl(a: var MemRegion, s: var IntSet, key: int) =
  333. var t = intSetPut(a, s, key shr TrunkShift)
  334. var u = key and TrunkMask
  335. t.bits[u shr IntShift] = t.bits[u shr IntShift] or (uint(1) shl (u and IntMask))
  336. proc excl(s: var IntSet, key: int) =
  337. var t = intSetGet(s, key shr TrunkShift)
  338. if t != nil:
  339. var u = key and TrunkMask
  340. t.bits[u shr IntShift] = t.bits[u shr IntShift] and not
  341. (uint(1) shl (u and IntMask))
  342. iterator elements(t: IntSet): int {.inline.} =
  343. # while traversing it is forbidden to change the set!
  344. for h in 0..high(t.data):
  345. var r = t.data[h]
  346. while r != nil:
  347. var i = 0
  348. while i <= high(r.bits):
  349. var w = r.bits[i] # taking a copy of r.bits[i] here is correct, because
  350. # modifying operations are not allowed during traversation
  351. var j = 0
  352. while w != 0: # test all remaining bits for zero
  353. if (w and 1) != 0: # the bit is set!
  354. yield (r.key shl TrunkShift) or (i shl IntShift +% j)
  355. inc(j)
  356. w = w shr 1
  357. inc(i)
  358. r = r.next
  359. proc isSmallChunk(c: PChunk): bool {.inline.} =
  360. result = c.size <= SmallChunkSize-smallChunkOverhead()
  361. proc chunkUnused(c: PChunk): bool {.inline.} =
  362. result = (c.prevSize and 1) == 0
  363. iterator allObjects(m: var MemRegion): pointer {.inline.} =
  364. m.locked = true
  365. for s in elements(m.chunkStarts):
  366. # we need to check here again as it could have been modified:
  367. if s in m.chunkStarts:
  368. let c = cast[PChunk](s shl PageShift)
  369. if not chunkUnused(c):
  370. if isSmallChunk(c):
  371. var c = cast[PSmallChunk](c)
  372. let size = c.size
  373. var a = cast[int](addr(c.data))
  374. let limit = a + c.acc
  375. while a <% limit:
  376. yield cast[pointer](a)
  377. a = a +% size
  378. else:
  379. let c = cast[PBigChunk](c)
  380. yield addr(c.data)
  381. m.locked = false
  382. proc iterToProc*(iter: typed, envType: typedesc; procName: untyped) {.
  383. magic: "Plugin", compileTime.}
  384. when not defined(gcDestructors):
  385. proc isCell(p: pointer): bool {.inline.} =
  386. result = cast[ptr FreeCell](p).zeroField >% 1
  387. # ------------- chunk management ----------------------------------------------
  388. proc pageIndex(c: PChunk): int {.inline.} =
  389. result = cast[int](c) shr PageShift
  390. proc pageIndex(p: pointer): int {.inline.} =
  391. result = cast[int](p) shr PageShift
  392. proc pageAddr(p: pointer): PChunk {.inline.} =
  393. result = cast[PChunk](cast[int](p) and not PageMask)
  394. #sysAssert(Contains(allocator.chunkStarts, pageIndex(result)))
  395. when false:
  396. proc writeFreeList(a: MemRegion) =
  397. var it = a.freeChunksList
  398. c_fprintf(stdout, "freeChunksList: %p\n", it)
  399. while it != nil:
  400. c_fprintf(stdout, "it: %p, next: %p, prev: %p, size: %ld\n",
  401. it, it.next, it.prev, it.size)
  402. it = it.next
  403. proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
  404. when not defined(emscripten):
  405. if not a.blockChunkSizeIncrease:
  406. let usedMem = a.occ #a.currMem # - a.freeMem
  407. if usedMem < 64 * 1024:
  408. a.nextChunkSize = PageSize*4
  409. else:
  410. a.nextChunkSize = min(roundup(usedMem shr 2, PageSize), a.nextChunkSize * 2)
  411. a.nextChunkSize = min(a.nextChunkSize, MaxBigChunkSize).int
  412. var size = size
  413. if size > a.nextChunkSize:
  414. result = cast[PBigChunk](allocPages(a, size))
  415. else:
  416. result = cast[PBigChunk](tryAllocPages(a, a.nextChunkSize))
  417. if result == nil:
  418. result = cast[PBigChunk](allocPages(a, size))
  419. a.blockChunkSizeIncrease = true
  420. else:
  421. size = a.nextChunkSize
  422. incCurrMem(a, size)
  423. inc(a.freeMem, size)
  424. a.addHeapLink(result, size)
  425. when defined(debugHeapLinks):
  426. cprintf("owner: %p; result: %p; next pointer %p; size: %ld\n", addr(a),
  427. result, result.heapLink, result.size)
  428. when defined(memtracker):
  429. trackLocation(addr result.size, sizeof(int))
  430. sysAssert((cast[int](result) and PageMask) == 0, "requestOsChunks 1")
  431. #zeroMem(result, size)
  432. result.next = nil
  433. result.prev = nil
  434. result.size = size
  435. # update next.prevSize:
  436. var nxt = cast[int](result) +% size
  437. sysAssert((nxt and PageMask) == 0, "requestOsChunks 2")
  438. var next = cast[PChunk](nxt)
  439. if pageIndex(next) in a.chunkStarts:
  440. #echo("Next already allocated!")
  441. next.prevSize = size or (next.prevSize and 1)
  442. # set result.prevSize:
  443. var lastSize = if a.lastSize != 0: a.lastSize else: PageSize
  444. var prv = cast[int](result) -% lastSize
  445. sysAssert((nxt and PageMask) == 0, "requestOsChunks 3")
  446. var prev = cast[PChunk](prv)
  447. if pageIndex(prev) in a.chunkStarts and prev.size == lastSize:
  448. #echo("Prev already allocated!")
  449. result.prevSize = lastSize or (result.prevSize and 1)
  450. else:
  451. result.prevSize = 0 or (result.prevSize and 1) # unknown
  452. # but do not overwrite 'used' field
  453. a.lastSize = size # for next request
  454. sysAssert((cast[int](result) and PageMask) == 0, "requestOschunks: unaligned chunk")
  455. proc isAccessible(a: MemRegion, p: pointer): bool {.inline.} =
  456. result = contains(a.chunkStarts, pageIndex(p))
  457. proc contains[T](list, x: T): bool =
  458. var it = list
  459. while it != nil:
  460. if it == x: return true
  461. it = it.next
  462. proc listAdd[T](head: var T, c: T) {.inline.} =
  463. sysAssert(c notin head, "listAdd 1")
  464. sysAssert c.prev == nil, "listAdd 2"
  465. sysAssert c.next == nil, "listAdd 3"
  466. c.next = head
  467. if head != nil:
  468. sysAssert head.prev == nil, "listAdd 4"
  469. head.prev = c
  470. head = c
  471. proc listRemove[T](head: var T, c: T) {.inline.} =
  472. sysAssert(c in head, "listRemove")
  473. if c == head:
  474. head = c.next
  475. sysAssert c.prev == nil, "listRemove 2"
  476. if head != nil: head.prev = nil
  477. else:
  478. sysAssert c.prev != nil, "listRemove 3"
  479. c.prev.next = c.next
  480. if c.next != nil: c.next.prev = c.prev
  481. c.next = nil
  482. c.prev = nil
  483. proc updatePrevSize(a: var MemRegion, c: PBigChunk,
  484. prevSize: int) {.inline.} =
  485. var ri = cast[PChunk](cast[int](c) +% c.size)
  486. sysAssert((cast[int](ri) and PageMask) == 0, "updatePrevSize")
  487. if isAccessible(a, ri):
  488. ri.prevSize = prevSize or (ri.prevSize and 1)
  489. proc splitChunk2(a: var MemRegion, c: PBigChunk, size: int): PBigChunk =
  490. result = cast[PBigChunk](cast[int](c) +% size)
  491. result.size = c.size - size
  492. track("result.size", addr result.size, sizeof(int))
  493. when not defined(nimOptimizedSplitChunk):
  494. # still active because of weird codegen issue on some of our CIs:
  495. result.next = nil
  496. result.prev = nil
  497. # size and not used:
  498. result.prevSize = size
  499. result.owner = addr a
  500. sysAssert((size and 1) == 0, "splitChunk 2")
  501. sysAssert((size and PageMask) == 0,
  502. "splitChunk: size is not a multiple of the PageSize")
  503. updatePrevSize(a, c, result.size)
  504. c.size = size
  505. incl(a, a.chunkStarts, pageIndex(result))
  506. proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) =
  507. let rest = splitChunk2(a, c, size)
  508. addChunkToMatrix(a, rest)
  509. proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
  510. var c = c
  511. sysAssert(c.size >= PageSize, "freeBigChunk")
  512. inc(a.freeMem, c.size)
  513. c.prevSize = c.prevSize and not 1 # set 'used' to false
  514. when coalescLeft:
  515. let prevSize = c.prevSize
  516. if prevSize != 0:
  517. var le = cast[PChunk](cast[int](c) -% prevSize)
  518. sysAssert((cast[int](le) and PageMask) == 0, "freeBigChunk 4")
  519. if isAccessible(a, le) and chunkUnused(le):
  520. sysAssert(not isSmallChunk(le), "freeBigChunk 5")
  521. if not isSmallChunk(le) and le.size < MaxBigChunkSize:
  522. removeChunkFromMatrix(a, cast[PBigChunk](le))
  523. inc(le.size, c.size)
  524. excl(a.chunkStarts, pageIndex(c))
  525. c = cast[PBigChunk](le)
  526. if c.size > MaxBigChunkSize:
  527. let rest = splitChunk2(a, c, MaxBigChunkSize)
  528. when defined(nimOptimizedSplitChunk):
  529. rest.next = nil
  530. rest.prev = nil
  531. addChunkToMatrix(a, c)
  532. c = rest
  533. when coalescRight:
  534. var ri = cast[PChunk](cast[int](c) +% c.size)
  535. sysAssert((cast[int](ri) and PageMask) == 0, "freeBigChunk 2")
  536. if isAccessible(a, ri) and chunkUnused(ri):
  537. sysAssert(not isSmallChunk(ri), "freeBigChunk 3")
  538. if not isSmallChunk(ri) and c.size < MaxBigChunkSize:
  539. removeChunkFromMatrix(a, cast[PBigChunk](ri))
  540. inc(c.size, ri.size)
  541. excl(a.chunkStarts, pageIndex(ri))
  542. if c.size > MaxBigChunkSize:
  543. let rest = splitChunk2(a, c, MaxBigChunkSize)
  544. addChunkToMatrix(a, rest)
  545. addChunkToMatrix(a, c)
  546. proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
  547. sysAssert(size > 0, "getBigChunk 2")
  548. var size = size # roundup(size, PageSize)
  549. var fl = 0
  550. var sl = 0
  551. mappingSearch(size, fl, sl)
  552. sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
  553. result = findSuitableBlock(a, fl, sl)
  554. when RegionHasLock:
  555. if not a.lockActive:
  556. a.lockActive = true
  557. initSysLock(a.lock)
  558. acquireSys a.lock
  559. if result == nil:
  560. if size < nimMinHeapPages * PageSize:
  561. result = requestOsChunks(a, nimMinHeapPages * PageSize)
  562. splitChunk(a, result, size)
  563. else:
  564. result = requestOsChunks(a, size)
  565. # if we over allocated split the chunk:
  566. if result.size > size:
  567. splitChunk(a, result, size)
  568. result.owner = addr a
  569. else:
  570. removeChunkFromMatrix2(a, result, fl, sl)
  571. if result.size >= size + PageSize:
  572. splitChunk(a, result, size)
  573. # set 'used' to to true:
  574. result.prevSize = 1
  575. track("setUsedToFalse", addr result.size, sizeof(int))
  576. sysAssert result.owner == addr a, "getBigChunk: No owner set!"
  577. incl(a, a.chunkStarts, pageIndex(result))
  578. dec(a.freeMem, size)
  579. when RegionHasLock:
  580. releaseSys a.lock
  581. proc getHugeChunk(a: var MemRegion; size: int): PBigChunk =
  582. result = cast[PBigChunk](allocPages(a, size))
  583. when RegionHasLock:
  584. if not a.lockActive:
  585. a.lockActive = true
  586. initSysLock(a.lock)
  587. acquireSys a.lock
  588. incCurrMem(a, size)
  589. # XXX add this to the heap links. But also remove it from it later.
  590. when false: a.addHeapLink(result, size)
  591. sysAssert((cast[int](result) and PageMask) == 0, "getHugeChunk")
  592. result.next = nil
  593. result.prev = nil
  594. result.size = size
  595. # set 'used' to to true:
  596. result.prevSize = 1
  597. result.owner = addr a
  598. incl(a, a.chunkStarts, pageIndex(result))
  599. when RegionHasLock:
  600. releaseSys a.lock
  601. proc freeHugeChunk(a: var MemRegion; c: PBigChunk) =
  602. let size = c.size
  603. sysAssert(size >= HugeChunkSize, "freeHugeChunk: invalid size")
  604. excl(a.chunkStarts, pageIndex(c))
  605. decCurrMem(a, size)
  606. osDeallocPages(c, size)
  607. proc getSmallChunk(a: var MemRegion): PSmallChunk =
  608. var res = getBigChunk(a, PageSize)
  609. sysAssert res.prev == nil, "getSmallChunk 1"
  610. sysAssert res.next == nil, "getSmallChunk 2"
  611. result = cast[PSmallChunk](res)
  612. # -----------------------------------------------------------------------------
  613. when not defined(gcDestructors):
  614. proc isAllocatedPtr(a: MemRegion, p: pointer): bool {.benign.}
  615. when true:
  616. template allocInv(a: MemRegion): bool = true
  617. else:
  618. proc allocInv(a: MemRegion): bool =
  619. ## checks some (not all yet) invariants of the allocator's data structures.
  620. for s in low(a.freeSmallChunks)..high(a.freeSmallChunks):
  621. var c = a.freeSmallChunks[s]
  622. while not (c == nil):
  623. if c.next == c:
  624. echo "[SYSASSERT] c.next == c"
  625. return false
  626. if not (c.size == s * MemAlign):
  627. echo "[SYSASSERT] c.size != s * MemAlign"
  628. return false
  629. var it = c.freeList
  630. while not (it == nil):
  631. if not (it.zeroField == 0):
  632. echo "[SYSASSERT] it.zeroField != 0"
  633. c_printf("%ld %p\n", it.zeroField, it)
  634. return false
  635. it = it.next
  636. c = c.next
  637. result = true
  638. when false:
  639. var
  640. rsizes: array[50_000, int]
  641. rsizesLen: int
  642. proc trackSize(size: int) =
  643. rsizes[rsizesLen] = size
  644. inc rsizesLen
  645. proc untrackSize(size: int) =
  646. for i in 0 .. rsizesLen-1:
  647. if rsizes[i] == size:
  648. rsizes[i] = rsizes[rsizesLen-1]
  649. dec rsizesLen
  650. return
  651. c_fprintf(stdout, "%ld\n", size)
  652. sysAssert(false, "untracked size!")
  653. else:
  654. template trackSize(x) = discard
  655. template untrackSize(x) = discard
  656. proc deallocBigChunk(a: var MemRegion, c: PBigChunk) =
  657. when RegionHasLock:
  658. acquireSys a.lock
  659. dec a.occ, c.size
  660. untrackSize(c.size)
  661. sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case B)"
  662. when not defined(gcDestructors):
  663. a.deleted = getBottom(a)
  664. del(a, a.root, cast[int](addr(c.data)))
  665. if c.size >= HugeChunkSize: freeHugeChunk(a, c)
  666. else: freeBigChunk(a, c)
  667. when RegionHasLock:
  668. releaseSys a.lock
  669. when defined(gcDestructors):
  670. template atomicPrepend(head, elem: untyped) =
  671. # see also https://en.cppreference.com/w/cpp/atomic/atomic_compare_exchange
  672. when hasThreadSupport:
  673. while true:
  674. elem.next.storea head.loada
  675. if atomicCompareExchangeN(addr head, addr elem.next, elem, weak = true, ATOMIC_RELEASE, ATOMIC_RELAXED):
  676. break
  677. else:
  678. elem.next.storea head.loada
  679. head.storea elem
  680. proc addToSharedFreeListBigChunks(a: var MemRegion; c: PBigChunk) {.inline.} =
  681. sysAssert c.next == nil, "c.next pointer must be nil"
  682. atomicPrepend a.sharedFreeListBigChunks, c
  683. proc addToSharedFreeList(c: PSmallChunk; f: ptr FreeCell) {.inline.} =
  684. atomicPrepend c.sharedFreeList, f
  685. proc compensateCounters(a: var MemRegion; c: PSmallChunk; size: int) =
  686. # rawDealloc did NOT do the usual:
  687. # `inc(c.free, size); dec(a.occ, size)` because it wasn't the owner of these
  688. # memory locations. We have to compensate here for these for the entire list.
  689. # Well, not for the entire list, but for `max` elements of the list because
  690. # we split the list in order to achieve bounded response times.
  691. var it = c.freeList
  692. var x = 0
  693. var maxIters = 20 # make it time-bounded
  694. while it != nil:
  695. if maxIters == 0:
  696. let rest = it.next.loada
  697. if rest != nil:
  698. it.next.storea nil
  699. addToSharedFreeList(c, rest)
  700. break
  701. inc x, size
  702. it = it.next.loada
  703. dec maxIters
  704. inc(c.free, x)
  705. dec(a.occ, x)
  706. proc freeDeferredObjects(a: var MemRegion; root: PBigChunk) =
  707. var it = root
  708. var maxIters = 20 # make it time-bounded
  709. while true:
  710. if maxIters == 0:
  711. let rest = it.next.loada
  712. it.next.storea nil
  713. addToSharedFreeListBigChunks(a, rest)
  714. break
  715. it = it.next.loada
  716. dec maxIters
  717. if it == nil: break
  718. proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
  719. when defined(nimTypeNames):
  720. inc(a.allocCounter)
  721. sysAssert(allocInv(a), "rawAlloc: begin")
  722. sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
  723. var size = roundup(requestedSize, MemAlign)
  724. sysAssert(size >= sizeof(FreeCell), "rawAlloc: requested size too small")
  725. sysAssert(size >= requestedSize, "insufficient allocated size!")
  726. #c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
  727. if size <= SmallChunkSize-smallChunkOverhead():
  728. # allocate a small block: for small chunks, we use only its next pointer
  729. let s = size div MemAlign
  730. var c = a.freeSmallChunks[s]
  731. if c == nil:
  732. c = getSmallChunk(a)
  733. c.freeList = nil
  734. sysAssert c.size == PageSize, "rawAlloc 3"
  735. c.size = size
  736. c.acc = size
  737. when defined(gcDestructors):
  738. c.sharedFreeList = nil
  739. c.free = SmallChunkSize - smallChunkOverhead() - size
  740. sysAssert c.owner == addr(a), "rawAlloc: No owner set!"
  741. c.next = nil
  742. c.prev = nil
  743. listAdd(a.freeSmallChunks[s], c)
  744. result = addr(c.data)
  745. sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 4")
  746. else:
  747. sysAssert(allocInv(a), "rawAlloc: begin c != nil")
  748. sysAssert c.next != c, "rawAlloc 5"
  749. #if c.size != size:
  750. # c_fprintf(stdout, "csize: %lld; size %lld\n", c.size, size)
  751. sysAssert c.size == size, "rawAlloc 6"
  752. when defined(gcDestructors):
  753. if c.freeList == nil:
  754. when hasThreadSupport:
  755. c.freeList = atomicExchangeN(addr c.sharedFreeList, nil, ATOMIC_RELAXED)
  756. else:
  757. c.freeList = c.sharedFreeList
  758. c.sharedFreeList = nil
  759. compensateCounters(a, c, size)
  760. if c.freeList == nil:
  761. sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize,
  762. "rawAlloc 7")
  763. result = cast[pointer](cast[int](addr(c.data)) +% c.acc)
  764. inc(c.acc, size)
  765. else:
  766. result = c.freeList
  767. when not defined(gcDestructors):
  768. sysAssert(c.freeList.zeroField == 0, "rawAlloc 8")
  769. c.freeList = c.freeList.next
  770. dec(c.free, size)
  771. sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 9")
  772. sysAssert(allocInv(a), "rawAlloc: end c != nil")
  773. sysAssert(allocInv(a), "rawAlloc: before c.free < size")
  774. if c.free < size:
  775. sysAssert(allocInv(a), "rawAlloc: before listRemove test")
  776. listRemove(a.freeSmallChunks[s], c)
  777. sysAssert(allocInv(a), "rawAlloc: end listRemove test")
  778. sysAssert(((cast[int](result) and PageMask) - smallChunkOverhead()) %%
  779. size == 0, "rawAlloc 21")
  780. sysAssert(allocInv(a), "rawAlloc: end small size")
  781. inc a.occ, size
  782. trackSize(c.size)
  783. else:
  784. when defined(gcDestructors):
  785. when hasThreadSupport:
  786. let deferredFrees = atomicExchangeN(addr a.sharedFreeListBigChunks, nil, ATOMIC_RELAXED)
  787. else:
  788. let deferredFrees = a.sharedFreeListBigChunks
  789. a.sharedFreeListBigChunks = nil
  790. if deferredFrees != nil:
  791. freeDeferredObjects(a, deferredFrees)
  792. size = requestedSize + bigChunkOverhead() # roundup(requestedSize+bigChunkOverhead(), PageSize)
  793. # allocate a large block
  794. var c = if size >= HugeChunkSize: getHugeChunk(a, size)
  795. else: getBigChunk(a, size)
  796. sysAssert c.prev == nil, "rawAlloc 10"
  797. sysAssert c.next == nil, "rawAlloc 11"
  798. result = addr(c.data)
  799. sysAssert((cast[int](c) and (MemAlign-1)) == 0, "rawAlloc 13")
  800. sysAssert((cast[int](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
  801. when not defined(gcDestructors):
  802. if a.root == nil: a.root = getBottom(a)
  803. add(a, a.root, cast[int](result), cast[int](result)+%size)
  804. inc a.occ, c.size
  805. trackSize(c.size)
  806. sysAssert(isAccessible(a, result), "rawAlloc 14")
  807. sysAssert(allocInv(a), "rawAlloc: end")
  808. when logAlloc: cprintf("var pointer_%p = alloc(%ld)\n", result, requestedSize)
  809. proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
  810. result = rawAlloc(a, requestedSize)
  811. zeroMem(result, requestedSize)
  812. proc rawDealloc(a: var MemRegion, p: pointer) =
  813. when defined(nimTypeNames):
  814. inc(a.deallocCounter)
  815. #sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
  816. sysAssert(allocInv(a), "rawDealloc: begin")
  817. var c = pageAddr(p)
  818. sysAssert(c != nil, "rawDealloc: begin")
  819. if isSmallChunk(c):
  820. # `p` is within a small chunk:
  821. var c = cast[PSmallChunk](c)
  822. var s = c.size
  823. # ^ We might access thread foreign storage here.
  824. # The other thread cannot possibly free this block as it's still alive.
  825. var f = cast[ptr FreeCell](p)
  826. if c.owner == addr(a):
  827. # We own the block, there is no foreign thread involved.
  828. dec a.occ, s
  829. untrackSize(s)
  830. sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case A)"
  831. sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %%
  832. s == 0, "rawDealloc 3")
  833. when not defined(gcDestructors):
  834. #echo("setting to nil: ", $cast[int](addr(f.zeroField)))
  835. sysAssert(f.zeroField != 0, "rawDealloc 1")
  836. f.zeroField = 0
  837. f.next = c.freeList
  838. c.freeList = f
  839. when overwriteFree:
  840. # set to 0xff to check for usage after free bugs:
  841. nimSetMem(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
  842. s -% sizeof(FreeCell))
  843. # check if it is not in the freeSmallChunks[s] list:
  844. if c.free < s:
  845. # add it to the freeSmallChunks[s] array:
  846. listAdd(a.freeSmallChunks[s div MemAlign], c)
  847. inc(c.free, s)
  848. else:
  849. inc(c.free, s)
  850. if c.free == SmallChunkSize-smallChunkOverhead():
  851. listRemove(a.freeSmallChunks[s div MemAlign], c)
  852. c.size = SmallChunkSize
  853. freeBigChunk(a, cast[PBigChunk](c))
  854. else:
  855. when defined(gcDestructors):
  856. addToSharedFreeList(c, f)
  857. sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %%
  858. s == 0, "rawDealloc 2")
  859. else:
  860. # set to 0xff to check for usage after free bugs:
  861. when overwriteFree: nimSetMem(p, -1'i32, c.size -% bigChunkOverhead())
  862. when defined(gcDestructors):
  863. if c.owner == addr(a):
  864. deallocBigChunk(a, cast[PBigChunk](c))
  865. else:
  866. addToSharedFreeListBigChunks(c.owner[], cast[PBigChunk](c))
  867. else:
  868. deallocBigChunk(a, cast[PBigChunk](c))
  869. sysAssert(allocInv(a), "rawDealloc: end")
  870. when logAlloc: cprintf("dealloc(pointer_%p)\n", p)
  871. when not defined(gcDestructors):
  872. proc isAllocatedPtr(a: MemRegion, p: pointer): bool =
  873. if isAccessible(a, p):
  874. var c = pageAddr(p)
  875. if not chunkUnused(c):
  876. if isSmallChunk(c):
  877. var c = cast[PSmallChunk](c)
  878. var offset = (cast[int](p) and (PageSize-1)) -%
  879. smallChunkOverhead()
  880. result = (c.acc >% offset) and (offset %% c.size == 0) and
  881. (cast[ptr FreeCell](p).zeroField >% 1)
  882. else:
  883. var c = cast[PBigChunk](c)
  884. result = p == addr(c.data) and cast[ptr FreeCell](p).zeroField >% 1
  885. proc prepareForInteriorPointerChecking(a: var MemRegion) {.inline.} =
  886. a.minLargeObj = lowGauge(a.root)
  887. a.maxLargeObj = highGauge(a.root)
  888. proc interiorAllocatedPtr(a: MemRegion, p: pointer): pointer =
  889. if isAccessible(a, p):
  890. var c = pageAddr(p)
  891. if not chunkUnused(c):
  892. if isSmallChunk(c):
  893. var c = cast[PSmallChunk](c)
  894. var offset = (cast[int](p) and (PageSize-1)) -%
  895. smallChunkOverhead()
  896. if c.acc >% offset:
  897. sysAssert(cast[int](addr(c.data)) +% offset ==
  898. cast[int](p), "offset is not what you think it is")
  899. var d = cast[ptr FreeCell](cast[int](addr(c.data)) +%
  900. offset -% (offset %% c.size))
  901. if d.zeroField >% 1:
  902. result = d
  903. sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
  904. else:
  905. var c = cast[PBigChunk](c)
  906. var d = addr(c.data)
  907. if p >= d and cast[ptr FreeCell](d).zeroField >% 1:
  908. result = d
  909. sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
  910. else:
  911. var q = cast[int](p)
  912. if q >=% a.minLargeObj and q <=% a.maxLargeObj:
  913. # this check is highly effective! Test fails for 99,96% of all checks on
  914. # an x86-64.
  915. var avlNode = inRange(a.root, q)
  916. if avlNode != nil:
  917. var k = cast[pointer](avlNode.key)
  918. var c = cast[PBigChunk](pageAddr(k))
  919. sysAssert(addr(c.data) == k, " k is not the same as addr(c.data)!")
  920. if cast[ptr FreeCell](k).zeroField >% 1:
  921. result = k
  922. sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
  923. proc ptrSize(p: pointer): int =
  924. when not defined(gcDestructors):
  925. var x = cast[pointer](cast[int](p) -% sizeof(FreeCell))
  926. var c = pageAddr(p)
  927. sysAssert(not chunkUnused(c), "ptrSize")
  928. result = c.size -% sizeof(FreeCell)
  929. if not isSmallChunk(c):
  930. dec result, bigChunkOverhead()
  931. else:
  932. var c = pageAddr(p)
  933. sysAssert(not chunkUnused(c), "ptrSize")
  934. result = c.size
  935. if not isSmallChunk(c):
  936. dec result, bigChunkOverhead()
  937. proc alloc(allocator: var MemRegion, size: Natural): pointer {.gcsafe.} =
  938. when not defined(gcDestructors):
  939. result = rawAlloc(allocator, size+sizeof(FreeCell))
  940. cast[ptr FreeCell](result).zeroField = 1 # mark it as used
  941. sysAssert(not isAllocatedPtr(allocator, result), "alloc")
  942. result = cast[pointer](cast[int](result) +% sizeof(FreeCell))
  943. track("alloc", result, size)
  944. else:
  945. result = rawAlloc(allocator, size)
  946. proc alloc0(allocator: var MemRegion, size: Natural): pointer =
  947. result = alloc(allocator, size)
  948. zeroMem(result, size)
  949. proc dealloc(allocator: var MemRegion, p: pointer) =
  950. when not defined(gcDestructors):
  951. sysAssert(p != nil, "dealloc: p is nil")
  952. var x = cast[pointer](cast[int](p) -% sizeof(FreeCell))
  953. sysAssert(x != nil, "dealloc: x is nil")
  954. sysAssert(isAccessible(allocator, x), "is not accessible")
  955. sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc: object header corrupted")
  956. rawDealloc(allocator, x)
  957. sysAssert(not isAllocatedPtr(allocator, x), "dealloc: object still accessible")
  958. track("dealloc", p, 0)
  959. else:
  960. rawDealloc(allocator, p)
  961. proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer =
  962. if newsize > 0:
  963. result = alloc(allocator, newsize)
  964. if p != nil:
  965. copyMem(result, p, min(ptrSize(p), newsize))
  966. dealloc(allocator, p)
  967. elif p != nil:
  968. dealloc(allocator, p)
  969. proc realloc0(allocator: var MemRegion, p: pointer, oldsize, newsize: Natural): pointer =
  970. result = realloc(allocator, p, newsize)
  971. if newsize > oldsize:
  972. zeroMem(cast[pointer](cast[uint](result) + uint(oldsize)), newsize - oldsize)
  973. proc deallocOsPages(a: var MemRegion) =
  974. # we free every 'ordinarily' allocated page by iterating over the page bits:
  975. var it = addr(a.heapLinks)
  976. while true:
  977. let next = it.next
  978. for i in 0..it.len-1:
  979. let (p, size) = it.chunks[i]
  980. when defined(debugHeapLinks):
  981. cprintf("owner %p; dealloc A: %p size: %ld; next: %p\n", addr(a),
  982. it, it.size, next)
  983. sysAssert size >= PageSize, "origSize too small"
  984. osDeallocPages(p, size)
  985. it = next
  986. if it == nil: break
  987. # And then we free the pages that are in use for the page bits:
  988. llDeallocAll(a)
  989. proc getFreeMem(a: MemRegion): int {.inline.} = result = a.freeMem
  990. proc getTotalMem(a: MemRegion): int {.inline.} = result = a.currMem
  991. proc getOccupiedMem(a: MemRegion): int {.inline.} =
  992. result = a.occ
  993. # a.currMem - a.freeMem
  994. when defined(nimTypeNames):
  995. proc getMemCounters(a: MemRegion): (int, int) {.inline.} =
  996. (a.allocCounter, a.deallocCounter)
  997. # ---------------------- thread memory region -------------------------------
  998. template instantiateForRegion(allocator: untyped) {.dirty.} =
  999. {.push stackTrace: off.}
  1000. when defined(nimFulldebug):
  1001. proc interiorAllocatedPtr*(p: pointer): pointer =
  1002. result = interiorAllocatedPtr(allocator, p)
  1003. proc isAllocatedPtr*(p: pointer): bool =
  1004. let p = cast[pointer](cast[int](p)-%ByteAddress(sizeof(Cell)))
  1005. result = isAllocatedPtr(allocator, p)
  1006. proc deallocOsPages = deallocOsPages(allocator)
  1007. proc allocImpl(size: Natural): pointer =
  1008. result = alloc(allocator, size)
  1009. proc alloc0Impl(size: Natural): pointer =
  1010. result = alloc0(allocator, size)
  1011. proc deallocImpl(p: pointer) =
  1012. dealloc(allocator, p)
  1013. proc reallocImpl(p: pointer, newSize: Natural): pointer =
  1014. result = realloc(allocator, p, newSize)
  1015. proc realloc0Impl(p: pointer, oldSize, newSize: Natural): pointer =
  1016. result = realloc(allocator, p, newSize)
  1017. if newSize > oldSize:
  1018. zeroMem(cast[pointer](cast[uint](result) + uint(oldSize)), newSize - oldSize)
  1019. when false:
  1020. proc countFreeMem(): int =
  1021. # only used for assertions
  1022. var it = allocator.freeChunksList
  1023. while it != nil:
  1024. inc(result, it.size)
  1025. it = it.next
  1026. when hasThreadSupport and not defined(gcDestructors):
  1027. proc addSysExitProc(quitProc: proc() {.noconv.}) {.importc: "atexit", header: "<stdlib.h>".}
  1028. var sharedHeap: MemRegion
  1029. var heapLock: SysLock
  1030. initSysLock(heapLock)
  1031. addSysExitProc(proc() {.noconv.} = deinitSys(heapLock))
  1032. proc getFreeMem(): int =
  1033. #sysAssert(result == countFreeMem())
  1034. result = allocator.freeMem
  1035. proc getTotalMem(): int =
  1036. result = allocator.currMem
  1037. proc getOccupiedMem(): int =
  1038. result = allocator.occ #getTotalMem() - getFreeMem()
  1039. proc getMaxMem*(): int =
  1040. result = getMaxMem(allocator)
  1041. when defined(nimTypeNames):
  1042. proc getMemCounters*(): (int, int) = getMemCounters(allocator)
  1043. # -------------------- shared heap region ----------------------------------
  1044. proc allocSharedImpl(size: Natural): pointer =
  1045. when hasThreadSupport and not defined(gcDestructors):
  1046. acquireSys(heapLock)
  1047. result = alloc(sharedHeap, size)
  1048. releaseSys(heapLock)
  1049. else:
  1050. result = allocImpl(size)
  1051. proc allocShared0Impl(size: Natural): pointer =
  1052. result = allocSharedImpl(size)
  1053. zeroMem(result, size)
  1054. proc deallocSharedImpl(p: pointer) =
  1055. when hasThreadSupport and not defined(gcDestructors):
  1056. acquireSys(heapLock)
  1057. dealloc(sharedHeap, p)
  1058. releaseSys(heapLock)
  1059. else:
  1060. deallocImpl(p)
  1061. proc reallocSharedImpl(p: pointer, newSize: Natural): pointer =
  1062. when hasThreadSupport and not defined(gcDestructors):
  1063. acquireSys(heapLock)
  1064. result = realloc(sharedHeap, p, newSize)
  1065. releaseSys(heapLock)
  1066. else:
  1067. result = reallocImpl(p, newSize)
  1068. proc reallocShared0Impl(p: pointer, oldSize, newSize: Natural): pointer =
  1069. when hasThreadSupport and not defined(gcDestructors):
  1070. acquireSys(heapLock)
  1071. result = realloc0(sharedHeap, p, oldSize, newSize)
  1072. releaseSys(heapLock)
  1073. else:
  1074. result = realloc0Impl(p, oldSize, newSize)
  1075. when hasThreadSupport:
  1076. when defined(gcDestructors):
  1077. proc getFreeSharedMem(): int =
  1078. allocator.freeMem
  1079. proc getTotalSharedMem(): int =
  1080. allocator.currMem
  1081. proc getOccupiedSharedMem(): int =
  1082. allocator.occ
  1083. else:
  1084. template sharedMemStatsShared(v: int) =
  1085. acquireSys(heapLock)
  1086. result = v
  1087. releaseSys(heapLock)
  1088. proc getFreeSharedMem(): int =
  1089. sharedMemStatsShared(sharedHeap.freeMem)
  1090. proc getTotalSharedMem(): int =
  1091. sharedMemStatsShared(sharedHeap.currMem)
  1092. proc getOccupiedSharedMem(): int =
  1093. sharedMemStatsShared(sharedHeap.occ)
  1094. #sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem)
  1095. {.pop.}
  1096. {.pop.}