Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
allocator.cpp
Go to the documentation of this file.
1
27// expand _TEST_EXPORT macros
28#define _RWSTD_TEST_SRC
29
30#include <rw_allocator.h>
31#include <rw_new.h> // for operator_new()
32#include <rw_driver.h> // for rw_assert()
33#include <rw_exception.h> // for rw_throw()
34
35#include <new> // for bad_alloc, placement new
36#include <stdarg.h> // for va_arg(), va_list
37#include <string.h> // for memset()
38
39
41static const char* const
42_rw_func_names[] = {
43 "UserAlloc ()",
44 "UserAlloc (const UserAlloc&)",
45 "UserAlloc (const UserAlloc<U>&)",
46 "~UserAlloc ()",
47 "operator= (const UserAlloc&)",
48 "operator=<U>(const UserAlloc<U>&)",
49 "allocate (size_type, void*)",
50 "deallocate (pointer, size_type)",
51 "construct (pointer, const_reference)",
52 "destroy (pointer)",
53 "address (reference) const",
54 "max_size () const"
55};
56
57static int _rw_id_gen; // generates unique ids
58
59
60
63SharedAlloc (size_t nbytes /* = MAX_SIZE */, size_t nblocks /* = MAX_SIZE */)
64 : max_bytes_ (nbytes), max_blocks_ (nblocks),
65 n_bytes_ (0), n_blocks_ (0), n_refs_ (0), id_ (0)
66{
67 memset (n_calls_, 0, sizeof n_calls_);
68 memset (n_throws_, 0, sizeof n_throws_);
69 memset (throw_at_calls_, 0, sizeof throw_at_calls_);
70}
71
72
73/* virtual */ SharedAlloc::
75{
76 static size_t deadbeef = 0;
77 if (0 == deadbeef) {
78 deadbeef = size_t (0xdeadbeefU);
79 if (deadbeef < deadbeef << 1) {
80 // assume 64-bit size_t
81 deadbeef <<= 31;
82 deadbeef <<= 1;
83 deadbeef |= size_t (0xdeadbeefU);
84 }
85 }
86
87 // invalidate
88 size_t n = sizeof n_calls_ / sizeof *n_calls_;
89 for (size_t i = 0; i != n; ++i)
90 n_calls_ [i] = deadbeef;
91
92 n = sizeof n_throws_ / sizeof *n_throws_;
93 for (size_t i = 0; i != n; ++i)
94 n_throws_ [i] = deadbeef;
95
96 n = sizeof throw_at_calls_ / sizeof *throw_at_calls_;
97 for (size_t i = 0; i != n; ++i)
98 throw_at_calls_ [i] = deadbeef;
99}
100
101
102/* virtual */ void* SharedAlloc::
103allocate (size_t nelems, size_t size, const void* /* = 0 */)
104{
106
107 const size_t nbytes = nelems * size;
108
109 if (max_blocks_ < n_blocks_ + nelems)
110 rw_throw (ex_bad_alloc, __FILE__, __LINE__, 0,
111 "allocate (%zu): reached block limit of %zu",
112 nbytes, max_blocks_);
113
114 if (max_bytes_ < n_bytes_ + nbytes)
115 rw_throw (ex_bad_alloc, __FILE__, __LINE__, 0,
116 "allocate (%zu): reached size limit of %zu",
117 nbytes, max_bytes_);
118
119 return operator_new (nbytes, false);
120}
121
122
123/* virtual */ void SharedAlloc::
124deallocate (void *ptr, size_t /* nelems */, size_t /* size */)
125{
127
128 return operator_delete (ptr, false);
129}
130
131
132/* virtual */ size_t SharedAlloc::
133max_size (size_t size /* = 1 */)
134{
136
137 return max_blocks_ / size;
138}
139
140
141/* virtual */ void SharedAlloc::
142funcall (MemFun mf, const SharedAlloc *other /* = 0 */)
143{
144 // increment the number of calls regardless of success
145 ++n_calls_ [mf];
146
147 if (m_ctor == mf) {
148 // ordinary (not a copy or converting) ctor
149 if (id_ <= 0) {
150 RW_ASSERT (0 == n_refs_);
151 id_ = ++_rw_id_gen;
152 }
153
154 // increment the number of references to this allocator
155 ++n_refs_;
156 }
157 else if (m_cpy_ctor == mf || m_cvt_ctor == mf) {
158 // copy or converting ctor
159 RW_ASSERT (0 < id_);
160 RW_ASSERT (0 < n_refs_);
161
162 // increment the number of references to this allocator
163 ++n_refs_;
164 }
165 else if (m_cpy_assign == mf || m_cvt_assign == mf) {
166 // assignment operator
167 RW_ASSERT (0 <= id_ && id_ <= _rw_id_gen);
168 RW_ASSERT (0 < n_refs_);
169
170 RW_ASSERT (0 != other);
171
172 if (this != other) {
173 // decrement the number of references and invalidate
174 // id if no object refers to *this
175 if (0 == --n_refs_)
176 id_ = -1;
177
178 SharedAlloc* const po = _RWSTD_CONST_CAST (SharedAlloc*, other);
179
180 if (other->id_ <= 0)
181 po->id_ = ++_rw_id_gen;
182
183 ++po->n_refs_;
184 }
185 }
186 else if (m_dtor == mf) {
187 // dtor
188 RW_ASSERT (0 <= id_ && id_ <= _rw_id_gen);
189 RW_ASSERT (0 < n_refs_);
190
191 // decrement the number of references and invalidate
192 // id if no object refers to *this
193 if (0 == --n_refs_)
194 id_ = -1;
195 }
196
197 // check the number of calls and throw an exception
198 // if the specified limit has been reached
199 if (n_calls_ [mf] == throw_at_calls_ [mf]) {
200 // increment the exception counter for this function
201 ++n_throws_ [mf];
202
203 rw_throw (ex_bad_alloc, __FILE__, __LINE__, 0,
204 "UserAlloc::%s: reached call limit of %zu",
205 _rw_func_names [mf], throw_at_calls_);
206
207 RW_ASSERT (!"logic error: should not reach");
208 }
209}
210
211
213instance (SharedAlloc *pinst /* = 0 */)
214{
215 // get or a set a pointer to the global allocator object
216 static SharedAlloc* pglobal = 0;
217
218 if (0 == pglobal) {
219 // construct the global allocator object in the static buffer
220 static size_t instbuf [sizeof (SharedAlloc) / sizeof (size_t) + 1];
221 static SharedAlloc* const pbuf = new (instbuf) SharedAlloc ();
222
223 pglobal = pbuf;
224 }
225
226 if (pinst) {
227 // set the global allocator object and return a pointer
228 // to the previous one
229 SharedAlloc* const tmp = pglobal;
230 pglobal = pinst;
231 pinst = tmp;
232 }
233 else
234 pinst = pglobal;
235
236 RW_ASSERT (0 != pinst);
237
238 return pinst;
239}
240
241
244{
245 memset (n_calls_, 0, sizeof n_calls_);
246}
247
248
249/* static */ const char* SharedAlloc::
250func_name (MemFun mf)
251{
252 return _rw_func_names [mf];
253}
254
255
257static void
258_rw_check_leaks (int line, size_t expect_blocks, size_t expect_bytes)
259{
260 if (0 < line) {
261 // determine and report memory leaks since last checkpoint
262
263 /* const */ size_t nbytes;
264 const size_t nblocks = rwt_check_leaks (&nbytes, 0);
265
266 if (_RWSTD_SIZE_MAX == expect_blocks)
267 expect_blocks = nblocks;
268
269 if (_RWSTD_SIZE_MAX == expect_bytes)
270 expect_bytes = nbytes;
271
272 rw_assert (nblocks == expect_blocks && nbytes == expect_bytes,
273 0, line,
274 "line %d. %{$FUNCALL} operator new allocated "
275 "%zu bytes in %zu blocks, expected %zu bytes "
276 "in %zu blocks",
277 __LINE__, nbytes, nblocks,
278 expect_bytes, expect_blocks);
279 }
280 else {
281 // establish a checkpoint for memory leaks
282 rwt_check_leaks (0, 0);
283 }
284}
285
286
287_TEST_EXPORT void
288rw_check_leaks (const SharedAlloc *palloc /* = 0 */,
289 int line /* = 0 */,
290 size_t expect_blocks /* = 0 */,
291 size_t expect_bytes /* = -1 */)
292{
293 _rw_check_leaks (line, expect_blocks, expect_bytes);
294
295 if (0 == palloc)
296 return;
297
298 static size_t nbytes;
299 static size_t nblocks;
300
301 if (0 < line) {
302 // determine and report memory leaks since last checkpoint
303 const size_t leaked_bytes = palloc->n_bytes_ - nbytes;
304 const size_t leaked_blocks = palloc->n_blocks_ - nblocks;
305
306 if (_RWSTD_SIZE_MAX == expect_blocks) // don't care
307 expect_blocks = leaked_blocks;
308
309 if (_RWSTD_SIZE_MAX == expect_bytes) // don't care
310 expect_bytes = nbytes;
311
312 rw_assert ( leaked_blocks == expect_blocks
313 && leaked_bytes == expect_bytes, 0, line,
314 "line %d. %{$FUNCALL} UserAlloc allocated "
315 "%zu bytes in %zu blocks, expected %zu bytes "
316 "in %zu blocks",
317 __LINE__, leaked_bytes, leaked_blocks,
318 expect_bytes, expect_blocks);
319 }
320 else {
321 // establish a checkpoint for memory leaks
322 nbytes = palloc->n_bytes_;
323 nblocks = palloc->n_blocks_;
324 }
325}
#define i
Definition 17.names.cpp:126
#define line
Definition 17.names.cpp:490
#define ptr
Definition 17.names.cpp:525
#define tmp
Definition 17.names.cpp:553
#define n
Definition 17.names.cpp:130
void(* operator_delete)(void *, void *)
void *(* operator_new)(size_t, void *)
#define _RWSTD_CONST_CAST(T, x)
Definition _defs.h:536
_TEST_EXPORT void rw_check_leaks(const SharedAlloc *palloc, int line, size_t expect_blocks, size_t expect_bytes)
_TEST_EXPORT int rw_assert(int, const char *, int, const char *,...)
Definition driver.cpp:1630
_TEST_EXPORT void rw_throw(ExceptionId exid, const char *file, int line, const char *function, const char *fmt,...)
@ ex_bad_alloc
_TEST_EXPORT _RWSTD_SIZE_T rwt_check_leaks(_RWSTD_SIZE_T *, const rwt_free_store *)
Definition new.cpp:684
#define _TEST_EXPORT
Definition rw_testdefs.h:55
#define RW_ASSERT(expr)
virtual ~SharedAlloc()
Definition allocator.cpp:74
virtual void deallocate(void *, _RWSTD_SIZE_T, _RWSTD_SIZE_T)
SharedAlloc(_RWSTD_SIZE_T=_RWSTD_SIZE_MAX, _RWSTD_SIZE_T=_RWSTD_SIZE_MAX)
Definition allocator.cpp:63
virtual void funcall(MemFun, const SharedAlloc *=0)
static SharedAlloc * instance(SharedAlloc *=0)
_RWSTD_SIZE_T max_blocks_
virtual _RWSTD_SIZE_T max_size(_RWSTD_SIZE_T=1)
_RWSTD_SIZE_T throw_at_calls_[n_funs]
_RWSTD_SIZE_T n_blocks_
_RWSTD_SIZE_T n_calls_[n_funs]
_RWSTD_SIZE_T max_bytes_
_RWSTD_SIZE_T n_throws_[n_funs]
void reset_call_counters()
static const char * func_name(MemFun)
virtual void * allocate(_RWSTD_SIZE_T, _RWSTD_SIZE_T, const void *)
_RWSTD_SIZE_T n_refs_
_RWSTD_SIZE_T n_bytes_