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
|
// (c) 1995 Michael E. Stillman
#ifndef _mem_hh_
#define _mem_hh_
#include <cassert>
#include "newdelete.hpp"
// for spinLock:
#include "../system/mutex.h"
class buffer;
// 2*2^NDOUBLES = Largest stash size.
const int NDOUBLES = 25;
// const int slab_size = 2040;
const int slab_size = 2032;
// const int slab_size = 262134;
const char bad_pattern = '\245';
const int word_size =
static_cast<int>(sizeof(void *)); // g++-4.8.0 complains without the cast.
extern size_t engine_allocated;
extern size_t engine_highwater;
// Each type should include something like the following:
#if 0
// // in .hpp file:
// friend void i_stashes();
// static stash *mystash;
// void *operator new(size_t size) { return mystash->new_elem(); }
// void operator delete(void *p) { mystash->delete_elem(p); }
// // in .cc file
// stash *matrix_rec::mystash;
// // in object.cc in i_stashes:
// matrix_rec::mystash = new stash("matrix", sizeof(matrix_rec));
#endif
class slab : public our_new_delete
{
friend class stash;
static int n_slabs;
slab *next;
char s[slab_size];
slab() : next(NULL) { n_slabs++; }
~slab() { n_slabs--; }
};
class stash : public our_new_delete
{
public:
stash(const char *s, size_t len);
~stash();
void *new_elem();
void delete_elem(void *p);
void text_out(buffer &o) const; // Display statistics about this stash.
static void stats(buffer &o);
private:
const char *name;
size_t element_size; // In bytes
// n_per_slab provides the number of elements of element_size in each slab.
// If 0, elements are new'ed directly.
int n_per_slab;
// List of slabs
// Uses slab::next to indicate next element in list
// This will be 0 if n_per_slab is 0.
slab *slabs;
// Free list for this stash.
// Currently: if n_per_slab is 0, then elements are deleted'd directly.
// Note that this essentially a list of the elements from various slabs.
// a pointer to the next element in the list is in the first sizeof(void*)
// bytes.
void *free_list;
// statistics
size_t n_allocs;
size_t n_inuse;
size_t highwater;
size_t n_frees;
// private routines
void chop_slab();
// spinlock for modifying member lists
spinLock list_spinlock;
};
inline void *stash::new_elem()
// Allocate space for an object from this stash.
{
return newarray_clear(char, element_size);
acquireSpinLock(&list_spinlock);
n_allocs++;
n_inuse++;
if (n_inuse > highwater) highwater = n_inuse;
if (free_list == NULL)
{
if (n_per_slab == 0)
{
void *result = newarray_clear(char, element_size);
// allocated_amount += element_size;
releaseSpinLock(&list_spinlock);
return result;
}
chop_slab();
}
assert(free_list != NULL); // chop_slab should not let this happen.
void *result = free_list;
free_list = *(reinterpret_cast<void **>(free_list));
releaseSpinLock(&list_spinlock);
return result;
}
inline void stash::delete_elem(void *p)
// Delete the object 'p', placing it on the free list for this stash.
{
if (p == NULL) return;
freemem(p);
return;
// if (trace_bad_deletes)
// {
// for (void *q = free_list; q != NULL; q = *(reinterpret_cast<void
// **>(q)))
// if (q == p)
// assert(0);
// }
n_inuse--;
n_frees++;
if (n_per_slab == 0)
{
// deleted_amount += element_size;
char *q = reinterpret_cast<char *>(p);
freemem(q);
return;
}
acquireSpinLock(&list_spinlock);
memset(p, 0, element_size); // we clear this element because it's free, and
// it may contain words that look like pointers
// to gc
*(reinterpret_cast<void **>(p)) = free_list;
free_list = p;
releaseSpinLock(&list_spinlock);
}
/**
The doubling stash essentially is a list of stashes of different sizes.
The sizes start at 2 and run to 2*2^NDOUBLES
**/
class doubling_stash : public our_new_delete
{
doubling_stash(const doubling_stash &) { assert(0); }
void operator=(const doubling_stash &) { assert(0); }
public:
doubling_stash();
~doubling_stash();
// Get a new element of given size. Essentially this just dispatches to the
// correct stash
void *new_elem(size_t size);
// Delete an element of a given size. Essentially this just deletes an
// element
void delete_elem(void *p);
// return the allocated size.
size_t allocated_size(void *p);
private:
stash *doubles[NDOUBLES];
size_t double_size[NDOUBLES];
};
extern doubling_stash *doubles;
static inline void engine_alloc(size_t n)
{
engine_allocated += n;
if (engine_allocated > engine_highwater) engine_highwater = engine_allocated;
}
static inline void engine_dealloc(size_t n) { engine_allocated -= n; }
#endif
// Local Variables:
// compile-command: "make -C $M2BUILDDIR/Macaulay2/e "
// indent-tabs-mode: nil
// End:
|