1/*****************************************************************************
2
3  Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
4  more contributor license agreements.  See the NOTICE file distributed
5  with this work for additional information regarding copyright ownership.
6  Accellera licenses this file to you under the Apache License, Version 2.0
7  (the "License"); you may not use this file except in compliance with the
8  License.  You may obtain a copy of the License at
9
10    http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15  implied.  See the License for the specific language governing
16  permissions and limitations under the License.
17
18 *****************************************************************************/
19
20/*****************************************************************************
21
22  sc_mempool.cpp - Memory pools for small objects.
23
24  Original Author: Stan Y. Liao, Synopsys, Inc.
25
26  CHANGE LOG AT END OF FILE
27 *****************************************************************************/
28
29
30
31
32//  <sc_mempool> is a class that manages the memory for small objects,
33//  of sizes <increment>, 2 * <increment>, ..., <num_pools> *
34//  <increment>.  When a memory request of <k> bytes is made through
35//  the memory pool, the smallest pool <j> such that <j> * <increment>
36//  >= <k> is used.  The default values of <increment> and <num_pools>
37//  are 8 and 8, respectively.  Each pool has an allocator, that
38//  simply keeps a free list of cells, and allocate new blocks
39//  whenever necessary.  We are relying on malloc() to return a
40//  properly aligned memory blocks.  Note that the memory blocks
41//  allocated by the mempool are never freed.  Thus, if purify is
42//  used, we may get MIU (memory-in-use) warnings.  To disable this,
43//  set the environment variable SYSTEMC_MEMPOOL_DONT_USE to 1.
44
45
46static const char* dont_use_envstring = "SYSTEMC_MEMPOOL_DONT_USE";
47static bool use_default_new = false;
48
49
50#include <stdio.h>
51#include <stdlib.h> // duplicate (c)stdlib.h headers for Solaris
52#include <cstdlib>
53#include "sysc/utils/sc_mempool.h"
54
55namespace sc_core {
56
57//  An allocator is one that handles a particular size.  It keeps a
58//  <free_list> from which a cell may be allocated quickly if there
59//  is one available.  If no cell is available from <free_list>, then
60//  the allocator tries to find whether space is available from the
61//  most-recently-allocated block, as pointed to by <next_avail>.  If
62//  so, then the cell pointed to by <next_avail> is returned, while
63//  <next_avail> is advanced.  If <next_avail> now points beyond
64//  the current block, then it's reset to 0.  On the other hand,
65//  if <next_avail> was 0 when a request to the block is made, then
66//  a new block is allocated by calling system malloc(), and the new
67//  block becomes the head of <block_list>.
68
69
70class sc_allocator {
71    friend class sc_mempool;
72
73public:
74    sc_allocator( int blksz, int cellsz );
75    ~sc_allocator();
76    void* allocate();
77    void release(void* p);
78
79    void display_statistics();
80
81private:
82    union link {
83        link* next;
84        double align;          // alignment required.
85    };
86
87    int block_size;            // size of each block in bytes,
88                               // including the link
89    int cell_size;             // size of each cell in bytes
90
91    char* block_list;
92    link* free_list;
93    char* next_avail;
94
95    int total_alloc;
96    int total_freed;
97    int free_list_alloc;
98};
99
100sc_allocator::sc_allocator( int blksz, int cellsz )
101  : block_size(sizeof(link) + (((blksz - 1) / cellsz) + 1) * cellsz),
102    cell_size(cellsz), block_list(0), free_list(0), next_avail(0),
103    total_alloc(0), total_freed(0), free_list_alloc(0)
104{}
105
106sc_allocator::~sc_allocator()
107{
108    // Shouldn't free the block_list, since global objects that use
109    // the memory pool may not have been destroyed yet ...
110    // Let it leak, let it leak, let it leak ...
111}
112
113void*
114sc_allocator::allocate()
115{
116    void* result = 0;
117    total_alloc++;
118    if (free_list != 0) {
119        free_list_alloc++;
120        result = free_list;
121        free_list = free_list->next;
122        return result;
123    }
124    else if (next_avail != 0) {
125        result = next_avail;
126        next_avail += cell_size;
127        // next_avail goes beyond the block
128        if (next_avail >= block_list + block_size)
129            next_avail = 0;
130        return result;
131    }
132    else {  // (next_avail == 0)
133        link* new_block = (link*) malloc(block_size);  // need alignment?
134        new_block->next = (link*) block_list;
135        block_list = (char*) new_block;
136        result = (block_list + sizeof(link));
137        // Assume that the block will hold more than one cell ... why
138        // wouldn't it?
139        next_avail = ((char*) result) + cell_size;
140        return result;
141    }
142}
143
144void
145sc_allocator::release(void* p)
146{
147    total_freed++;
148    ((link*) p)->next = free_list;
149    free_list = (link*) p;
150}
151
152void
153sc_allocator::display_statistics()
154{
155    int nblocks = 0;
156    for (link* b = (link*) block_list; b != 0; b = b->next)
157        nblocks++;
158    printf("size %3d: %2d block(s), %3d requests (%3d from free list), %3d freed.\n",
159           cell_size, nblocks, total_alloc, free_list_alloc, total_freed);
160}
161
162
163static const int cell_sizes[] = {
164/* 0 */   0,
165/* 1 */   8,
166/* 2 */  16,
167/* 3 */  24,
168/* 4 */  32,
169/* 5 */  48,
170/* 6 */  64,
171/* 7 */  80,
172/* 8 */  96,
173/* 9 */ 128
174};
175
176static const int cell_size_to_allocator[] = {
177/*  0 */    0,
178/*  1 */    1,
179/*  2 */    2,
180/*  3 */    3,
181/*  4 */    4,
182/*  5 */    5,
183/*  6 */    5,
184/*  7 */    6,
185/*  8 */    6,
186/*  9 */    7,
187/* 10 */    7,
188/* 11 */    8,
189/* 12 */    8,
190/* 13 */    9,
191/* 14 */    9,
192/* 15 */    9,
193/* 16 */    9
194};
195
196
197class sc_mempool_int {
198    friend class sc_mempool;
199
200public:
201    sc_mempool_int(int blksz, int npools, int incr);
202    ~sc_mempool_int();
203    void* do_allocate(std::size_t);
204    void  do_release(void*, std::size_t);
205
206    void display_statistics();
207
208private:
209    sc_allocator** allocators;
210    int num_pools;
211    int increment;
212    int max_size;
213};
214
215
216static bool
217compute_use_default_new()
218{
219    const char* e = getenv(dont_use_envstring);
220    return (e != 0) && (atoi(e) != 0);
221}
222
223sc_mempool_int::sc_mempool_int(int blksz, int npools, int incr) :
224    allocators(0), num_pools(0), increment(0), max_size(0)
225{
226    use_default_new = compute_use_default_new();
227    if (! use_default_new) {
228        num_pools = npools;
229        increment = incr;
230        max_size = cell_sizes[sizeof(cell_sizes)/sizeof(cell_sizes[0]) - 1];
231        allocators = new sc_allocator*[npools + 1];
232        for (int i = 1; i <= npools; ++i)
233            allocators[i] = new sc_allocator(blksz, cell_sizes[i]);
234        allocators[0] = allocators[1];
235    }
236}
237
238sc_mempool_int::~sc_mempool_int()
239{
240    for (int i = 1; i <= num_pools; ++i)
241        delete allocators[i];
242    delete[] allocators;
243}
244
245static sc_mempool_int* the_mempool = 0;
246
247void*
248sc_mempool_int::do_allocate(std::size_t sz)
249{
250    int which_allocator = cell_size_to_allocator[(sz - 1) / increment + 1];
251    void* p = allocators[which_allocator]->allocate();
252    return p;
253}
254
255void
256sc_mempool_int::do_release(void* p, std::size_t sz)
257{
258    int which_allocator = cell_size_to_allocator[(sz - 1) / increment + 1];
259    allocators[which_allocator]->release(p);
260}
261
262void
263sc_mempool_int::display_statistics()
264{
265    printf("*** Memory Pool Statistics ***\n");
266    for (int i = 1; i <= num_pools; ++i)
267        allocators[i]->display_statistics();
268}
269
270/****************************************************************************/
271
272void*
273sc_mempool::allocate(std::size_t sz)
274{
275    if (use_default_new)
276        return ::operator new(sz);
277
278    if (the_mempool == 0) {
279        use_default_new = compute_use_default_new();
280        if (use_default_new)
281            return ::operator new(sz);
282
283        // Note that the_mempool is never freed.  This is going to cause
284        // memory leaks when the program exits.
285        the_mempool = new sc_mempool_int( 1984, sizeof(cell_sizes)/sizeof(cell_sizes[0]) - 1, 8 );
286    }
287
288    if (sz > (unsigned) the_mempool->max_size)
289        return ::operator new(sz);
290
291    return the_mempool->do_allocate(sz);
292}
293
294void
295sc_mempool::release(void* p, std::size_t sz)
296{
297    if (p) {
298
299        if (use_default_new || sz > (unsigned) the_mempool->max_size) {
300            ::operator delete(p);
301            return;
302        }
303
304        the_mempool->do_release(p, sz);
305    }
306}
307
308void
309sc_mempool::display_statistics()
310{
311    if (the_mempool && !use_default_new) {
312        the_mempool->display_statistics();
313    } else {
314        printf("SystemC info: no memory allocation was done through the memory pool.\n");
315    }
316}
317
318} // namespace sc_core
319
320// $Log: sc_mempool.cpp,v $
321// Revision 1.4  2011/08/26 20:46:18  acg
322//  Andy Goodrich: moved the modification log to the end of the file to
323//  eliminate source line number skew when check-ins are done.
324//
325// Revision 1.3  2011/08/24 22:05:56  acg
326//  Torsten Maehne: initialization changes to remove warnings.
327//
328// Revision 1.2  2011/02/18 20:38:44  acg
329//  Andy Goodrich: Updated Copyright notice.
330//
331// Revision 1.1.1.1  2006/12/15 20:20:06  acg
332// SystemC 2.3
333//
334// Revision 1.3  2006/01/13 18:53:10  acg
335// Andy Goodrich: Added $Log command so that CVS comments are reproduced in
336// the source.
337
338// taf
339