summaryrefslogtreecommitdiffstats
path: root/src/lib/mempool-datastack.c
blob: b3c1094bf0321f72784fd34c33c51de1d9073fc2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "mempool.h"

/*
 * The datastack pool is a thin wrapper around the datastack API.  It exists
 * to allow datastack allocations via the pool API.
 *
 * Note: Do not confuse it with the *unsafe* datastack pool.
 *
 * Implementation
 * ==============
 *
 * A datastack pool maintains information about the datastack frame that was
 * in use when the pool was created so it can sanity check all p_new(),
 * p_malloc(), and p_realloc() calls.
 *
 * Creation
 * --------
 *
 * When a datastack pool is created, a new pool structure is allocated from
 * the datastack (via t_new()).  The current datastack frame number is saved
 * into the pool's private data (struct datastack_pool).
 *
 * Allocation & Reallocation
 * -------------------------
 *
 * After verifying that the saved datastack frame id matches the currently
 * active one, the p_malloc() and p_realloc() calls get directed to
 * t_malloc0() and t_try_realloc(), respectively.  There is no
 * per-allocation information to track.
 *
 * Freeing
 * -------
 *
 * Freeing is a no-op unless the currently active data stack frame id is
 * different from the one saved during pool creation, in which case the
 * process panics.
 *
 * Clearing
 * --------
 *
 * A no-op.
 *
 * Destruction
 * -----------
 *
 * Since the memory backing the pool structure itself is allocated from the
 * datastack via t_new(), the pool and all allocations it made are freed
 * when the datastack frame is popped.
 *
 * Even though the pool maintains a reference count, no memory is freed when
 * it reaches zero.  Once the reference count reaches zero, the state of the
 * pool is undefined and none of its memory maybe be used.
 */

static const char *pool_data_stack_get_name(pool_t pool);
static void pool_data_stack_ref(pool_t pool);
static void pool_data_stack_unref(pool_t *pool);
static void *pool_data_stack_malloc(pool_t pool, size_t size);
static void pool_data_stack_free(pool_t pool, void *mem);
static void *pool_data_stack_realloc(pool_t pool, void *mem,
				     size_t old_size, size_t new_size);
static void pool_data_stack_clear(pool_t pool);
static size_t pool_data_stack_get_max_easy_alloc_size(pool_t pool);

static struct pool_vfuncs static_data_stack_pool_vfuncs = {
	pool_data_stack_get_name,

	pool_data_stack_ref,
	pool_data_stack_unref,

	pool_data_stack_malloc,
	pool_data_stack_free,

	pool_data_stack_realloc,

	pool_data_stack_clear,
	pool_data_stack_get_max_easy_alloc_size
};

static const struct pool static_data_stack_pool = {
	.v = &static_data_stack_pool_vfuncs,

	.alloconly_pool = TRUE,
	.datastack_pool = TRUE
};

struct datastack_pool {
	struct pool pool;
	int refcount;

	unsigned int data_stack_frame;
};

pool_t pool_datastack_create(void)
{
	struct datastack_pool *dpool;

	dpool = t_new(struct datastack_pool, 1);
	dpool->pool = static_data_stack_pool;
	dpool->refcount = 1;
	dpool->data_stack_frame = data_stack_frame_id;
	return &dpool->pool;
}

static const char *pool_data_stack_get_name(pool_t pool ATTR_UNUSED)
{
	return "data stack";
}

static void pool_data_stack_ref(pool_t pool)
{
	struct datastack_pool *dpool =
		container_of(pool, struct datastack_pool, pool);

	if (unlikely(dpool->data_stack_frame != data_stack_frame_id))
		i_panic("pool_data_stack_ref(): stack frame changed");

	dpool->refcount++;
}

static void pool_data_stack_unref(pool_t *pool)
{
	struct datastack_pool *dpool =
		container_of(*pool, struct datastack_pool, pool);

	if (unlikely(dpool->data_stack_frame != data_stack_frame_id))
		i_panic("pool_data_stack_unref(): stack frame changed");

	dpool->refcount--;
	i_assert(dpool->refcount >= 0);

	*pool = NULL;
}

static void *pool_data_stack_malloc(pool_t pool ATTR_UNUSED, size_t size)
{
	struct datastack_pool *dpool =
		container_of(pool, struct datastack_pool, pool);

	if (unlikely(dpool->data_stack_frame != data_stack_frame_id))
		i_panic("pool_data_stack_malloc(): stack frame changed");

	return t_malloc0(size);
}

static void pool_data_stack_free(pool_t pool, void *mem ATTR_UNUSED)
{
	struct datastack_pool *dpool =
		container_of(pool, struct datastack_pool, pool);

	if (unlikely(dpool->data_stack_frame != data_stack_frame_id))
		i_panic("pool_data_stack_free(): stack frame changed");
}

static void *pool_data_stack_realloc(pool_t pool, void *mem,
				     size_t old_size, size_t new_size)
{
	struct datastack_pool *dpool =
		container_of(pool, struct datastack_pool, pool);
	void *new_mem;

	/* @UNSAFE */
	if (unlikely(dpool->data_stack_frame != data_stack_frame_id))
		i_panic("pool_data_stack_realloc(): stack frame changed");

	if (old_size >= new_size)
		return mem;

	if (!t_try_realloc(mem, new_size)) {
		new_mem = t_malloc_no0(new_size);
		memcpy(new_mem, mem, old_size);
		mem = new_mem;
	}

	memset((char *) mem + old_size, 0, new_size - old_size);
	return mem;
}

static void pool_data_stack_clear(pool_t pool ATTR_UNUSED)
{
}

static size_t
pool_data_stack_get_max_easy_alloc_size(pool_t pool ATTR_UNUSED)
{
	return t_get_bytes_available();
}