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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. /*-
  2. * Copyright 2019 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 20
  63. #define MEMPOOL_UID_LEN 20
  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. gint lock;
  76. pid_t owner;
  77. guint 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. gchar tagname[MEMPOOL_TAG_LEN]; /**< readable name */
  95. gchar 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. guint pools_allocated; /**< total number of allocated pools */
  115. guint pools_freed; /**< number of freed pools */
  116. guint bytes_allocated; /**< bytes that are allocated with pool allocator */
  117. guint chunks_allocated; /**< number of chunks that are allocated */
  118. guint shared_chunks_allocated; /**< shared chunks allocated */
  119. guint chunks_freed; /**< chunks freed */
  120. guint oversized_chunks; /**< oversized chunks */
  121. guint 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 gchar *tag, gint flags,
  129. const gchar *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 gchar *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 gchar *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 gchar *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 gchar *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. gchar *rspamd_mempool_strdup_ (rspamd_mempool_t *pool, const gchar *src, const gchar *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. struct f_str_tok;
  197. /**
  198. * Make a copy of fixed string token in pool as null terminated string
  199. * @param pool memory pool object
  200. * @param src source string
  201. * @return pointer to newly created string that is copy of src
  202. */
  203. gchar *rspamd_mempool_ftokdup_ (rspamd_mempool_t *pool,
  204. const struct f_str_tok *src,
  205. const gchar *loc)
  206. RSPAMD_ATTR_ALLOC_ALIGN(MIN_MEM_ALIGNMENT);
  207. #define rspamd_mempool_ftokdup(pool, src) \
  208. rspamd_mempool_ftokdup_ ((pool), (src), (G_STRLOC))
  209. /**
  210. * Allocate piece of shared memory
  211. * @param pool memory pool object
  212. * @param size bytes to allocate
  213. */
  214. void *rspamd_mempool_alloc_shared_ (rspamd_mempool_t *pool, gsize size, gsize alignment, const gchar *loc)
  215. RSPAMD_ATTR_ALLOC_SIZE(2) RSPAMD_ATTR_ALLOC_ALIGN(MIN_MEM_ALIGNMENT) RSPAMD_ATTR_RETURNS_NONNUL;
  216. #define rspamd_mempool_alloc_shared(pool, size) \
  217. rspamd_mempool_alloc_shared_((pool), (size), MIN_MEM_ALIGNMENT, (G_STRLOC))
  218. #define rspamd_mempool_alloc_shared_type(pool, type) \
  219. (type *)(rspamd_mempool_alloc_shared_((pool), sizeof(type), \
  220. MAX(MIN_MEM_ALIGNMENT, RSPAMD_ALIGNOF(type)), (G_STRLOC)))
  221. void *rspamd_mempool_alloc0_shared_ (rspamd_mempool_t *pool, gsize size, gsize alignment, const gchar *loc)
  222. RSPAMD_ATTR_ALLOC_SIZE(2) RSPAMD_ATTR_ALLOC_ALIGN(MIN_MEM_ALIGNMENT) RSPAMD_ATTR_RETURNS_NONNUL;
  223. #define rspamd_mempool_alloc0_shared(pool, size) \
  224. rspamd_mempool_alloc0_shared_((pool), (size), MIN_MEM_ALIGNMENT, (G_STRLOC))
  225. #define rspamd_mempool_alloc0_shared_type(pool, type) \
  226. (type *)(rspamd_mempool_alloc0_shared_((pool), sizeof(type), \
  227. MAX(MIN_MEM_ALIGNMENT, RSPAMD_ALIGNOF(type)), (G_STRLOC)))
  228. /**
  229. * Add destructor callback to pool
  230. * @param pool memory pool object
  231. * @param func pointer to function-destructor
  232. * @param data pointer to data that would be passed to destructor
  233. */
  234. void rspamd_mempool_add_destructor_full (rspamd_mempool_t *pool,
  235. rspamd_mempool_destruct_t func,
  236. void *data,
  237. const gchar *function,
  238. const gchar *line);
  239. /* Macros for common usage */
  240. #define rspamd_mempool_add_destructor(pool, func, data) \
  241. rspamd_mempool_add_destructor_full (pool, func, data, (MEMPOOL_STR_FUNC), (G_STRLOC))
  242. /**
  243. * Replace destructor callback to pool for specified pointer
  244. * @param pool memory pool object
  245. * @param func pointer to function-destructor
  246. * @param old_data pointer to old data
  247. * @param new_data pointer to data that would be passed to destructor
  248. */
  249. void rspamd_mempool_replace_destructor (rspamd_mempool_t *pool,
  250. rspamd_mempool_destruct_t func,
  251. void *old_data, void *new_data);
  252. /**
  253. * Calls all destructors associated with the specific memory pool without removing
  254. * of the pool itself
  255. * @param pool
  256. */
  257. void rspamd_mempool_destructors_enforce (rspamd_mempool_t *pool);
  258. /**
  259. * Delete pool, free all its chunks and call destructors chain
  260. * @param pool memory pool object
  261. */
  262. void rspamd_mempool_delete (rspamd_mempool_t *pool);
  263. /**
  264. * Get new mutex from pool (allocated in shared memory)
  265. * @param pool memory pool object
  266. * @return mutex object
  267. */
  268. rspamd_mempool_mutex_t *rspamd_mempool_get_mutex (rspamd_mempool_t *pool);
  269. /**
  270. * Lock mutex
  271. * @param mutex mutex to lock
  272. */
  273. void rspamd_mempool_lock_mutex (rspamd_mempool_mutex_t *mutex);
  274. /**
  275. * Unlock mutex
  276. * @param mutex mutex to unlock
  277. */
  278. void rspamd_mempool_unlock_mutex (rspamd_mempool_mutex_t *mutex);
  279. /**
  280. * Create new rwlock and place it in shared memory
  281. * @param pool memory pool object
  282. * @return rwlock object
  283. */
  284. rspamd_mempool_rwlock_t *rspamd_mempool_get_rwlock (rspamd_mempool_t *pool);
  285. /**
  286. * Acquire read lock
  287. * @param lock rwlock object
  288. */
  289. void rspamd_mempool_rlock_rwlock (rspamd_mempool_rwlock_t *lock);
  290. /**
  291. * Acquire write lock
  292. * @param lock rwlock object
  293. */
  294. void rspamd_mempool_wlock_rwlock (rspamd_mempool_rwlock_t *lock);
  295. /**
  296. * Release read lock
  297. * @param lock rwlock object
  298. */
  299. void rspamd_mempool_runlock_rwlock (rspamd_mempool_rwlock_t *lock);
  300. /**
  301. * Release write lock
  302. * @param lock rwlock object
  303. */
  304. void rspamd_mempool_wunlock_rwlock (rspamd_mempool_rwlock_t *lock);
  305. /**
  306. * Get pool allocator statistics
  307. * @param st stat pool struct
  308. */
  309. void rspamd_mempool_stat (rspamd_mempool_stat_t *st);
  310. /**
  311. * Reset memory pool stat
  312. */
  313. void rspamd_mempool_stat_reset (void);
  314. /**
  315. * Get optimal pool size based on page size for this system
  316. * @return size of memory page in system
  317. */
  318. #define rspamd_mempool_suggest_size() rspamd_mempool_suggest_size_(G_STRLOC)
  319. gsize rspamd_mempool_suggest_size_ (const char *loc);
  320. gsize rspamd_mempool_get_used_size (rspamd_mempool_t *pool);
  321. gsize rspamd_mempool_get_wasted_size (rspamd_mempool_t *pool);
  322. /**
  323. * Set memory pool variable
  324. * @param pool memory pool object
  325. * @param name name of variable
  326. * @param gpointer value of variable
  327. * @param destructor pointer to function-destructor
  328. */
  329. void rspamd_mempool_set_variable (rspamd_mempool_t *pool,
  330. const gchar *name,
  331. gpointer value,
  332. rspamd_mempool_destruct_t destructor);
  333. /**
  334. * Get memory pool variable
  335. * @param pool memory pool object
  336. * @param name name of variable
  337. * @return NULL or pointer to variable data
  338. */
  339. gpointer rspamd_mempool_get_variable (rspamd_mempool_t *pool,
  340. const gchar *name);
  341. /**
  342. * Removes variable from memory pool
  343. * @param pool memory pool object
  344. * @param name name of variable
  345. */
  346. void rspamd_mempool_remove_variable (rspamd_mempool_t *pool,
  347. const gchar *name);
  348. /**
  349. * Prepend element to a list creating it in the memory pool
  350. * @param l
  351. * @param p
  352. * @return
  353. */
  354. GList *rspamd_mempool_glist_prepend (rspamd_mempool_t *pool,
  355. GList *l, gpointer p) G_GNUC_WARN_UNUSED_RESULT;
  356. /**
  357. * Append element to a list creating it in the memory pool
  358. * @param l
  359. * @param p
  360. * @return
  361. */
  362. GList *rspamd_mempool_glist_append (rspamd_mempool_t *pool,
  363. GList *l, gpointer p) G_GNUC_WARN_UNUSED_RESULT;
  364. #ifdef __cplusplus
  365. }
  366. #endif
  367. #ifdef __cplusplus
  368. #include <stdexcept> /* For std::runtime_error */
  369. namespace rspamd {
  370. template<class T>
  371. class mempool_allocator {
  372. public:
  373. typedef T value_type;
  374. mempool_allocator() = delete;
  375. template<class U>
  376. mempool_allocator(const mempool_allocator<U> &other) : pool(other.pool) {}
  377. mempool_allocator(rspamd_mempool_t *_pool) : pool(_pool) {}
  378. [[nodiscard]] constexpr T* allocate(std::size_t n)
  379. {
  380. if (G_MAXSIZE / 2 / sizeof(T) > n) {
  381. throw std::runtime_error("integer overflow");
  382. }
  383. return reinterpret_cast<T*>(rspamd_mempool_alloc(pool, n * sizeof(T)));
  384. }
  385. constexpr void deallocate(T* p, std::size_t n) {
  386. /* Do nothing */
  387. }
  388. private:
  389. rspamd_mempool_t *pool;
  390. };
  391. }
  392. #endif
  393. #endif