alloc.nim 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242
  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. data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory
  82. BigChunk = object of BaseChunk # not necessarily > PageSize!
  83. next, prev: PBigChunk # chunks of the same (or bigger) size
  84. data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory
  85. HeapLinks = object
  86. len: int
  87. chunks: array[30, (PBigChunk, int)]
  88. next: ptr HeapLinks
  89. MemRegion = object
  90. when not defined(gcDestructors):
  91. minLargeObj, maxLargeObj: int
  92. freeSmallChunks: array[0..max(1, SmallChunkSize div MemAlign-1), PSmallChunk]
  93. when defined(gcDestructors):
  94. sharedFreeLists: array[0..max(1, SmallChunkSize div MemAlign-1), ptr FreeCell]
  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): ptr HeapLinks =
  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. result = n
  299. else:
  300. let L = it.len
  301. it.chunks[L] = (p, size)
  302. inc it.len
  303. result = it
  304. when not defined(gcDestructors):
  305. include "system/avltree"
  306. proc llDeallocAll(a: var MemRegion) =
  307. var it = a.llmem
  308. while it != nil:
  309. # we know each block in the list has the size of 1 page:
  310. var next = it.next
  311. osDeallocPages(it, PageSize)
  312. it = next
  313. a.llmem = nil
  314. proc intSetGet(t: IntSet, key: int): PTrunk =
  315. var it = t.data[key and high(t.data)]
  316. while it != nil:
  317. if it.key == key: return it
  318. it = it.next
  319. result = nil
  320. proc intSetPut(a: var MemRegion, t: var IntSet, key: int): PTrunk =
  321. result = intSetGet(t, key)
  322. if result == nil:
  323. result = cast[PTrunk](llAlloc(a, sizeof(result[])))
  324. result.next = t.data[key and high(t.data)]
  325. t.data[key and high(t.data)] = result
  326. result.key = key
  327. proc contains(s: IntSet, key: int): bool =
  328. var t = intSetGet(s, key shr TrunkShift)
  329. if t != nil:
  330. var u = key and TrunkMask
  331. result = (t.bits[u shr IntShift] and (uint(1) shl (u and IntMask))) != 0
  332. else:
  333. result = false
  334. proc incl(a: var MemRegion, s: var IntSet, key: int) =
  335. var t = intSetPut(a, s, key shr TrunkShift)
  336. var u = key and TrunkMask
  337. t.bits[u shr IntShift] = t.bits[u shr IntShift] or (uint(1) shl (u and IntMask))
  338. proc excl(s: var IntSet, key: int) =
  339. var t = intSetGet(s, key shr TrunkShift)
  340. if t != nil:
  341. var u = key and TrunkMask
  342. t.bits[u shr IntShift] = t.bits[u shr IntShift] and not
  343. (uint(1) shl (u and IntMask))
  344. iterator elements(t: IntSet): int {.inline.} =
  345. # while traversing it is forbidden to change the set!
  346. for h in 0..high(t.data):
  347. var r = t.data[h]
  348. while r != nil:
  349. var i = 0
  350. while i <= high(r.bits):
  351. var w = r.bits[i] # taking a copy of r.bits[i] here is correct, because
  352. # modifying operations are not allowed during traversation
  353. var j = 0
  354. while w != 0: # test all remaining bits for zero
  355. if (w and 1) != 0: # the bit is set!
  356. yield (r.key shl TrunkShift) or (i shl IntShift +% j)
  357. inc(j)
  358. w = w shr 1
  359. inc(i)
  360. r = r.next
  361. proc isSmallChunk(c: PChunk): bool {.inline.} =
  362. result = c.size <= SmallChunkSize-smallChunkOverhead()
  363. proc chunkUnused(c: PChunk): bool {.inline.} =
  364. result = (c.prevSize and 1) == 0
  365. iterator allObjects(m: var MemRegion): pointer {.inline.} =
  366. m.locked = true
  367. for s in elements(m.chunkStarts):
  368. # we need to check here again as it could have been modified:
  369. if s in m.chunkStarts:
  370. let c = cast[PChunk](s shl PageShift)
  371. if not chunkUnused(c):
  372. if isSmallChunk(c):
  373. var c = cast[PSmallChunk](c)
  374. let size = c.size
  375. var a = cast[int](addr(c.data))
  376. let limit = a + c.acc
  377. while a <% limit:
  378. yield cast[pointer](a)
  379. a = a +% size
  380. else:
  381. let c = cast[PBigChunk](c)
  382. yield addr(c.data)
  383. m.locked = false
  384. proc iterToProc*(iter: typed, envType: typedesc; procName: untyped) {.
  385. magic: "Plugin", compileTime.}
  386. when not defined(gcDestructors):
  387. proc isCell(p: pointer): bool {.inline.} =
  388. result = cast[ptr FreeCell](p).zeroField >% 1
  389. # ------------- chunk management ----------------------------------------------
  390. proc pageIndex(c: PChunk): int {.inline.} =
  391. result = cast[int](c) shr PageShift
  392. proc pageIndex(p: pointer): int {.inline.} =
  393. result = cast[int](p) shr PageShift
  394. proc pageAddr(p: pointer): PChunk {.inline.} =
  395. result = cast[PChunk](cast[int](p) and not PageMask)
  396. #sysAssert(Contains(allocator.chunkStarts, pageIndex(result)))
  397. when false:
  398. proc writeFreeList(a: MemRegion) =
  399. var it = a.freeChunksList
  400. c_fprintf(stdout, "freeChunksList: %p\n", it)
  401. while it != nil:
  402. c_fprintf(stdout, "it: %p, next: %p, prev: %p, size: %ld\n",
  403. it, it.next, it.prev, it.size)
  404. it = it.next
  405. proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
  406. when not defined(emscripten):
  407. if not a.blockChunkSizeIncrease:
  408. let usedMem = a.occ #a.currMem # - a.freeMem
  409. if usedMem < 64 * 1024:
  410. a.nextChunkSize = PageSize*4
  411. else:
  412. a.nextChunkSize = min(roundup(usedMem shr 2, PageSize), a.nextChunkSize * 2)
  413. a.nextChunkSize = min(a.nextChunkSize, MaxBigChunkSize).int
  414. var size = size
  415. if size > a.nextChunkSize:
  416. result = cast[PBigChunk](allocPages(a, size))
  417. else:
  418. result = cast[PBigChunk](tryAllocPages(a, a.nextChunkSize))
  419. if result == nil:
  420. result = cast[PBigChunk](allocPages(a, size))
  421. a.blockChunkSizeIncrease = true
  422. else:
  423. size = a.nextChunkSize
  424. incCurrMem(a, size)
  425. inc(a.freeMem, size)
  426. let heapLink = a.addHeapLink(result, size)
  427. when defined(debugHeapLinks):
  428. cprintf("owner: %p; result: %p; next pointer %p; size: %ld\n", addr(a),
  429. result, heapLink, size)
  430. when defined(memtracker):
  431. trackLocation(addr result.size, sizeof(int))
  432. sysAssert((cast[int](result) and PageMask) == 0, "requestOsChunks 1")
  433. #zeroMem(result, size)
  434. result.next = nil
  435. result.prev = nil
  436. result.size = size
  437. # update next.prevSize:
  438. var nxt = cast[int](result) +% size
  439. sysAssert((nxt and PageMask) == 0, "requestOsChunks 2")
  440. var next = cast[PChunk](nxt)
  441. if pageIndex(next) in a.chunkStarts:
  442. #echo("Next already allocated!")
  443. next.prevSize = size or (next.prevSize and 1)
  444. # set result.prevSize:
  445. var lastSize = if a.lastSize != 0: a.lastSize else: PageSize
  446. var prv = cast[int](result) -% lastSize
  447. sysAssert((nxt and PageMask) == 0, "requestOsChunks 3")
  448. var prev = cast[PChunk](prv)
  449. if pageIndex(prev) in a.chunkStarts and prev.size == lastSize:
  450. #echo("Prev already allocated!")
  451. result.prevSize = lastSize or (result.prevSize and 1)
  452. else:
  453. result.prevSize = 0 or (result.prevSize and 1) # unknown
  454. # but do not overwrite 'used' field
  455. a.lastSize = size # for next request
  456. sysAssert((cast[int](result) and PageMask) == 0, "requestOschunks: unaligned chunk")
  457. proc isAccessible(a: MemRegion, p: pointer): bool {.inline.} =
  458. result = contains(a.chunkStarts, pageIndex(p))
  459. proc contains[T](list, x: T): bool =
  460. var it = list
  461. while it != nil:
  462. if it == x: return true
  463. it = it.next
  464. proc listAdd[T](head: var T, c: T) {.inline.} =
  465. sysAssert(c notin head, "listAdd 1")
  466. sysAssert c.prev == nil, "listAdd 2"
  467. sysAssert c.next == nil, "listAdd 3"
  468. c.next = head
  469. if head != nil:
  470. sysAssert head.prev == nil, "listAdd 4"
  471. head.prev = c
  472. head = c
  473. proc listRemove[T](head: var T, c: T) {.inline.} =
  474. sysAssert(c in head, "listRemove")
  475. if c == head:
  476. head = c.next
  477. sysAssert c.prev == nil, "listRemove 2"
  478. if head != nil: head.prev = nil
  479. else:
  480. sysAssert c.prev != nil, "listRemove 3"
  481. c.prev.next = c.next
  482. if c.next != nil: c.next.prev = c.prev
  483. c.next = nil
  484. c.prev = nil
  485. proc updatePrevSize(a: var MemRegion, c: PBigChunk,
  486. prevSize: int) {.inline.} =
  487. var ri = cast[PChunk](cast[int](c) +% c.size)
  488. sysAssert((cast[int](ri) and PageMask) == 0, "updatePrevSize")
  489. if isAccessible(a, ri):
  490. ri.prevSize = prevSize or (ri.prevSize and 1)
  491. proc splitChunk2(a: var MemRegion, c: PBigChunk, size: int): PBigChunk =
  492. result = cast[PBigChunk](cast[int](c) +% size)
  493. result.size = c.size - size
  494. track("result.size", addr result.size, sizeof(int))
  495. when not defined(nimOptimizedSplitChunk):
  496. # still active because of weird codegen issue on some of our CIs:
  497. result.next = nil
  498. result.prev = nil
  499. # size and not used:
  500. result.prevSize = size
  501. result.owner = addr a
  502. sysAssert((size and 1) == 0, "splitChunk 2")
  503. sysAssert((size and PageMask) == 0,
  504. "splitChunk: size is not a multiple of the PageSize")
  505. updatePrevSize(a, c, result.size)
  506. c.size = size
  507. incl(a, a.chunkStarts, pageIndex(result))
  508. proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) =
  509. let rest = splitChunk2(a, c, size)
  510. addChunkToMatrix(a, rest)
  511. proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
  512. var c = c
  513. sysAssert(c.size >= PageSize, "freeBigChunk")
  514. inc(a.freeMem, c.size)
  515. c.prevSize = c.prevSize and not 1 # set 'used' to false
  516. when coalescLeft:
  517. let prevSize = c.prevSize
  518. if prevSize != 0:
  519. var le = cast[PChunk](cast[int](c) -% prevSize)
  520. sysAssert((cast[int](le) and PageMask) == 0, "freeBigChunk 4")
  521. if isAccessible(a, le) and chunkUnused(le):
  522. sysAssert(not isSmallChunk(le), "freeBigChunk 5")
  523. if not isSmallChunk(le) and le.size < MaxBigChunkSize:
  524. removeChunkFromMatrix(a, cast[PBigChunk](le))
  525. inc(le.size, c.size)
  526. excl(a.chunkStarts, pageIndex(c))
  527. c = cast[PBigChunk](le)
  528. if c.size > MaxBigChunkSize:
  529. let rest = splitChunk2(a, c, MaxBigChunkSize)
  530. when defined(nimOptimizedSplitChunk):
  531. rest.next = nil
  532. rest.prev = nil
  533. addChunkToMatrix(a, c)
  534. c = rest
  535. when coalescRight:
  536. var ri = cast[PChunk](cast[int](c) +% c.size)
  537. sysAssert((cast[int](ri) and PageMask) == 0, "freeBigChunk 2")
  538. if isAccessible(a, ri) and chunkUnused(ri):
  539. sysAssert(not isSmallChunk(ri), "freeBigChunk 3")
  540. if not isSmallChunk(ri) and c.size < MaxBigChunkSize:
  541. removeChunkFromMatrix(a, cast[PBigChunk](ri))
  542. inc(c.size, ri.size)
  543. excl(a.chunkStarts, pageIndex(ri))
  544. if c.size > MaxBigChunkSize:
  545. let rest = splitChunk2(a, c, MaxBigChunkSize)
  546. addChunkToMatrix(a, rest)
  547. addChunkToMatrix(a, c)
  548. proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
  549. sysAssert(size > 0, "getBigChunk 2")
  550. var size = size # roundup(size, PageSize)
  551. var fl = 0
  552. var sl = 0
  553. mappingSearch(size, fl, sl)
  554. sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
  555. result = findSuitableBlock(a, fl, sl)
  556. when RegionHasLock:
  557. if not a.lockActive:
  558. a.lockActive = true
  559. initSysLock(a.lock)
  560. acquireSys a.lock
  561. if result == nil:
  562. if size < nimMinHeapPages * PageSize:
  563. result = requestOsChunks(a, nimMinHeapPages * PageSize)
  564. splitChunk(a, result, size)
  565. else:
  566. result = requestOsChunks(a, size)
  567. # if we over allocated split the chunk:
  568. if result.size > size:
  569. splitChunk(a, result, size)
  570. result.owner = addr a
  571. else:
  572. removeChunkFromMatrix2(a, result, fl, sl)
  573. if result.size >= size + PageSize:
  574. splitChunk(a, result, size)
  575. # set 'used' to to true:
  576. result.prevSize = 1
  577. track("setUsedToFalse", addr result.size, sizeof(int))
  578. sysAssert result.owner == addr a, "getBigChunk: No owner set!"
  579. incl(a, a.chunkStarts, pageIndex(result))
  580. dec(a.freeMem, size)
  581. when RegionHasLock:
  582. releaseSys a.lock
  583. proc getHugeChunk(a: var MemRegion; size: int): PBigChunk =
  584. result = cast[PBigChunk](allocPages(a, size))
  585. when RegionHasLock:
  586. if not a.lockActive:
  587. a.lockActive = true
  588. initSysLock(a.lock)
  589. acquireSys a.lock
  590. incCurrMem(a, size)
  591. # XXX add this to the heap links. But also remove it from it later.
  592. when false: a.addHeapLink(result, size)
  593. sysAssert((cast[int](result) and PageMask) == 0, "getHugeChunk")
  594. result.next = nil
  595. result.prev = nil
  596. result.size = size
  597. # set 'used' to to true:
  598. result.prevSize = 1
  599. result.owner = addr a
  600. incl(a, a.chunkStarts, pageIndex(result))
  601. when RegionHasLock:
  602. releaseSys a.lock
  603. proc freeHugeChunk(a: var MemRegion; c: PBigChunk) =
  604. let size = c.size
  605. sysAssert(size >= HugeChunkSize, "freeHugeChunk: invalid size")
  606. excl(a.chunkStarts, pageIndex(c))
  607. decCurrMem(a, size)
  608. osDeallocPages(c, size)
  609. proc getSmallChunk(a: var MemRegion): PSmallChunk =
  610. var res = getBigChunk(a, PageSize)
  611. sysAssert res.prev == nil, "getSmallChunk 1"
  612. sysAssert res.next == nil, "getSmallChunk 2"
  613. result = cast[PSmallChunk](res)
  614. # -----------------------------------------------------------------------------
  615. when not defined(gcDestructors):
  616. proc isAllocatedPtr(a: MemRegion, p: pointer): bool {.benign.}
  617. when true:
  618. template allocInv(a: MemRegion): bool = true
  619. else:
  620. proc allocInv(a: MemRegion): bool =
  621. ## checks some (not all yet) invariants of the allocator's data structures.
  622. for s in low(a.freeSmallChunks)..high(a.freeSmallChunks):
  623. var c = a.freeSmallChunks[s]
  624. while not (c == nil):
  625. if c.next == c:
  626. echo "[SYSASSERT] c.next == c"
  627. return false
  628. if not (c.size == s * MemAlign):
  629. echo "[SYSASSERT] c.size != s * MemAlign"
  630. return false
  631. var it = c.freeList
  632. while not (it == nil):
  633. if not (it.zeroField == 0):
  634. echo "[SYSASSERT] it.zeroField != 0"
  635. c_printf("%ld %p\n", it.zeroField, it)
  636. return false
  637. it = it.next
  638. c = c.next
  639. result = true
  640. when false:
  641. var
  642. rsizes: array[50_000, int]
  643. rsizesLen: int
  644. proc trackSize(size: int) =
  645. rsizes[rsizesLen] = size
  646. inc rsizesLen
  647. proc untrackSize(size: int) =
  648. for i in 0 .. rsizesLen-1:
  649. if rsizes[i] == size:
  650. rsizes[i] = rsizes[rsizesLen-1]
  651. dec rsizesLen
  652. return
  653. c_fprintf(stdout, "%ld\n", size)
  654. sysAssert(false, "untracked size!")
  655. else:
  656. template trackSize(x) = discard
  657. template untrackSize(x) = discard
  658. proc deallocBigChunk(a: var MemRegion, c: PBigChunk) =
  659. when RegionHasLock:
  660. acquireSys a.lock
  661. dec a.occ, c.size
  662. untrackSize(c.size)
  663. sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case B)"
  664. when not defined(gcDestructors):
  665. a.deleted = getBottom(a)
  666. del(a, a.root, cast[int](addr(c.data)))
  667. if c.size >= HugeChunkSize: freeHugeChunk(a, c)
  668. else: freeBigChunk(a, c)
  669. when RegionHasLock:
  670. releaseSys a.lock
  671. when defined(gcDestructors):
  672. template atomicPrepend(head, elem: untyped) =
  673. # see also https://en.cppreference.com/w/cpp/atomic/atomic_compare_exchange
  674. when hasThreadSupport:
  675. while true:
  676. elem.next.storea head.loada
  677. if atomicCompareExchangeN(addr head, addr elem.next, elem, weak = true, ATOMIC_RELEASE, ATOMIC_RELAXED):
  678. break
  679. else:
  680. elem.next.storea head.loada
  681. head.storea elem
  682. proc addToSharedFreeListBigChunks(a: var MemRegion; c: PBigChunk) {.inline.} =
  683. sysAssert c.next == nil, "c.next pointer must be nil"
  684. atomicPrepend a.sharedFreeListBigChunks, c
  685. proc addToSharedFreeList(c: PSmallChunk; f: ptr FreeCell; size: int) {.inline.} =
  686. atomicPrepend c.owner.sharedFreeLists[size], f
  687. const MaxSteps = 20
  688. proc compensateCounters(a: var MemRegion; c: PSmallChunk; size: int) =
  689. # rawDealloc did NOT do the usual:
  690. # `inc(c.free, size); dec(a.occ, size)` because it wasn't the owner of these
  691. # memory locations. We have to compensate here for these for the entire list.
  692. # Well, not for the entire list, but for `max` elements of the list because
  693. # we split the list in order to achieve bounded response times.
  694. var it = c.freeList
  695. var x = 0
  696. while it != nil:
  697. inc x, size
  698. let chunk = cast[PSmallChunk](pageAddr(it))
  699. inc(chunk.free, x)
  700. it = it.next
  701. dec(a.occ, x)
  702. proc freeDeferredObjects(a: var MemRegion; root: PBigChunk) =
  703. var it = root
  704. var maxIters = MaxSteps # make it time-bounded
  705. while true:
  706. let rest = it.next.loada
  707. it.next.storea nil
  708. deallocBigChunk(a, cast[PBigChunk](it))
  709. if maxIters == 0:
  710. if rest != nil:
  711. addToSharedFreeListBigChunks(a, rest)
  712. sysAssert a.sharedFreeListBigChunks != nil, "re-enqueing failed"
  713. break
  714. it = rest
  715. dec maxIters
  716. if it == nil: break
  717. proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
  718. when defined(nimTypeNames):
  719. inc(a.allocCounter)
  720. sysAssert(allocInv(a), "rawAlloc: begin")
  721. sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
  722. var size = roundup(requestedSize, MemAlign)
  723. sysAssert(size >= sizeof(FreeCell), "rawAlloc: requested size too small")
  724. sysAssert(size >= requestedSize, "insufficient allocated size!")
  725. #c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
  726. if size <= SmallChunkSize-smallChunkOverhead():
  727. # allocate a small block: for small chunks, we use only its next pointer
  728. let s = size div MemAlign
  729. var c = a.freeSmallChunks[s]
  730. if c == nil:
  731. c = getSmallChunk(a)
  732. c.freeList = nil
  733. sysAssert c.size == PageSize, "rawAlloc 3"
  734. c.size = size
  735. c.acc = size
  736. c.free = SmallChunkSize - smallChunkOverhead() - size
  737. sysAssert c.owner == addr(a), "rawAlloc: No owner set!"
  738. c.next = nil
  739. c.prev = nil
  740. listAdd(a.freeSmallChunks[s], c)
  741. result = addr(c.data)
  742. sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 4")
  743. else:
  744. sysAssert(allocInv(a), "rawAlloc: begin c != nil")
  745. sysAssert c.next != c, "rawAlloc 5"
  746. #if c.size != size:
  747. # c_fprintf(stdout, "csize: %lld; size %lld\n", c.size, size)
  748. sysAssert c.size == size, "rawAlloc 6"
  749. when defined(gcDestructors):
  750. if c.freeList == nil:
  751. when hasThreadSupport:
  752. # Steal the entire list from `sharedFreeList`:
  753. c.freeList = atomicExchangeN(addr a.sharedFreeLists[s], nil, ATOMIC_RELAXED)
  754. else:
  755. c.freeList = a.sharedFreeLists[s]
  756. a.sharedFreeLists[s] = nil
  757. compensateCounters(a, c, size)
  758. if c.freeList == nil:
  759. sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize,
  760. "rawAlloc 7")
  761. result = cast[pointer](cast[int](addr(c.data)) +% c.acc)
  762. inc(c.acc, size)
  763. else:
  764. result = c.freeList
  765. when not defined(gcDestructors):
  766. sysAssert(c.freeList.zeroField == 0, "rawAlloc 8")
  767. c.freeList = c.freeList.next
  768. dec(c.free, size)
  769. sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 9")
  770. sysAssert(allocInv(a), "rawAlloc: end c != nil")
  771. sysAssert(allocInv(a), "rawAlloc: before c.free < size")
  772. if c.free < size:
  773. sysAssert(allocInv(a), "rawAlloc: before listRemove test")
  774. listRemove(a.freeSmallChunks[s], c)
  775. sysAssert(allocInv(a), "rawAlloc: end listRemove test")
  776. sysAssert(((cast[int](result) and PageMask) - smallChunkOverhead()) %%
  777. size == 0, "rawAlloc 21")
  778. sysAssert(allocInv(a), "rawAlloc: end small size")
  779. inc a.occ, size
  780. trackSize(c.size)
  781. else:
  782. when defined(gcDestructors):
  783. when hasThreadSupport:
  784. let deferredFrees = atomicExchangeN(addr a.sharedFreeListBigChunks, nil, ATOMIC_RELAXED)
  785. else:
  786. let deferredFrees = a.sharedFreeListBigChunks
  787. a.sharedFreeListBigChunks = nil
  788. if deferredFrees != nil:
  789. freeDeferredObjects(a, deferredFrees)
  790. size = requestedSize + bigChunkOverhead() # roundup(requestedSize+bigChunkOverhead(), PageSize)
  791. # allocate a large block
  792. var c = if size >= HugeChunkSize: getHugeChunk(a, size)
  793. else: getBigChunk(a, size)
  794. sysAssert c.prev == nil, "rawAlloc 10"
  795. sysAssert c.next == nil, "rawAlloc 11"
  796. result = addr(c.data)
  797. sysAssert((cast[int](c) and (MemAlign-1)) == 0, "rawAlloc 13")
  798. sysAssert((cast[int](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
  799. when not defined(gcDestructors):
  800. if a.root == nil: a.root = getBottom(a)
  801. add(a, a.root, cast[int](result), cast[int](result)+%size)
  802. inc a.occ, c.size
  803. trackSize(c.size)
  804. sysAssert(isAccessible(a, result), "rawAlloc 14")
  805. sysAssert(allocInv(a), "rawAlloc: end")
  806. when logAlloc: cprintf("var pointer_%p = alloc(%ld)\n", result, requestedSize)
  807. proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
  808. result = rawAlloc(a, requestedSize)
  809. zeroMem(result, requestedSize)
  810. proc rawDealloc(a: var MemRegion, p: pointer) =
  811. when defined(nimTypeNames):
  812. inc(a.deallocCounter)
  813. #sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
  814. sysAssert(allocInv(a), "rawDealloc: begin")
  815. var c = pageAddr(p)
  816. sysAssert(c != nil, "rawDealloc: begin")
  817. if isSmallChunk(c):
  818. # `p` is within a small chunk:
  819. var c = cast[PSmallChunk](c)
  820. let s = c.size
  821. # ^ We might access thread foreign storage here.
  822. # The other thread cannot possibly free this block as it's still alive.
  823. var f = cast[ptr FreeCell](p)
  824. if c.owner == addr(a):
  825. # We own the block, there is no foreign thread involved.
  826. dec a.occ, s
  827. untrackSize(s)
  828. sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case A)"
  829. sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %%
  830. s == 0, "rawDealloc 3")
  831. when not defined(gcDestructors):
  832. #echo("setting to nil: ", $cast[int](addr(f.zeroField)))
  833. sysAssert(f.zeroField != 0, "rawDealloc 1")
  834. f.zeroField = 0
  835. f.next = c.freeList
  836. c.freeList = f
  837. when overwriteFree:
  838. # set to 0xff to check for usage after free bugs:
  839. nimSetMem(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
  840. s -% sizeof(FreeCell))
  841. # check if it is not in the freeSmallChunks[s] list:
  842. if c.free < s:
  843. # add it to the freeSmallChunks[s] array:
  844. listAdd(a.freeSmallChunks[s div MemAlign], c)
  845. inc(c.free, s)
  846. else:
  847. inc(c.free, s)
  848. if c.free == SmallChunkSize-smallChunkOverhead():
  849. listRemove(a.freeSmallChunks[s div MemAlign], c)
  850. c.size = SmallChunkSize
  851. freeBigChunk(a, cast[PBigChunk](c))
  852. else:
  853. when defined(gcDestructors):
  854. addToSharedFreeList(c, f, s div MemAlign)
  855. sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %%
  856. s == 0, "rawDealloc 2")
  857. else:
  858. # set to 0xff to check for usage after free bugs:
  859. when overwriteFree: nimSetMem(p, -1'i32, c.size -% bigChunkOverhead())
  860. when defined(gcDestructors):
  861. if c.owner == addr(a):
  862. deallocBigChunk(a, cast[PBigChunk](c))
  863. else:
  864. addToSharedFreeListBigChunks(c.owner[], cast[PBigChunk](c))
  865. else:
  866. deallocBigChunk(a, cast[PBigChunk](c))
  867. sysAssert(allocInv(a), "rawDealloc: end")
  868. when logAlloc: cprintf("dealloc(pointer_%p)\n", p)
  869. when not defined(gcDestructors):
  870. proc isAllocatedPtr(a: MemRegion, p: pointer): bool =
  871. if isAccessible(a, p):
  872. var c = pageAddr(p)
  873. if not chunkUnused(c):
  874. if isSmallChunk(c):
  875. var c = cast[PSmallChunk](c)
  876. var offset = (cast[int](p) and (PageSize-1)) -%
  877. smallChunkOverhead()
  878. result = (c.acc >% offset) and (offset %% c.size == 0) and
  879. (cast[ptr FreeCell](p).zeroField >% 1)
  880. else:
  881. var c = cast[PBigChunk](c)
  882. result = p == addr(c.data) and cast[ptr FreeCell](p).zeroField >% 1
  883. proc prepareForInteriorPointerChecking(a: var MemRegion) {.inline.} =
  884. a.minLargeObj = lowGauge(a.root)
  885. a.maxLargeObj = highGauge(a.root)
  886. proc interiorAllocatedPtr(a: MemRegion, p: pointer): pointer =
  887. if isAccessible(a, p):
  888. var c = pageAddr(p)
  889. if not chunkUnused(c):
  890. if isSmallChunk(c):
  891. var c = cast[PSmallChunk](c)
  892. var offset = (cast[int](p) and (PageSize-1)) -%
  893. smallChunkOverhead()
  894. if c.acc >% offset:
  895. sysAssert(cast[int](addr(c.data)) +% offset ==
  896. cast[int](p), "offset is not what you think it is")
  897. var d = cast[ptr FreeCell](cast[int](addr(c.data)) +%
  898. offset -% (offset %% c.size))
  899. if d.zeroField >% 1:
  900. result = d
  901. sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
  902. else:
  903. var c = cast[PBigChunk](c)
  904. var d = addr(c.data)
  905. if p >= d and cast[ptr FreeCell](d).zeroField >% 1:
  906. result = d
  907. sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
  908. else:
  909. var q = cast[int](p)
  910. if q >=% a.minLargeObj and q <=% a.maxLargeObj:
  911. # this check is highly effective! Test fails for 99,96% of all checks on
  912. # an x86-64.
  913. var avlNode = inRange(a.root, q)
  914. if avlNode != nil:
  915. var k = cast[pointer](avlNode.key)
  916. var c = cast[PBigChunk](pageAddr(k))
  917. sysAssert(addr(c.data) == k, " k is not the same as addr(c.data)!")
  918. if cast[ptr FreeCell](k).zeroField >% 1:
  919. result = k
  920. sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
  921. proc ptrSize(p: pointer): int =
  922. when not defined(gcDestructors):
  923. var x = cast[pointer](cast[int](p) -% sizeof(FreeCell))
  924. var c = pageAddr(p)
  925. sysAssert(not chunkUnused(c), "ptrSize")
  926. result = c.size -% sizeof(FreeCell)
  927. if not isSmallChunk(c):
  928. dec result, bigChunkOverhead()
  929. else:
  930. var c = pageAddr(p)
  931. sysAssert(not chunkUnused(c), "ptrSize")
  932. result = c.size
  933. if not isSmallChunk(c):
  934. dec result, bigChunkOverhead()
  935. proc alloc(allocator: var MemRegion, size: Natural): pointer {.gcsafe.} =
  936. when not defined(gcDestructors):
  937. result = rawAlloc(allocator, size+sizeof(FreeCell))
  938. cast[ptr FreeCell](result).zeroField = 1 # mark it as used
  939. sysAssert(not isAllocatedPtr(allocator, result), "alloc")
  940. result = cast[pointer](cast[int](result) +% sizeof(FreeCell))
  941. track("alloc", result, size)
  942. else:
  943. result = rawAlloc(allocator, size)
  944. proc alloc0(allocator: var MemRegion, size: Natural): pointer =
  945. result = alloc(allocator, size)
  946. zeroMem(result, size)
  947. proc dealloc(allocator: var MemRegion, p: pointer) =
  948. when not defined(gcDestructors):
  949. sysAssert(p != nil, "dealloc: p is nil")
  950. var x = cast[pointer](cast[int](p) -% sizeof(FreeCell))
  951. sysAssert(x != nil, "dealloc: x is nil")
  952. sysAssert(isAccessible(allocator, x), "is not accessible")
  953. sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc: object header corrupted")
  954. rawDealloc(allocator, x)
  955. sysAssert(not isAllocatedPtr(allocator, x), "dealloc: object still accessible")
  956. track("dealloc", p, 0)
  957. else:
  958. rawDealloc(allocator, p)
  959. proc realloc(allocator: var MemRegion, p: pointer, newsize: Natural): pointer =
  960. if newsize > 0:
  961. result = alloc(allocator, newsize)
  962. if p != nil:
  963. copyMem(result, p, min(ptrSize(p), newsize))
  964. dealloc(allocator, p)
  965. elif p != nil:
  966. dealloc(allocator, p)
  967. proc realloc0(allocator: var MemRegion, p: pointer, oldsize, newsize: Natural): pointer =
  968. result = realloc(allocator, p, newsize)
  969. if newsize > oldsize:
  970. zeroMem(cast[pointer](cast[uint](result) + uint(oldsize)), newsize - oldsize)
  971. proc deallocOsPages(a: var MemRegion) =
  972. # we free every 'ordinarily' allocated page by iterating over the page bits:
  973. var it = addr(a.heapLinks)
  974. while true:
  975. let next = it.next
  976. for i in 0..it.len-1:
  977. let (p, size) = it.chunks[i]
  978. when defined(debugHeapLinks):
  979. cprintf("owner %p; dealloc A: %p size: %ld; next: %p\n", addr(a),
  980. it, size, next)
  981. sysAssert size >= PageSize, "origSize too small"
  982. osDeallocPages(p, size)
  983. it = next
  984. if it == nil: break
  985. # And then we free the pages that are in use for the page bits:
  986. llDeallocAll(a)
  987. proc getFreeMem(a: MemRegion): int {.inline.} = result = a.freeMem
  988. proc getTotalMem(a: MemRegion): int {.inline.} = result = a.currMem
  989. proc getOccupiedMem(a: MemRegion): int {.inline.} =
  990. result = a.occ
  991. # a.currMem - a.freeMem
  992. when defined(nimTypeNames):
  993. proc getMemCounters(a: MemRegion): (int, int) {.inline.} =
  994. (a.allocCounter, a.deallocCounter)
  995. # ---------------------- thread memory region -------------------------------
  996. template instantiateForRegion(allocator: untyped) {.dirty.} =
  997. {.push stackTrace: off.}
  998. when defined(nimFulldebug):
  999. proc interiorAllocatedPtr*(p: pointer): pointer =
  1000. result = interiorAllocatedPtr(allocator, p)
  1001. proc isAllocatedPtr*(p: pointer): bool =
  1002. let p = cast[pointer](cast[int](p)-%ByteAddress(sizeof(Cell)))
  1003. result = isAllocatedPtr(allocator, p)
  1004. proc deallocOsPages = deallocOsPages(allocator)
  1005. proc allocImpl(size: Natural): pointer =
  1006. result = alloc(allocator, size)
  1007. proc alloc0Impl(size: Natural): pointer =
  1008. result = alloc0(allocator, size)
  1009. proc deallocImpl(p: pointer) =
  1010. dealloc(allocator, p)
  1011. proc reallocImpl(p: pointer, newSize: Natural): pointer =
  1012. result = realloc(allocator, p, newSize)
  1013. proc realloc0Impl(p: pointer, oldSize, newSize: Natural): pointer =
  1014. result = realloc(allocator, p, newSize)
  1015. if newSize > oldSize:
  1016. zeroMem(cast[pointer](cast[uint](result) + uint(oldSize)), newSize - oldSize)
  1017. when false:
  1018. proc countFreeMem(): int =
  1019. # only used for assertions
  1020. var it = allocator.freeChunksList
  1021. while it != nil:
  1022. inc(result, it.size)
  1023. it = it.next
  1024. when hasThreadSupport and not defined(gcDestructors):
  1025. proc addSysExitProc(quitProc: proc() {.noconv.}) {.importc: "atexit", header: "<stdlib.h>".}
  1026. var sharedHeap: MemRegion
  1027. var heapLock: SysLock
  1028. initSysLock(heapLock)
  1029. addSysExitProc(proc() {.noconv.} = deinitSys(heapLock))
  1030. proc getFreeMem(): int =
  1031. #sysAssert(result == countFreeMem())
  1032. result = allocator.freeMem
  1033. proc getTotalMem(): int =
  1034. result = allocator.currMem
  1035. proc getOccupiedMem(): int =
  1036. result = allocator.occ #getTotalMem() - getFreeMem()
  1037. proc getMaxMem*(): int =
  1038. result = getMaxMem(allocator)
  1039. when defined(nimTypeNames):
  1040. proc getMemCounters*(): (int, int) = getMemCounters(allocator)
  1041. # -------------------- shared heap region ----------------------------------
  1042. proc allocSharedImpl(size: Natural): pointer =
  1043. when hasThreadSupport and not defined(gcDestructors):
  1044. acquireSys(heapLock)
  1045. result = alloc(sharedHeap, size)
  1046. releaseSys(heapLock)
  1047. else:
  1048. result = allocImpl(size)
  1049. proc allocShared0Impl(size: Natural): pointer =
  1050. result = allocSharedImpl(size)
  1051. zeroMem(result, size)
  1052. proc deallocSharedImpl(p: pointer) =
  1053. when hasThreadSupport and not defined(gcDestructors):
  1054. acquireSys(heapLock)
  1055. dealloc(sharedHeap, p)
  1056. releaseSys(heapLock)
  1057. else:
  1058. deallocImpl(p)
  1059. proc reallocSharedImpl(p: pointer, newSize: Natural): pointer =
  1060. when hasThreadSupport and not defined(gcDestructors):
  1061. acquireSys(heapLock)
  1062. result = realloc(sharedHeap, p, newSize)
  1063. releaseSys(heapLock)
  1064. else:
  1065. result = reallocImpl(p, newSize)
  1066. proc reallocShared0Impl(p: pointer, oldSize, newSize: Natural): pointer =
  1067. when hasThreadSupport and not defined(gcDestructors):
  1068. acquireSys(heapLock)
  1069. result = realloc0(sharedHeap, p, oldSize, newSize)
  1070. releaseSys(heapLock)
  1071. else:
  1072. result = realloc0Impl(p, oldSize, newSize)
  1073. when hasThreadSupport:
  1074. when defined(gcDestructors):
  1075. proc getFreeSharedMem(): int =
  1076. allocator.freeMem
  1077. proc getTotalSharedMem(): int =
  1078. allocator.currMem
  1079. proc getOccupiedSharedMem(): int =
  1080. allocator.occ
  1081. else:
  1082. template sharedMemStatsShared(v: int) =
  1083. acquireSys(heapLock)
  1084. result = v
  1085. releaseSys(heapLock)
  1086. proc getFreeSharedMem(): int =
  1087. sharedMemStatsShared(sharedHeap.freeMem)
  1088. proc getTotalSharedMem(): int =
  1089. sharedMemStatsShared(sharedHeap.currMem)
  1090. proc getOccupiedSharedMem(): int =
  1091. sharedMemStatsShared(sharedHeap.occ)
  1092. #sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem)
  1093. {.pop.}
  1094. {.pop.}