]> git.ipfire.org Git - thirdparty/squid.git/blob - src/mem/Pool.h
SourceFormat Enforcement
[thirdparty/squid.git] / src / mem / Pool.h
1 /*
2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 #ifndef _MEM_POOL_H_
10 #define _MEM_POOL_H_
11
12 /**
13 \defgroup MemPoolsAPI Memory Management (Memory Pool Allocator)
14 \ingroup Components
15 *
16 *\par
17 * MemPools are a pooled memory allocator running on top of malloc(). It's
18 * purpose is to reduce memory fragmentation and provide detailed statistics
19 * on memory consumption.
20 *
21 \par
22 * Preferably all memory allocations in Squid should be done using MemPools
23 * or one of the types built on top of it (i.e. cbdata).
24 *
25 \note Usually it is better to use cbdata types as these gives you additional
26 * safeguards in references and typechecking. However, for high usage pools where
27 * the cbdata functionality of cbdata is not required directly using a MemPool
28 * might be the way to go.
29 */
30
31 #include "mem/Meter.h"
32 #include "splay.h"
33 #include "util.h"
34
35 #if HAVE_GNUMALLOC_H
36 #include <gnumalloc.h>
37 #elif HAVE_MALLOC_H
38 #include <malloc.h>
39 #endif
40 #if HAVE_MEMORY_H
41 #include <memory.h>
42 #endif
43
44 #if !M_MMAP_MAX
45 #if USE_DLMALLOC
46 #define M_MMAP_MAX -4
47 #endif
48 #endif
49
50 /// \ingroup MemPoolsAPI
51 #define toMB(size) ( ((double) size) / ((double)(1024*1024)) )
52 /// \ingroup MemPoolsAPI
53 #define toKB(size) ( (size + 1024 - 1) / 1024 )
54
55 /// \ingroup MemPoolsAPI
56 #define MEM_PAGE_SIZE 4096
57 /// \ingroup MemPoolsAPI
58 #define MEM_MIN_FREE 32
59 /// \ingroup MemPoolsAPI
60 #define MEM_MAX_FREE 65535 /* unsigned short is max number of items per chunk */
61
62 class MemImplementingAllocator;
63 class MemPoolStats;
64
65 /// \ingroup MemPoolsAPI
66 /// \todo Kill this typedef for C++
67 typedef struct _MemPoolGlobalStats MemPoolGlobalStats;
68
69 /// \ingroup MemPoolsAPI
70 class MemPoolIterator
71 {
72 public:
73 MemImplementingAllocator *pool;
74 MemPoolIterator * next;
75 };
76
77 /**
78 \ingroup MemPoolsAPI
79 * Object to track per-pool cumulative counters
80 */
81 class mgb_t
82 {
83 public:
84 mgb_t() : count(0), bytes(0) {}
85 double count;
86 double bytes;
87 };
88
89 /**
90 \ingroup MemPoolsAPI
91 * Object to track per-pool memory usage (alloc = inuse+idle)
92 */
93 class MemPoolMeter
94 {
95 public:
96 MemPoolMeter();
97 void flush();
98
99 Mem::Meter alloc;
100 Mem::Meter inuse;
101 Mem::Meter idle;
102
103 /** history Allocations */
104 mgb_t gb_allocated;
105 mgb_t gb_oallocated;
106
107 /** account Saved Allocations */
108 mgb_t gb_saved;
109
110 /** account Free calls */
111 mgb_t gb_freed;
112 };
113
114 class MemImplementingAllocator;
115
116 /// \ingroup MemPoolsAPI
117 class MemPools
118 {
119 public:
120 static MemPools &GetInstance();
121 MemPools();
122 void init();
123 void flushMeters();
124
125 /**
126 \param label Name for the pool. Displayed in stats.
127 \param obj_size Size of elements in MemPool.
128 */
129 MemImplementingAllocator * create(const char *label, size_t obj_size);
130
131 /**
132 * Sets upper limit in bytes to amount of free ram kept in pools. This is
133 * not strict upper limit, but a hint. When MemPools are over this limit,
134 * totally free chunks are immediately considered for release. Otherwise
135 * only chunks that have not been referenced for a long time are checked.
136 */
137 void setIdleLimit(ssize_t new_idle_limit);
138 ssize_t idleLimit() const;
139
140 /**
141 \par
142 * Main cleanup handler. For MemPools to stay within upper idle limits,
143 * this function needs to be called periodically, preferrably at some
144 * constant rate, eg. from Squid event. It looks through all pools and
145 * chunks, cleans up internal states and checks for releasable chunks.
146 *
147 \par
148 * Between the calls to this function objects are placed onto internal
149 * cache instead of returning to their home chunks, mainly for speedup
150 * purpose. During that time state of chunk is not known, it is not
151 * known whether chunk is free or in use. This call returns all objects
152 * to their chunks and restores consistency.
153 *
154 \par
155 * Should be called relatively often, as it sorts chunks in suitable
156 * order as to reduce free memory fragmentation and increase chunk
157 * utilisation.
158 * Suitable frequency for cleanup is in range of few tens of seconds to
159 * few minutes, depending of memory activity.
160 *
161 \todo DOCS: Re-write this shorter!
162 *
163 \param maxage Release all totally idle chunks that
164 * have not been referenced for maxage seconds.
165 */
166 void clean(time_t maxage);
167
168 void setDefaultPoolChunking(bool const &);
169 MemImplementingAllocator *pools;
170 ssize_t mem_idle_limit;
171 int poolCount;
172 bool defaultIsChunked;
173 private:
174 static MemPools *Instance;
175 };
176
177 /**
178 \ingroup MemPoolsAPI
179 * a pool is a [growing] space for objects of the same size
180 */
181 class MemAllocator
182 {
183 public:
184 MemAllocator (char const *aLabel);
185 virtual ~MemAllocator() {}
186
187 /**
188 \param stats Object to be filled with statistical data about pool.
189 \retval Number of objects in use, ie. allocated.
190 */
191 virtual int getStats(MemPoolStats * stats, int accumulate = 0) = 0;
192
193 virtual MemPoolMeter const &getMeter() const = 0;
194
195 /**
196 * Allocate one element from the pool
197 */
198 virtual void *alloc() = 0;
199
200 /**
201 * Free a element allocated by MemAllocator::alloc()
202 */
203 virtual void freeOne(void *) = 0;
204
205 virtual char const *objectType() const;
206 virtual size_t objectSize() const = 0;
207 virtual int getInUseCount() = 0;
208 void zeroBlocks(bool doIt) {doZero = doIt;}
209 int inUseCount();
210
211 /**
212 * Allows you tune chunk size of pooling. Objects are allocated in chunks
213 * instead of individually. This conserves memory, reduces fragmentation.
214 * Because of that memory can be freed also only in chunks. Therefore
215 * there is tradeoff between memory conservation due to chunking and free
216 * memory fragmentation.
217 *
218 \note As a general guideline, increase chunk size only for pools that keep
219 * very many items for relatively long time.
220 */
221 virtual void setChunkSize(size_t) {}
222
223 /**
224 \param minSize Minimum size needed to be allocated.
225 \retval n Smallest size divisible by sizeof(void*)
226 */
227 static size_t RoundedSize(size_t minSize);
228
229 protected:
230 /** Whether to zero memory on initial allocation and on return to the pool.
231 *
232 * We do this on some pools because many object constructors are/were incomplete
233 * and we are afraid some code may use the object after free.
234 * These probems are becoming less common, so when possible set this to false.
235 */
236 bool doZero;
237
238 private:
239 const char *label;
240 };
241
242 /// \ingroup MemPoolsAPI
243 class MemImplementingAllocator : public MemAllocator
244 {
245 public:
246 MemImplementingAllocator(char const *aLabel, size_t aSize);
247 virtual ~MemImplementingAllocator();
248 virtual MemPoolMeter const &getMeter() const;
249 virtual MemPoolMeter &getMeter();
250 virtual void flushMetersFull();
251 virtual void flushMeters();
252
253 /**
254 * Allocate one element from the pool
255 */
256 virtual void *alloc();
257
258 /**
259 * Free a element allocated by MemImplementingAllocator::alloc()
260 */
261 virtual void freeOne(void *);
262
263 virtual bool idleTrigger(int shift) const = 0;
264 virtual void clean(time_t maxage) = 0;
265 virtual size_t objectSize() const;
266 virtual int getInUseCount() = 0;
267 protected:
268 virtual void *allocate() = 0;
269 virtual void deallocate(void *, bool aggressive) = 0;
270 MemPoolMeter meter;
271 int memPID;
272 public:
273 MemImplementingAllocator *next;
274 public:
275 size_t alloc_calls;
276 size_t free_calls;
277 size_t saved_calls;
278 size_t obj_size;
279 };
280
281 /// \ingroup MemPoolsAPI
282 class MemPoolStats
283 {
284 public:
285 MemAllocator *pool;
286 const char *label;
287 MemPoolMeter *meter;
288 int obj_size;
289 int chunk_capacity;
290 int chunk_size;
291
292 int chunks_alloc;
293 int chunks_inuse;
294 int chunks_partial;
295 int chunks_free;
296
297 int items_alloc;
298 int items_inuse;
299 int items_idle;
300
301 int overhead;
302 };
303
304 /// \ingroup MemPoolsAPI
305 /// \todo Classify and add constructor/destructor to initialize properly.
306 struct _MemPoolGlobalStats {
307 MemPoolMeter *TheMeter;
308
309 int tot_pools_alloc;
310 int tot_pools_inuse;
311 int tot_pools_mempid;
312
313 int tot_chunks_alloc;
314 int tot_chunks_inuse;
315 int tot_chunks_partial;
316 int tot_chunks_free;
317
318 int tot_items_alloc;
319 int tot_items_inuse;
320 int tot_items_idle;
321
322 int tot_overhead;
323 ssize_t mem_idle_limit;
324 };
325
326 /// \ingroup MemPoolsAPI
327 #define memPoolCreate MemPools::GetInstance().create
328
329 /* Allocator API */
330 /**
331 \ingroup MemPoolsAPI
332 * Initialise iteration through all of the pools.
333 \retval Iterator for use by memPoolIterateNext() and memPoolIterateDone()
334 */
335 extern MemPoolIterator * memPoolIterate(void);
336
337 /**
338 \ingroup MemPoolsAPI
339 * Get next pool pointer, until getting NULL pointer.
340 */
341 extern MemImplementingAllocator * memPoolIterateNext(MemPoolIterator * iter);
342
343 /**
344 \ingroup MemPoolsAPI
345 * Should be called after finished with iterating through all pools.
346 */
347 extern void memPoolIterateDone(MemPoolIterator ** iter);
348
349 /**
350 \ingroup MemPoolsAPI
351 \todo Stats API - not sured how to refactor yet
352 *
353 * Fills MemPoolGlobalStats with statistical data about overall
354 * usage for all pools.
355 *
356 \retval Number of pools that have at least one object in use.
357 * Ie. number of dirty pools.
358 */
359 extern int memPoolGetGlobalStats(MemPoolGlobalStats * stats);
360
361 /// \ingroup MemPoolsAPI
362 extern int memPoolInUseCount(MemAllocator *);
363 /// \ingroup MemPoolsAPI
364 extern int memPoolsTotalAllocated(void);
365
366 #endif /* _MEM_POOL_H_ */
367