memalloc.nim 18 KB

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