File: test_pool_alloc.cpp

package info (click to toggle)
boost 1.27.0-3
  • links: PTS
  • area: main
  • in suites: woody
  • size: 19,908 kB
  • ctags: 26,546
  • sloc: cpp: 122,225; ansic: 10,956; python: 4,412; sh: 855; yacc: 803; makefile: 257; perl: 165; lex: 90; csh: 6
file content (319 lines) | stat: -rw-r--r-- 8,630 bytes parent folder | download
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
// Copyright (C) 2000, 2001 Stephen Cleary (shammah@voyager.net)
//
// This file can be redistributed and/or modified under the terms found
//  in "copyright.html"
// This software and its documentation is provided "as is" without express or
//  implied warranty, and with no claim as to its suitability for any purpose.

// Uncomment this to stub out all MT locking
#define BOOST_NO_MT

#include <boost/pool/pool_alloc.hpp>
#include <boost/pool/object_pool.hpp>

#include <stdexcept>
#include <iostream>
#include <vector>
#include <algorithm>
#include <list>
#include <deque>
#include <set>

// VERBOSE will print out trace-like statements to show exactly what this
//  test file is doing.
//#define VERBOSE

// Each "tester" object below checks into and out of the "cdtor_checker",
//  which will report any problems related to the construction/destruction of
//  "tester" objects.
class cdtor_checker
{
  private:
    // Each constructed object registers its "this" pointer into "objs"
    std::set<void *> objs;

  public:
    ~cdtor_checker()
    {
      // At end of program, print out memory leaks
      //  (assuming no static "tester"s)
      for (std::set<void *>::const_iterator i = objs.begin();
          i != objs.end(); ++i)
        std::cout << "Memory leak: " << *i << std::endl;
    }

    void check_in(void * const This)
    {
      if (objs.find(This) != objs.end())
        std::cout << "Double-constructed memory: " << This << std::endl;
      objs.insert(This);
    }
    void check_out(void * const This)
    {
      std::set<void *>::iterator i = objs.find(This);
      if (i == objs.end())
        std::cout << "Destroyed non-constructed memory: " << This << std::endl;
      objs.erase(This);
    }

    // true iff all objects that have checked in have checked out
    bool ok() const { return objs.empty(); }
};
static cdtor_checker mem;

struct tester
{
  tester(int arg1, int arg2)
  {
    if (arg1 == 17 && arg2 == 17)
    {
#ifdef VERBOSE
      std::cout << this << ": tester not constructed" << std::endl;
#endif
      throw std::logic_error("No construction allowed!");
    }
#ifdef VERBOSE
    std::cout << this << ": tester::tester()" << std::endl;
#endif
    mem.check_in(this);
  }
#ifdef VERBOSE
  tester(const tester & other)
  {
    std::cout << this << ": tester::tester(" << &other << ')' << std::endl;
#else
  tester(const tester &)
  {
#endif
    mem.check_in(this);
  }
  ~tester()
  {
#ifdef VERBOSE
    std::cout << this << ": tester::~tester()" << std::endl;
#endif
    mem.check_out(this);
  }
};

void test()
{
  {
    // should do nothing
    boost::object_pool<tester> pool;
  }

  {
    // Construct several tester objects.  Don't delete them (i.e.,
    //  test pool's garbage collection).
#ifdef VERBOSE
    std::cout << "Testing pool. . ." << std::endl;
#endif
    boost::object_pool<tester> pool;
    for (int i = 0; i < 10; ++i)
      pool.construct(13, 13);
  }

  {
    // Construct several tester objects.  Delete some of them.
#ifdef VERBOSE
    std::cout << "Testing pool with some removed. . ." << std::endl;
#endif
    boost::object_pool<tester> pool;
    std::vector<tester *> v;
    for (int i = 0; i < 10; ++i)
      v.push_back(pool.construct(13, 13));
    std::random_shuffle(v.begin(), v.end());
    for (int j = 0; j < 5; ++j)
      pool.destroy(v[j]);
  }

  {
    // Test how pool reacts with constructors that throw exceptions.
    //  Shouldn't have any memory leaks.
#ifdef VERBOSE
    std::cout << "Testing with exceptional constructors :). . ." << std::endl;
#endif
    boost::object_pool<tester> pool;
    for (int i = 0; i < 5; ++i)
    {
      pool.construct(13, 13);
    }
    for (int j = 0; j < 5; ++j)
    {
      try
      {
        // The following constructor will raise an exception.
        pool.construct(17, 17);
      }
      catch (const std::logic_error &) { }
    }
  }
}

void test_alloc()
{
#ifdef VERBOSE
  std::cout << "Testing allocator. . ." << std::endl;
#endif

  {
    // Allocate several tester objects.  Delete one.
#ifdef VERBOSE
    std::cout << "with vector. . ." << std::endl;
#endif
    std::vector<tester, boost::pool_allocator<tester> > l;
    for (int i = 0; i < 10; ++i)
      l.push_back(tester(13, 13));
    l.pop_back();
  }

  {
    // Allocate several tester objects.  Delete two.
#ifdef VERBOSE
    std::cout << "with deque. . ." << std::endl;
#endif
    std::deque<tester, boost::pool_allocator<tester> > l;
    for (int i = 0; i < 10; ++i)
      l.push_back(tester(13, 13));
    l.pop_back();
    l.pop_front();
  }

  {
    // Allocate several tester objects.  Delete two.
#ifdef VERBOSE
    std::cout << "with list. . ." << std::endl;
#endif
    std::list<tester, boost::fast_pool_allocator<tester> > l;
    // lists rebind their allocators, so dumping is useless
    for (int i = 0; i < 10; ++i)
      l.push_back(tester(13, 13));
    l.pop_back();
    l.pop_front();
  }

  tester * tmp;
  {
    // Create a memory leak on purpose.  (Allocator doesn't have
    //  garbage collection)
#ifdef VERBOSE
    std::cout << "Testing allocator cleanup. . ." << std::endl;
#endif
    // (Note: memory leak)
    boost::pool_allocator<tester> a;
    tmp = a.allocate(1, 0);
    new (tmp) tester(13, 13);
  }
  if (mem.ok())
    std::cout << "Error: Pool allocator cleaned up!" << std::endl;
  // Remove memory checker entry (to avoid error later) and
  //  clean up memory leak
  tmp->~tester();
  boost::pool_allocator<tester>::deallocate(tmp, 1);
}

// This is a wrapper around a UserAllocator.  It just registers alloc/dealloc
//  to/from the system memory.  It's used to make sure pool's are allocating
//  and deallocating system memory properly.
// Do NOT use this class with static or singleton pools.
template <typename UserAllocator>
struct TrackAlloc
{
  typedef typename UserAllocator::size_type size_type;
  typedef typename UserAllocator::difference_type difference_type;

