/src/dovecot/src/lib/mempool-datastack.c
Line | Count | Source |
1 | | /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "mempool.h" |
5 | | |
6 | | /* |
7 | | * The datastack pool is a thin wrapper around the datastack API. It exists |
8 | | * to allow datastack allocations via the pool API. |
9 | | * |
10 | | * Note: Do not confuse it with the *unsafe* datastack pool. |
11 | | * |
12 | | * Implementation |
13 | | * ============== |
14 | | * |
15 | | * A datastack pool maintains information about the datastack frame that was |
16 | | * in use when the pool was created so it can sanity check all p_new(), |
17 | | * p_malloc(), and p_realloc() calls. |
18 | | * |
19 | | * Creation |
20 | | * -------- |
21 | | * |
22 | | * When a datastack pool is created, a new pool structure is allocated from |
23 | | * the datastack (via t_new()). The current datastack frame number is saved |
24 | | * into the pool's private data (struct datastack_pool). |
25 | | * |
26 | | * Allocation & Reallocation |
27 | | * ------------------------- |
28 | | * |
29 | | * After verifying that the saved datastack frame id matches the currently |
30 | | * active one, the p_malloc() and p_realloc() calls get directed to |
31 | | * t_malloc0() and t_try_realloc(), respectively. There is no |
32 | | * per-allocation information to track. |
33 | | * |
34 | | * Freeing |
35 | | * ------- |
36 | | * |
37 | | * Freeing is a no-op unless the currently active data stack frame id is |
38 | | * different from the one saved during pool creation, in which case the |
39 | | * process panics. |
40 | | * |
41 | | * Clearing |
42 | | * -------- |
43 | | * |
44 | | * A no-op. |
45 | | * |
46 | | * Destruction |
47 | | * ----------- |
48 | | * |
49 | | * Since the memory backing the pool structure itself is allocated from the |
50 | | * datastack via t_new(), the pool and all allocations it made are freed |
51 | | * when the datastack frame is popped. |
52 | | * |
53 | | * Even though the pool maintains a reference count, no memory is freed when |
54 | | * it reaches zero. Once the reference count reaches zero, the state of the |
55 | | * pool is undefined and none of its memory maybe be used. |
56 | | */ |
57 | | |
58 | | static const char *pool_data_stack_get_name(pool_t pool); |
59 | | static void pool_data_stack_ref(pool_t pool); |
60 | | static void pool_data_stack_unref(pool_t *pool); |
61 | | static void *pool_data_stack_malloc(pool_t pool, size_t size); |
62 | | static void pool_data_stack_free(pool_t pool, void *mem); |
63 | | static void *pool_data_stack_realloc(pool_t pool, void *mem, |
64 | | size_t old_size, size_t new_size); |
65 | | static void pool_data_stack_clear(pool_t pool); |
66 | | static size_t pool_data_stack_get_max_easy_alloc_size(pool_t pool); |
67 | | |
68 | | static struct pool_vfuncs static_data_stack_pool_vfuncs = { |
69 | | pool_data_stack_get_name, |
70 | | |
71 | | pool_data_stack_ref, |
72 | | pool_data_stack_unref, |
73 | | |
74 | | pool_data_stack_malloc, |
75 | | pool_data_stack_free, |
76 | | |
77 | | pool_data_stack_realloc, |
78 | | |
79 | | pool_data_stack_clear, |
80 | | pool_data_stack_get_max_easy_alloc_size |
81 | | }; |
82 | | |
83 | | static const struct pool static_data_stack_pool = { |
84 | | .v = &static_data_stack_pool_vfuncs, |
85 | | |
86 | | .alloconly_pool = TRUE, |
87 | | .datastack_pool = TRUE |
88 | | }; |
89 | | |
90 | | struct datastack_pool { |
91 | | struct pool pool; |
92 | | int refcount; |
93 | | |
94 | | unsigned int data_stack_frame; |
95 | | }; |
96 | | |
97 | | pool_t pool_datastack_create(void) |
98 | 324k | { |
99 | 324k | struct datastack_pool *dpool; |
100 | | |
101 | 324k | dpool = t_new(struct datastack_pool, 1); |
102 | 324k | dpool->pool = static_data_stack_pool; |
103 | 324k | dpool->refcount = 1; |
104 | 324k | dpool->data_stack_frame = data_stack_frame_id; |
105 | 324k | return &dpool->pool; |
106 | 324k | } |
107 | | |
108 | | static const char *pool_data_stack_get_name(pool_t pool ATTR_UNUSED) |
109 | 0 | { |
110 | 0 | return "data stack"; |
111 | 0 | } |
112 | | |
113 | | static void pool_data_stack_ref(pool_t pool) |
114 | 0 | { |
115 | 0 | struct datastack_pool *dpool = |
116 | 0 | container_of(pool, struct datastack_pool, pool); |
117 | |
|
118 | 0 | if (unlikely(dpool->data_stack_frame != data_stack_frame_id)) |
119 | 0 | i_panic("pool_data_stack_ref(): stack frame changed"); |
120 | | |
121 | 0 | dpool->refcount++; |
122 | 0 | } |
123 | | |
124 | | static void pool_data_stack_unref(pool_t *pool) |
125 | 0 | { |
126 | 0 | struct datastack_pool *dpool = |
127 | 0 | container_of(*pool, struct datastack_pool, pool); |
128 | |
|
129 | 0 | if (unlikely(dpool->data_stack_frame != data_stack_frame_id)) |
130 | 0 | i_panic("pool_data_stack_unref(): stack frame changed"); |
131 | | |
132 | 0 | dpool->refcount--; |
133 | 0 | i_assert(dpool->refcount >= 0); |
134 | | |
135 | 0 | *pool = NULL; |
136 | 0 | } |
137 | | |
138 | | static void *pool_data_stack_malloc(pool_t pool ATTR_UNUSED, size_t size) |
139 | 649k | { |
140 | 649k | struct datastack_pool *dpool = |
141 | 649k | container_of(pool, struct datastack_pool, pool); |
142 | | |
143 | 649k | if (unlikely(dpool->data_stack_frame != data_stack_frame_id)) |
144 | 0 | i_panic("pool_data_stack_malloc(): stack frame changed"); |
145 | | |
146 | 649k | return t_malloc0(size); |
147 | 649k | } |
148 | | |
149 | | static void pool_data_stack_free(pool_t pool, void *mem ATTR_UNUSED) |
150 | 0 | { |
151 | 0 | struct datastack_pool *dpool = |
152 | 0 | container_of(pool, struct datastack_pool, pool); |
153 | |
|
154 | 0 | if (unlikely(dpool->data_stack_frame != data_stack_frame_id)) |
155 | 0 | i_panic("pool_data_stack_free(): stack frame changed"); |
156 | 0 | } |
157 | | |
158 | | static void *pool_data_stack_realloc(pool_t pool, void *mem, |
159 | | size_t old_size, size_t new_size) |
160 | 32.1k | { |
161 | 32.1k | struct datastack_pool *dpool = |
162 | 32.1k | container_of(pool, struct datastack_pool, pool); |
163 | 32.1k | void *new_mem; |
164 | | |
165 | 32.1k | i_assert(old_size < SIZE_MAX); |
166 | | |
167 | | /* @UNSAFE */ |
168 | 32.1k | if (unlikely(dpool->data_stack_frame != data_stack_frame_id)) |
169 | 0 | i_panic("pool_data_stack_realloc(): stack frame changed"); |
170 | | |
171 | 32.1k | if (old_size >= new_size) |
172 | 0 | return mem; |
173 | | |
174 | 32.1k | if (!t_try_realloc(mem, new_size)) { |
175 | 346 | new_mem = t_malloc_no0(new_size); |
176 | 346 | if (old_size > 0) |
177 | 346 | memcpy(new_mem, mem, old_size); |
178 | 346 | mem = new_mem; |
179 | 346 | } |
180 | | |
181 | 32.1k | memset((char *) mem + old_size, 0, new_size - old_size); |
182 | 32.1k | return mem; |
183 | 32.1k | } |
184 | | |
185 | | static void pool_data_stack_clear(pool_t pool ATTR_UNUSED) |
186 | 0 | { |
187 | 0 | } |
188 | | |
189 | | static size_t |
190 | | pool_data_stack_get_max_easy_alloc_size(pool_t pool ATTR_UNUSED) |
191 | 32.1k | { |
192 | 32.1k | return t_get_bytes_available(); |
193 | 32.1k | } |