memalloc.nim 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. when notJSnotNims:
  2. proc zeroMem*(p: pointer, size: Natural) {.inline, noSideEffect,
  3. tags: [], raises: [].}
  4. ## Overwrites the contents of the memory at `p` with the value 0.
  5. ##
  6. ## Exactly `size` bytes will be overwritten. Like any procedure
  7. ## dealing with raw memory this is **unsafe**.
  8. proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign,
  9. tags: [], raises: [].}
  10. ## Copies the contents from the memory at `source` to the memory
  11. ## at `dest`.
  12. ## Exactly `size` bytes will be copied. The memory
  13. ## regions may not overlap. Like any procedure dealing with raw
  14. ## memory this is **unsafe**.
  15. proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign,
  16. tags: [], raises: [].}
  17. ## Copies the contents from the memory at `source` to the memory
  18. ## at `dest`.
  19. ##
  20. ## Exactly `size` bytes will be copied. The memory
  21. ## regions may overlap, `moveMem` handles this case appropriately
  22. ## and is thus somewhat more safe than `copyMem`. Like any procedure
  23. ## dealing with raw memory this is still **unsafe**, though.
  24. proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect,
  25. tags: [], raises: [].}
  26. ## Compares the memory blocks `a` and `b`. `size` bytes will
  27. ## be compared.
  28. ##
  29. ## If the blocks are equal, `true` is returned, `false`
  30. ## otherwise. Like any procedure dealing with raw memory this is
  31. ## **unsafe**.
  32. proc cmpMem*(a, b: pointer, size: Natural): int {.inline, noSideEffect,
  33. tags: [], raises: [].}
  34. ## Compares the memory blocks `a` and `b`. `size` bytes will
  35. ## be compared.
  36. ##
  37. ## Returns:
  38. ## * a value less than zero, if `a < b`
  39. ## * a value greater than zero, if `a > b`
  40. ## * zero, if `a == b`
  41. ##
  42. ## Like any procedure dealing with raw memory this is
  43. ## **unsafe**.
  44. when hasAlloc and not defined(js):
  45. proc allocImpl*(size: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
  46. proc alloc0Impl*(size: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
  47. proc deallocImpl*(p: pointer) {.noconv, rtl, tags: [], benign, raises: [].}
  48. proc reallocImpl*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
  49. proc realloc0Impl*(p: pointer, oldSize, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
  50. proc allocSharedImpl*(size: Natural): pointer {.noconv, compilerproc, rtl, benign, raises: [], tags: [].}
  51. proc allocShared0Impl*(size: Natural): pointer {.noconv, rtl, benign, raises: [], tags: [].}
  52. proc deallocSharedImpl*(p: pointer) {.noconv, rtl, benign, raises: [], tags: [].}
  53. proc reallocSharedImpl*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
  54. proc reallocShared0Impl*(p: pointer, oldSize, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
  55. # Allocator statistics for memory leak tests
  56. {.push stackTrace: off.}
  57. type AllocStats* = object
  58. allocCount: int
  59. deallocCount: int
  60. proc `-`*(a, b: AllocStats): AllocStats =
  61. result.allocCount = a.allocCount - b.allocCount
  62. result.deallocCount = a.deallocCount - b.deallocCount
  63. template dumpAllocstats*(code: untyped) =
  64. let stats1 = getAllocStats()
  65. code
  66. let stats2 = getAllocStats()
  67. echo $(stats2 - stats1)
  68. when defined(nimAllocStats):
  69. var stats: AllocStats
  70. template incStat(what: untyped) = atomicInc stats.what
  71. proc getAllocStats*(): AllocStats = stats
  72. else:
  73. template incStat(what: untyped) = discard
  74. proc getAllocStats*(): AllocStats = discard
  75. template alloc*(size: Natural): pointer =
  76. ## Allocates a new memory block with at least `size` bytes.
  77. ##
  78. ## The block has to be freed with `realloc(block, 0) <#realloc.t,pointer,Natural>`_
  79. ## or `dealloc(block) <#dealloc,pointer>`_.
  80. ## The block is not initialized, so reading
  81. ## from it before writing to it is undefined behaviour!
  82. ##
  83. ## The allocated memory belongs to its allocating thread!
  84. ## Use `allocShared <#allocShared.t,Natural>`_ to allocate from a shared heap.
  85. ##
  86. ## See also:
  87. ## * `alloc0 <#alloc0.t,Natural>`_
  88. incStat(allocCount)
  89. allocImpl(size)
  90. proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline, benign, raises: [].} =
  91. ## Allocates a new memory block with at least `T.sizeof * size` bytes.
  92. ##
  93. ## The block has to be freed with `resize(block, 0) <#resize,ptr.T,Natural>`_
  94. ## or `dealloc(block) <#dealloc,pointer>`_.
  95. ## The block is not initialized, so reading
  96. ## from it before writing to it is undefined behaviour!
  97. ##
  98. ## The allocated memory belongs to its allocating thread!
  99. ## Use `createSharedU <#createSharedU,typedesc>`_ to allocate from a shared heap.
  100. ##
  101. ## See also:
  102. ## * `create <#create,typedesc>`_
  103. cast[ptr T](alloc(T.sizeof * size))
  104. template alloc0*(size: Natural): pointer =
  105. ## Allocates a new memory block with at least `size` bytes.
  106. ##
  107. ## The block has to be freed with `realloc(block, 0) <#realloc.t,pointer,Natural>`_
  108. ## or `dealloc(block) <#dealloc,pointer>`_.
  109. ## The block is initialized with all bytes containing zero, so it is
  110. ## somewhat safer than `alloc <#alloc.t,Natural>`_.
  111. ##
  112. ## The allocated memory belongs to its allocating thread!
  113. ## Use `allocShared0 <#allocShared0.t,Natural>`_ to allocate from a shared heap.
  114. incStat(allocCount)
  115. alloc0Impl(size)
  116. proc create*(T: typedesc, size = 1.Positive): ptr T {.inline, benign, raises: [].} =
  117. ## Allocates a new memory block with at least `T.sizeof * size` bytes.
  118. ##
  119. ## The block has to be freed with `resize(block, 0) <#resize,ptr.T,Natural>`_
  120. ## or `dealloc(block) <#dealloc,pointer>`_.
  121. ## The block is initialized with all bytes containing zero, so it is
  122. ## somewhat safer than `createU <#createU,typedesc>`_.
  123. ##
  124. ## The allocated memory belongs to its allocating thread!
  125. ## Use `createShared <#createShared,typedesc>`_ to allocate from a shared heap.
  126. cast[ptr T](alloc0(sizeof(T) * size))
  127. template realloc*(p: pointer, newSize: Natural): pointer =
  128. ## Grows or shrinks a given memory block.
  129. ##
  130. ## If `p` is **nil** then a new memory block is returned.
  131. ## In either way the block has at least `newSize` bytes.
  132. ## If `newSize == 0` and `p` is not **nil** `realloc` calls `dealloc(p)`.
  133. ## In other cases the block has to be freed with
  134. ## `dealloc(block) <#dealloc,pointer>`_.
  135. ##
  136. ## The allocated memory belongs to its allocating thread!
  137. ## Use `reallocShared <#reallocShared.t,pointer,Natural>`_ to reallocate
  138. ## from a shared heap.
  139. reallocImpl(p, newSize)
  140. template realloc0*(p: pointer, oldSize, newSize: Natural): pointer =
  141. ## Grows or shrinks a given memory block.
  142. ##
  143. ## If `p` is **nil** then a new memory block is returned.
  144. ## In either way the block has at least `newSize` bytes.
  145. ## If `newSize == 0` and `p` is not **nil** `realloc` calls `dealloc(p)`.
  146. ## In other cases the block has to be freed with
  147. ## `dealloc(block) <#dealloc,pointer>`_.
  148. ##
  149. ## The block is initialized with all bytes containing zero, so it is
  150. ## somewhat safer then realloc
  151. ##
  152. ## The allocated memory belongs to its allocating thread!
  153. ## Use `reallocShared <#reallocShared.t,pointer,Natural>`_ to reallocate
  154. ## from a shared heap.
  155. realloc0Impl(p, oldSize, newSize)
  156. proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline, benign, raises: [].} =
  157. ## Grows or shrinks a given memory block.
  158. ##
  159. ## If `p` is **nil** then a new memory block is returned.
  160. ## In either way the block has at least `T.sizeof * newSize` bytes.
  161. ## If `newSize == 0` and `p` is not **nil** `resize` calls `dealloc(p)`.
  162. ## In other cases the block has to be freed with `free`.
  163. ##
  164. ## The allocated memory belongs to its allocating thread!
  165. ## Use `resizeShared <#resizeShared,ptr.T,Natural>`_ to reallocate
  166. ## from a shared heap.
  167. cast[ptr T](realloc(p, T.sizeof * newSize))
  168. proc dealloc*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} =
  169. ## Frees the memory allocated with `alloc`, `alloc0`,
  170. ## `realloc`, `create` or `createU`.
  171. ##
  172. ## **This procedure is dangerous!**
  173. ## If one forgets to free the memory a leak occurs; if one tries to
  174. ## access freed memory (or just freeing it twice!) a core dump may happen
  175. ## or other memory may be corrupted.
  176. ##
  177. ## The freed memory must belong to its allocating thread!
  178. ## Use `deallocShared <#deallocShared,pointer>`_ to deallocate from a shared heap.
  179. incStat(deallocCount)
  180. deallocImpl(p)
  181. template allocShared*(size: Natural): pointer =
  182. ## Allocates a new memory block on the shared heap with at
  183. ## least `size` bytes.
  184. ##
  185. ## The block has to be freed with
  186. ## `reallocShared(block, 0) <#reallocShared.t,pointer,Natural>`_
  187. ## or `deallocShared(block) <#deallocShared,pointer>`_.
  188. ##
  189. ## The block is not initialized, so reading from it before writing
  190. ## to it is undefined behaviour!
  191. ##
  192. ## See also:
  193. ## * `allocShared0 <#allocShared0.t,Natural>`_.
  194. incStat(allocCount)
  195. allocSharedImpl(size)
  196. proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline, tags: [],
  197. benign, raises: [].} =
  198. ## Allocates a new memory block on the shared heap with at
  199. ## least `T.sizeof * size` bytes.
  200. ##
  201. ## The block has to be freed with
  202. ## `resizeShared(block, 0) <#resizeShared,ptr.T,Natural>`_ or
  203. ## `freeShared(block) <#freeShared,ptr.T>`_.
  204. ##
  205. ## The block is not initialized, so reading from it before writing
  206. ## to it is undefined behaviour!
  207. ##
  208. ## See also:
  209. ## * `createShared <#createShared,typedesc>`_
  210. cast[ptr T](allocShared(T.sizeof * size))
  211. template allocShared0*(size: Natural): pointer =
  212. ## Allocates a new memory block on the shared heap with at
  213. ## least `size` bytes.
  214. ##
  215. ## The block has to be freed with
  216. ## `reallocShared(block, 0) <#reallocShared.t,pointer,Natural>`_
  217. ## or `deallocShared(block) <#deallocShared,pointer>`_.
  218. ##
  219. ## The block is initialized with all bytes
  220. ## containing zero, so it is somewhat safer than
  221. ## `allocShared <#allocShared.t,Natural>`_.
  222. incStat(allocCount)
  223. allocShared0Impl(size)
  224. proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
  225. ## Allocates a new memory block on the shared heap with at
  226. ## least `T.sizeof * size` bytes.
  227. ##
  228. ## The block has to be freed with
  229. ## `resizeShared(block, 0) <#resizeShared,ptr.T,Natural>`_ or
  230. ## `freeShared(block) <#freeShared,ptr.T>`_.
  231. ##
  232. ## The block is initialized with all bytes
  233. ## containing zero, so it is somewhat safer than
  234. ## `createSharedU <#createSharedU,typedesc>`_.
  235. cast[ptr T](allocShared0(T.sizeof * size))
  236. template reallocShared*(p: pointer, newSize: Natural): pointer =
  237. ## Grows or shrinks a given memory block on the heap.
  238. ##
  239. ## If `p` is **nil** then a new memory block is returned.
  240. ## In either way the block has at least `newSize` bytes.
  241. ## If `newSize == 0` and `p` is not **nil** `reallocShared` calls
  242. ## `deallocShared(p)`.
  243. ## In other cases the block has to be freed with
  244. ## `deallocShared <#deallocShared,pointer>`_.
  245. reallocSharedImpl(p, newSize)
  246. template reallocShared0*(p: pointer, oldSize, newSize: Natural): pointer =
  247. ## Grows or shrinks a given memory block on the heap.
  248. ##
  249. ## When growing, the new bytes of the block is initialized with all bytes
  250. ## containing zero, so it is somewhat safer then reallocShared
  251. ##
  252. ## If `p` is **nil** then a new memory block is returned.
  253. ## In either way the block has at least `newSize` bytes.
  254. ## If `newSize == 0` and `p` is not **nil** `reallocShared` calls
  255. ## `deallocShared(p)`.
  256. ## In other cases the block has to be freed with
  257. ## `deallocShared <#deallocShared,pointer>`_.
  258. reallocShared0Impl(p, oldSize, newSize)
  259. proc resizeShared*[T](p: ptr T, newSize: Natural): ptr T {.inline, raises: [].} =
  260. ## Grows or shrinks a given memory block on the heap.
  261. ##
  262. ## If `p` is **nil** then a new memory block is returned.
  263. ## In either way the block has at least `T.sizeof * newSize` bytes.
  264. ## If `newSize == 0` and `p` is not **nil** `resizeShared` calls
  265. ## `freeShared(p)`.
  266. ## In other cases the block has to be freed with
  267. ## `freeShared <#freeShared,ptr.T>`_.
  268. cast[ptr T](reallocShared(p, T.sizeof * newSize))
  269. proc deallocShared*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} =
  270. ## Frees the memory allocated with `allocShared`, `allocShared0` or
  271. ## `reallocShared`.
  272. ##
  273. ## **This procedure is dangerous!**
  274. ## If one forgets to free the memory a leak occurs; if one tries to
  275. ## access freed memory (or just freeing it twice!) a core dump may happen
  276. ## or other memory may be corrupted.
  277. incStat(deallocCount)
  278. deallocSharedImpl(p)
  279. proc freeShared*[T](p: ptr T) {.inline, benign, raises: [].} =
  280. ## Frees the memory allocated with `createShared`, `createSharedU` or
  281. ## `resizeShared`.
  282. ##
  283. ## **This procedure is dangerous!**
  284. ## If one forgets to free the memory a leak occurs; if one tries to
  285. ## access freed memory (or just freeing it twice!) a core dump may happen
  286. ## or other memory may be corrupted.
  287. deallocShared(p)
  288. include bitmasks
  289. template `+!`(p: pointer, s: SomeInteger): pointer =
  290. cast[pointer](cast[int](p) +% int(s))
  291. template `-!`(p: pointer, s: SomeInteger): pointer =
  292. cast[pointer](cast[int](p) -% int(s))
  293. proc alignedAlloc(size, align: Natural): pointer =
  294. if align <= MemAlign:
  295. when compileOption("threads"):
  296. result = allocShared(size)
  297. else:
  298. result = alloc(size)
  299. else:
  300. # allocate (size + align - 1) necessary for alignment,
  301. # plus 2 bytes to store offset
  302. when compileOption("threads"):
  303. let base = allocShared(size + align - 1 + sizeof(uint16))
  304. else:
  305. let base = alloc(size + align - 1 + sizeof(uint16))
  306. # memory layout: padding + offset (2 bytes) + user_data
  307. # in order to deallocate: read offset at user_data - 2 bytes,
  308. # then deallocate user_data - offset
  309. let offset = align - (cast[int](base) and (align - 1))
  310. cast[ptr uint16](base +! (offset - sizeof(uint16)))[] = uint16(offset)
  311. result = base +! offset
  312. proc alignedAlloc0(size, align: Natural): pointer =
  313. if align <= MemAlign:
  314. when compileOption("threads"):
  315. result = allocShared0(size)
  316. else:
  317. result = alloc0(size)
  318. else:
  319. # see comments for alignedAlloc
  320. when compileOption("threads"):
  321. let base = allocShared0(size + align - 1 + sizeof(uint16))
  322. else:
  323. let base = alloc0(size + align - 1 + sizeof(uint16))
  324. let offset = align - (cast[int](base) and (align - 1))
  325. cast[ptr uint16](base +! (offset - sizeof(uint16)))[] = uint16(offset)
  326. result = base +! offset
  327. proc alignedDealloc(p: pointer, align: int) {.compilerproc.} =
  328. if align <= MemAlign:
  329. when compileOption("threads"):
  330. deallocShared(p)
  331. else:
  332. dealloc(p)
  333. else:
  334. # read offset at p - 2 bytes, then deallocate (p - offset) pointer
  335. let offset = cast[ptr uint16](p -! sizeof(uint16))[]
  336. when compileOption("threads"):
  337. deallocShared(p -! offset)
  338. else:
  339. dealloc(p -! offset)
  340. proc alignedRealloc(p: pointer, oldSize, newSize, align: Natural): pointer =
  341. if align <= MemAlign:
  342. when compileOption("threads"):
  343. result = reallocShared(p, newSize)
  344. else:
  345. result = realloc(p, newSize)
  346. else:
  347. result = alignedAlloc(newSize, align)
  348. copyMem(result, p, oldSize)
  349. alignedDealloc(p, align)
  350. proc alignedRealloc0(p: pointer, oldSize, newSize, align: Natural): pointer =
  351. if align <= MemAlign:
  352. when compileOption("threads"):
  353. result = reallocShared0(p, oldSize, newSize)
  354. else:
  355. result = realloc0(p, oldSize, newSize)
  356. else:
  357. result = alignedAlloc(newSize, align)
  358. copyMem(result, p, oldSize)
  359. zeroMem(result +! oldSize, newSize - oldSize)
  360. alignedDealloc(p, align)
  361. {.pop.}
  362. # GC interface:
  363. when hasAlloc:
  364. proc getOccupiedMem*(): int {.rtl.}
  365. ## Returns the number of bytes that are owned by the process and hold data.
  366. proc getFreeMem*(): int {.rtl.}
  367. ## Returns the number of bytes that are owned by the process, but do not
  368. ## hold any meaningful data.
  369. proc getTotalMem*(): int {.rtl.}
  370. ## Returns the number of bytes that are owned by the process.
  371. when defined(js):
  372. # Stubs:
  373. proc getOccupiedMem(): int = return -1
  374. proc getFreeMem(): int = return -1
  375. proc getTotalMem(): int = return -1
  376. proc dealloc(p: pointer) = discard
  377. proc alloc(size: Natural): pointer = discard
  378. proc alloc0(size: Natural): pointer = discard
  379. proc realloc(p: pointer, newsize: Natural): pointer = discard
  380. proc realloc0(p: pointer, oldsize, newsize: Natural): pointer = discard
  381. proc allocShared(size: Natural): pointer = discard
  382. proc allocShared0(size: Natural): pointer = discard
  383. proc deallocShared(p: pointer) = discard
  384. proc reallocShared(p: pointer, newsize: Natural): pointer = discard
  385. proc reallocShared0(p: pointer, oldsize, newsize: Natural): pointer = discard
  386. when hasAlloc and hasThreadSupport and not defined(useMalloc):
  387. proc getOccupiedSharedMem*(): int {.rtl.}
  388. ## Returns the number of bytes that are owned by the process
  389. ## on the shared heap and hold data. This is only available when
  390. ## threads are enabled.
  391. proc getFreeSharedMem*(): int {.rtl.}
  392. ## Returns the number of bytes that are owned by the
  393. ## process on the shared heap, but do not hold any meaningful data.
  394. ## This is only available when threads are enabled.
  395. proc getTotalSharedMem*(): int {.rtl.}
  396. ## Returns the number of bytes on the shared heap that are owned by the
  397. ## process. This is only available when threads are enabled.