You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mem_pool.h 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. /*
  2. * Copyright 2023 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /**
  17. * @file mem_pool.h
  18. * \brief Memory pools library.
  19. *
  20. * Memory pools library. Library is designed to implement efficient way to
  21. * store data in memory avoiding calling of many malloc/free. It has overhead
  22. * because of fact that objects live in pool for rather long time and are not freed
  23. * immediately after use, but if we know certainly when these objects can be used, we
  24. * can use pool for them
  25. */
  26. #ifndef RSPAMD_MEM_POOL_H
  27. #define RSPAMD_MEM_POOL_H
  28. #include "config.h"
  29. #if defined(HAVE_PTHREAD_PROCESS_SHARED) && !defined(DISABLE_PTHREAD_MUTEX)
  30. #include <pthread.h>
  31. #endif
  32. #ifdef __cplusplus
  33. #define MEMPOOL_STR_FUNC __FUNCTION__
  34. #else
  35. #define MEMPOOL_STR_FUNC G_STRFUNC
  36. #endif
  37. #ifdef __cplusplus
  38. extern "C" {
  39. #endif
  40. struct f_str_s;
  41. #ifdef __has_attribute
  42. #if __has_attribute(alloc_size)
  43. #define RSPAMD_ATTR_ALLOC_SIZE(pos) __attribute__((alloc_size(pos)))
  44. #else
  45. #define RSPAMD_ATTR_ALLOC_SIZE(pos)
  46. #endif
  47. #if __has_attribute(assume_aligned)
  48. #define RSPAMD_ATTR_ALLOC_ALIGN(al) __attribute__((assume_aligned(al)))
  49. #else
  50. #define RSPAMD_ATTR_ALLOC_ALIGN(al)
  51. #endif
  52. #if __has_attribute(returns_nonnull)
  53. #define RSPAMD_ATTR_RETURNS_NONNUL __attribute__((returns_nonnull))
  54. #else
  55. #define RSPAMD_ATTR_RETURNS_NONNUL
  56. #endif
  57. #else
  58. #define RSPAMD_ATTR_ALLOC_SIZE(pos)
  59. #define RSPAMD_ATTR_ALLOC_ALIGN(al)
  60. #define RSPAMD_ATTR_RETURNS_NONNUL
  61. #endif
  62. #define MEMPOOL_TAG_LEN 16
  63. #define MEMPOOL_UID_LEN 16
  64. /* All pointers are aligned as this variable */
  65. #define MIN_MEM_ALIGNMENT G_MEM_ALIGN
  66. /**
  67. * Destructor type definition
  68. */
  69. typedef void (*rspamd_mempool_destruct_t)(void *ptr);
  70. /**
  71. * Pool mutex structure
  72. */
  73. #if !defined(HAVE_PTHREAD_PROCESS_SHARED) || defined(DISABLE_PTHREAD_MUTEX)
  74. typedef struct memory_pool_mutex_s {
  75. int lock;
  76. pid_t owner;
  77. unsigned int spin;
  78. } rspamd_mempool_mutex_t;
  79. /**
  80. * Rwlock for locking shared memory regions
  81. */
  82. typedef struct memory_pool_rwlock_s {
  83. rspamd_mempool_mutex_t *__r_lock; /**< read mutex (private) */
  84. rspamd_mempool_mutex_t *__w_lock; /**< write mutex (private) */
  85. } rspamd_mempool_rwlock_t;
  86. #else
  87. typedef pthread_mutex_t rspamd_mempool_mutex_t;
  88. typedef pthread_rwlock_t rspamd_mempool_rwlock_t;
  89. #endif
  90. /**
  91. * Tag to use for logging purposes
  92. */
  93. struct rspamd_mempool_tag {
  94. char tagname[MEMPOOL_TAG_LEN]; /**< readable name */
  95. char uid[MEMPOOL_UID_LEN]; /**< unique id */
  96. };
  97. enum rspamd_mempool_flags {
  98. RSPAMD_MEMPOOL_DEBUG = (1u << 0u),
  99. };
  100. /**
  101. * Memory pool type
  102. */
  103. struct rspamd_mempool_entry_point;
  104. struct rspamd_mutex_s;
  105. struct rspamd_mempool_specific;
  106. typedef struct memory_pool_s {
  107. struct rspamd_mempool_specific *priv;
  108. struct rspamd_mempool_tag tag; /**< memory pool tag */
  109. } rspamd_mempool_t;
  110. /**
  111. * Statistics structure
  112. */
  113. typedef struct memory_pool_stat_s {
  114. unsigned int pools_allocated; /**< total number of allocated pools */
  115. unsigned int pools_freed; /**< number of freed pools */
  116. unsigned int bytes_allocated; /**< bytes that are allocated with pool allocator */
  117. unsigned int chunks_allocated; /**< number of chunks that are allocated */
  118. unsigned int shared_chunks_allocated; /**< shared chunks allocated */
  119. unsigned int chunks_freed; /**< chunks freed */
  120. unsigned int oversized_chunks; /**< oversized chunks */
  121. unsigned int fragmented_size; /**< fragmentation size */
  122. } rspamd_mempool_stat_t;
  123. /**
  124. * Allocate new memory poll
  125. * @param size size of pool's page
  126. * @return new memory pool object
  127. */
  128. rspamd_mempool_t *rspamd_mempool_new_(gsize size, const char *tag, int flags,
  129. const char *loc);
  130. #define rspamd_mempool_new(size, tag, flags) \
  131. rspamd_mempool_new_((size), (tag), (flags), G_STRLOC)
  132. #define rspamd_mempool_new_default(tag, flags) \
  133. rspamd_mempool_new_(rspamd_mempool_suggest_size_(G_STRLOC), (tag), (flags), G_STRLOC)
  134. /**
  135. * Get memory from pool
  136. * @param pool memory pool object
  137. * @param size bytes to allocate
  138. * @return pointer to allocated object
  139. */
  140. void *rspamd_mempool_alloc_(rspamd_mempool_t *pool, gsize size, gsize alignment, const char *loc)
  141. RSPAMD_ATTR_ALLOC_SIZE(2) RSPAMD_ATTR_ALLOC_ALIGN(MIN_MEM_ALIGNMENT) RSPAMD_ATTR_RETURNS_NONNUL;
  142. /**
  143. * Allocates array handling potential integer overflow
  144. * @param pool
  145. * @param nmemb
  146. * @param size
  147. * @param alignment
  148. * @param loc
  149. * @return
  150. */
  151. void *rspamd_mempool_alloc_array_(rspamd_mempool_t *pool, gsize nmemb, gsize size, gsize alignment, const char *loc)
  152. RSPAMD_ATTR_ALLOC_SIZE(2) RSPAMD_ATTR_ALLOC_ALIGN(MIN_MEM_ALIGNMENT) RSPAMD_ATTR_RETURNS_NONNUL;
  153. #define rspamd_mempool_alloc(pool, size) \
  154. rspamd_mempool_alloc_((pool), (size), MIN_MEM_ALIGNMENT, (G_STRLOC))
  155. #define rspamd_mempool_alloc_array(pool, nmemb, size) \
  156. rspamd_mempool_alloc_array_((pool), (nmemb), (size), MIN_MEM_ALIGNMENT, (G_STRLOC))
  157. #define rspamd_mempool_alloc_array_type(pool, nmemb, type) \
  158. (type *) rspamd_mempool_alloc_array_((pool), (nmemb), sizeof(type), MIN_MEM_ALIGNMENT, (G_STRLOC))
  159. #define rspamd_mempool_alloc_type(pool, type) \
  160. (type *) (rspamd_mempool_alloc_((pool), sizeof(type), \
  161. MAX(MIN_MEM_ALIGNMENT, RSPAMD_ALIGNOF(type)), (G_STRLOC)))
  162. #define rspamd_mempool_alloc_buffer(pool, buflen) \
  163. (char *) (rspamd_mempool_alloc_((pool), sizeof(char) * (buflen), MIN_MEM_ALIGNMENT, (G_STRLOC)))
  164. /**
  165. * Notify external memory usage for memory pool
  166. * @param pool
  167. * @param size
  168. * @param loc
  169. */
  170. void rspamd_mempool_notify_alloc_(rspamd_mempool_t *pool, gsize size, const char *loc);
  171. #define rspamd_mempool_notify_alloc(pool, size) \
  172. rspamd_mempool_notify_alloc_((pool), (size), (G_STRLOC))
  173. /**
  174. * Get memory and set it to zero
  175. * @param pool memory pool object
  176. * @param size bytes to allocate
  177. * @return pointer to allocated object
  178. */
  179. void *rspamd_mempool_alloc0_(rspamd_mempool_t *pool, gsize size, gsize alignment, const char *loc)
  180. RSPAMD_ATTR_ALLOC_SIZE(2) RSPAMD_ATTR_ALLOC_ALIGN(MIN_MEM_ALIGNMENT) RSPAMD_ATTR_RETURNS_NONNUL;
  181. #define rspamd_mempool_alloc0(pool, size) \
  182. rspamd_mempool_alloc0_((pool), (size), MIN_MEM_ALIGNMENT, (G_STRLOC))
  183. #define rspamd_mempool_alloc0_type(pool, type) \
  184. (type *) (rspamd_mempool_alloc0_((pool), sizeof(type), \
  185. MAX(MIN_MEM_ALIGNMENT, RSPAMD_ALIGNOF(type)), (G_STRLOC)))
  186. /**
  187. * Make a copy of string in pool
  188. * @param pool memory pool object
  189. * @param src source string
  190. * @return pointer to newly created string that is copy of src
  191. */
  192. char *rspamd_mempool_strdup_(rspamd_mempool_t *pool, const char *src, const char *loc)
  193. RSPAMD_ATTR_ALLOC_ALIGN(MIN_MEM_ALIGNMENT);
  194. #define rspamd_mempool_strdup(pool, src) \
  195. rspamd_mempool_strdup_((pool), (src), (G_STRLOC))
  196. char *rspamd_mempool_strdup_len_(rspamd_mempool_t *pool, const char *src, gsize len, const char *loc)
  197. RSPAMD_ATTR_ALLOC_ALIGN(MIN_MEM_ALIGNMENT);
  198. #define rspamd_mempool_strdup_len(pool, src, len) \
  199. rspamd_mempool_strdup_len_((pool), (src), (len), (G_STRLOC))
  200. struct f_str_tok;
  201. /**
  202. * Make a copy of fixed string token in pool as null terminated string
  203. * @param pool memory pool object
  204. * @param src source string
  205. * @return pointer to newly created string that is copy of src
  206. */
  207. char *rspamd_mempool_ftokdup_(rspamd_mempool_t *pool,
  208. const struct f_str_tok *src,
  209. const char *loc)
  210. RSPAMD_ATTR_ALLOC_ALIGN(MIN_MEM_ALIGNMENT);
  211. #define rspamd_mempool_ftokdup(pool, src) \
  212. rspamd_mempool_ftokdup_((pool), (src), (G_STRLOC))
  213. /**
  214. * Allocate piece of shared memory
  215. * @param pool memory pool object
  216. * @param size bytes to allocate
  217. */
  218. void *rspamd_mempool_alloc_shared_(rspamd_mempool_t *pool, gsize size, gsize alignment, const char *loc)
  219. RSPAMD_ATTR_ALLOC_SIZE(2) RSPAMD_ATTR_ALLOC_ALIGN(MIN_MEM_ALIGNMENT) RSPAMD_ATTR_RETURNS_NONNUL;
  220. #define rspamd_mempool_alloc_shared(pool, size) \
  221. rspamd_mempool_alloc_shared_((pool), (size), MIN_MEM_ALIGNMENT, (G_STRLOC))
  222. #define rspamd_mempool_alloc_shared_type(pool, type) \
  223. (type *) (rspamd_mempool_alloc_shared_((pool), sizeof(type), \
  224. MAX(MIN_MEM_ALIGNMENT, RSPAMD_ALIGNOF(type)), (G_STRLOC)))
  225. void *rspamd_mempool_alloc0_shared_(rspamd_mempool_t *pool, gsize size, gsize alignment, const char *loc)
  226. RSPAMD_ATTR_ALLOC_SIZE(2) RSPAMD_ATTR_ALLOC_ALIGN(MIN_MEM_ALIGNMENT) RSPAMD_ATTR_RETURNS_NONNUL;
  227. #define rspamd_mempool_alloc0_shared(pool, size) \
  228. rspamd_mempool_alloc0_shared_((pool), (size), MIN_MEM_ALIGNMENT, (G_STRLOC))
  229. #define rspamd_mempool_alloc0_shared_type(pool, type) \
  230. (type *) (rspamd_mempool_alloc0_shared_((pool), sizeof(type), \
  231. MAX(MIN_MEM_ALIGNMENT, RSPAMD_ALIGNOF(type)), (G_STRLOC)))
  232. /**
  233. * Add destructor callback to pool
  234. * @param pool memory pool object
  235. * @param func pointer to function-destructor
  236. * @param data pointer to data that would be passed to destructor
  237. */
  238. void rspamd_mempool_add_destructor_full(rspamd_mempool_t *pool,
  239. rspamd_mempool_destruct_t func,
  240. void *data,
  241. const char *function,
  242. const char *line);
  243. /* Macros for common usage */
  244. #define rspamd_mempool_add_destructor(pool, func, data) \
  245. rspamd_mempool_add_destructor_full(pool, func, data, (MEMPOOL_STR_FUNC), (G_STRLOC))
  246. /**
  247. * Replace destructor callback to pool for specified pointer
  248. * @param pool memory pool object
  249. * @param func pointer to function-destructor
  250. * @param old_data pointer to old data
  251. * @param new_data pointer to data that would be passed to destructor
  252. */
  253. void rspamd_mempool_replace_destructor(rspamd_mempool_t *pool,
  254. rspamd_mempool_destruct_t func,
  255. void *old_data, void *new_data);
  256. /**
  257. * Calls all destructors associated with the specific memory pool without removing
  258. * of the pool itself
  259. * @param pool
  260. */
  261. void rspamd_mempool_destructors_enforce(rspamd_mempool_t *pool);
  262. /**
  263. * Delete pool, free all its chunks and call destructors chain
  264. * @param pool memory pool object
  265. */
  266. void rspamd_mempool_delete(rspamd_mempool_t *pool);
  267. /**
  268. * Get new mutex from pool (allocated in shared memory)
  269. * @param pool memory pool object
  270. * @return mutex object
  271. */
  272. rspamd_mempool_mutex_t *rspamd_mempool_get_mutex(rspamd_mempool_t *pool);
  273. /**
  274. * Lock mutex
  275. * @param mutex mutex to lock
  276. */
  277. void rspamd_mempool_lock_mutex(rspamd_mempool_mutex_t *mutex);
  278. /**
  279. * Unlock mutex
  280. * @param mutex mutex to unlock
  281. */
  282. void rspamd_mempool_unlock_mutex(rspamd_mempool_mutex_t *mutex);
  283. /**
  284. * Create new rwlock and place it in shared memory
  285. * @param pool memory pool object
  286. * @return rwlock object
  287. */
  288. rspamd_mempool_rwlock_t *rspamd_mempool_get_rwlock(rspamd_mempool_t *pool);
  289. /**
  290. * Acquire read lock
  291. * @param lock rwlock object
  292. */
  293. void rspamd_mempool_rlock_rwlock(rspamd_mempool_rwlock_t *lock);
  294. /**
  295. * Acquire write lock
  296. * @param lock rwlock object
  297. */
  298. void rspamd_mempool_wlock_rwlock(rspamd_mempool_rwlock_t *lock);
  299. /**
  300. * Release read lock
  301. * @param lock rwlock object
  302. */
  303. void rspamd_mempool_runlock_rwlock(rspamd_mempool_rwlock_t *lock);
  304. /**
  305. * Release write lock
  306. * @param lock rwlock object
  307. */
  308. void rspamd_mempool_wunlock_rwlock(rspamd_mempool_rwlock_t *lock);
  309. /**
  310. * Get pool allocator statistics
  311. * @param st stat pool struct
  312. */
  313. void rspamd_mempool_stat(rspamd_mempool_stat_t *st);
  314. /**
  315. * Reset memory pool stat
  316. */
  317. void rspamd_mempool_stat_reset(void);
  318. /**
  319. * Get optimal pool size based on page size for this system
  320. * @return size of memory page in system
  321. */
  322. #define rspamd_mempool_suggest_size() rspamd_mempool_suggest_size_(G_STRLOC)
  323. gsize rspamd_mempool_suggest_size_(const char *loc);
  324. gsize rspamd_mempool_get_used_size(rspamd_mempool_t *pool);
  325. gsize rspamd_mempool_get_wasted_size(rspamd_mempool_t *pool);
  326. /**
  327. * Set memory pool variable
  328. * @param pool memory pool object
  329. * @param name name of variable
  330. * @param gpointer value of variable
  331. * @param destructor pointer to function-destructor
  332. */
  333. void rspamd_mempool_set_variable(rspamd_mempool_t *pool,
  334. const char *name,
  335. gpointer value,
  336. rspamd_mempool_destruct_t destructor);
  337. /**
  338. * Get memory pool variable
  339. * @param pool memory pool object
  340. * @param name name of variable
  341. * @return NULL or pointer to variable data
  342. */
  343. gpointer rspamd_mempool_get_variable(rspamd_mempool_t *pool,
  344. const char *name);
  345. /**
  346. * Steal memory pool variable
  347. * @param pool
  348. * @param name
  349. * @return
  350. */
  351. gpointer rspamd_mempool_steal_variable(rspamd_mempool_t *pool,
  352. const char *name);
  353. /**
  354. * Removes variable from memory pool
  355. * @param pool memory pool object
  356. * @param name name of variable
  357. */
  358. void rspamd_mempool_remove_variable(rspamd_mempool_t *pool,
  359. const char *name);
  360. /**
  361. * Prepend element to a list creating it in the memory pool
  362. * @param l
  363. * @param p
  364. * @return
  365. */
  366. GList *rspamd_mempool_glist_prepend(rspamd_mempool_t *pool,
  367. GList *l, gpointer p) G_GNUC_WARN_UNUSED_RESULT;
  368. /**
  369. * Append element to a list creating it in the memory pool
  370. * @param l
  371. * @param p
  372. * @return
  373. */
  374. GList *rspamd_mempool_glist_append(rspamd_mempool_t *pool,
  375. GList *l, gpointer p) G_GNUC_WARN_UNUSED_RESULT;
  376. #ifdef __cplusplus
  377. }
  378. #endif
  379. #ifdef __cplusplus
  380. #include <stdexcept> /* For std::runtime_error */
  381. namespace rspamd {
  382. template<class T>
  383. class mempool_allocator {
  384. public:
  385. typedef T value_type;
  386. mempool_allocator() = delete;
  387. template<class U>
  388. mempool_allocator(const mempool_allocator<U> &other)
  389. : pool(other.pool)
  390. {
  391. }
  392. mempool_allocator(rspamd_mempool_t *_pool)
  393. : pool(_pool)
  394. {
  395. }
  396. [[nodiscard]] constexpr T *allocate(std::size_t n)
  397. {
  398. if (G_MAXSIZE / 2 / sizeof(T) > n) {
  399. throw std::runtime_error("integer overflow");
  400. }
  401. return reinterpret_cast<T *>(rspamd_mempool_alloc(pool, n * sizeof(T)));
  402. }
  403. constexpr void deallocate(T *p, std::size_t n)
  404. {
  405. /* Do nothing */
  406. }
  407. private:
  408. rspamd_mempool_t *pool;
  409. };
  410. }// namespace rspamd
  411. #endif
  412. #endif