  static std::set<char *> allocated_blocks;

  static char * malloc(const size_type bytes)
  {
    char * const ret = UserAllocator::malloc(bytes);
    allocated_blocks.insert(ret);
    return ret;
  }
  static void free(char * const block)
  {
    if (allocated_blocks.find(block) == allocated_blocks.end())
      std::cout << "Free'd non-malloc'ed block: " << (void *) block << std::endl;
    allocated_blocks.erase(block);
    UserAllocator::free(block);
  }

  static bool ok() { return allocated_blocks.empty(); }
};
template <typename UserAllocator>
std::set<char *> TrackAlloc<UserAllocator>::allocated_blocks;

typedef TrackAlloc<boost::default_user_allocator_new_delete> track_alloc;

void test_mem_usage()
{
#ifdef VERBOSE
  std::cout << "Testing memory usage. . ." << std::endl;
#endif

  typedef boost::pool<track_alloc> pool_type;

  {
    // Constructor should do nothing; no memory allocation
    pool_type pool(sizeof(int));
    if (!track_alloc::ok())
      std::cout << "Memory error" << std::endl;
    if (pool.release_memory())
      std::cout << "Pool released memory" << std::endl;
    if (pool.purge_memory())
      std::cout << "Pool purged memory" << std::endl;

    // Should allocate from system
    pool.free(pool.malloc());
    if (track_alloc::ok())
      std::cout << "Memory error" << std::endl;

    // Ask pool to give up memory it's not using; this should succeed
    if (!pool.release_memory())
      std::cout << "Pool didn't release memory" << std::endl;
    if (!track_alloc::ok())
      std::cout << "Memory error" << std::endl;

    // Should allocate from system again
    pool.malloc(); // loses the pointer to the returned chunk (*A*)

    // Ask pool to give up memory it's not using; this should fail
    if (pool.release_memory())
      std::cout << "Pool released memory" << std::endl;

    // Force pool to give up memory it's not using; this should succeed
    //  This will clean up the memory leak from (*A*)
    if (!pool.purge_memory())
      std::cout << "Pool didn't purge memory" << std::endl;
    if (!track_alloc::ok())
      std::cout << "Memory error" << std::endl;

    // Should allocate from system again
    pool.malloc(); // loses the pointer to the returned chunk (*B*)

    // pool's destructor should purge the memory
    //  This will clean up the memory leak from (*B*)
  }

  if (!track_alloc::ok())
    std::cout << "Memory error" << std::endl;
}

int main(void)
{
  test();
  test_alloc();
  test_mem_usage();

#ifdef VERBOSE
  std::cout << "main() exiting. . ." << std::endl;
#endif
  if (mem.ok() && track_alloc::ok())
    std::cout << "All tests passed!" << std::endl;
  else
    std::cout << "Memory inconsistent!" << std::endl;
  return 0;
}