|
43 | 43 | extern "C" { |
44 | 44 | #endif |
45 | 45 |
|
46 | | -static int GC_collect_a_little_or_not(void) |
47 | | -{ |
48 | | - return 0; |
49 | | -} |
50 | | - |
51 | | -typedef struct list_s { |
52 | | - void *memory; |
53 | | - size_t used; |
54 | | - size_t size; |
55 | | - struct list_s *next; |
56 | | -} list; |
| 46 | +#define OMC_MEGABYTE 1024*1024 |
| 47 | +/// 2MB pool by default |
| 48 | +#define OMC_INITIAL_BLOCK_SIZE 2*OMC_MEGABYTE |
| 49 | +/// Error out at a 1GB block request. Something is clearly not going |
| 50 | +/// as intended. If we continue we will most probably overflow at the |
| 51 | +/// 4GB mark anyway so catch it and report it. |
| 52 | +#define OMC_ERROR_AT_EXPAND_REQUEST 1024*OMC_MEGABYTE |
| 53 | + |
| 54 | + |
| 55 | +/// This is the pointer to the current block of memory. The 'memory pool'. |
| 56 | +/// It changes when the program requests memory space that does not fit |
| 57 | +/// in the current block. In which case a new block will be created and |
| 58 | +/// this will be updated. Restoring a saved state (a cleanup operation) |
| 59 | +/// will also update this. |
| 60 | +OMCMemPoolBlock *memory_pools = NULL; |
57 | 61 |
|
58 | 62 | #if !defined(OMC_NO_THREADS) |
59 | 63 | static pthread_mutex_t memory_pool_mutex = PTHREAD_MUTEX_INITIALIZER; |
60 | 64 | #endif |
61 | | -static list *memory_pools = NULL; |
62 | 65 |
|
63 | | -static void pool_init(void) |
| 66 | +static int GC_collect_a_little_or_not(void) |
64 | 67 | { |
65 | | - memory_pools = (list*) omc_alloc_interface.malloc_uncollectable(sizeof(list)); |
66 | | - memory_pools->used = 0; |
67 | | - memory_pools->size = 2*1024*1024; /* 2MB pool by default */ |
68 | | - memory_pools->memory = omc_alloc_interface.malloc_uncollectable(memory_pools->size); |
69 | | - memory_pools->next = NULL; |
| 68 | + return 0; |
70 | 69 | } |
71 | 70 |
|
72 | | -static size_t upper_power_of_two(size_t v) |
| 71 | +static void pool_init(void) |
73 | 72 | { |
74 | | - v--; |
75 | | - v |= v >> 1; |
76 | | - v |= v >> 2; |
77 | | - v |= v >> 4; |
78 | | - v |= v >> 8; |
79 | | - v |= v >> 16; |
80 | | - v++; |
81 | | - return v; |
| 73 | + memory_pools = (OMCMemPoolBlock*) omc_alloc_interface.malloc_uncollectable(sizeof(OMCMemPoolBlock)); |
| 74 | + memory_pools->used = 0; |
| 75 | + memory_pools->size = OMC_INITIAL_BLOCK_SIZE; |
| 76 | + memory_pools->memory = omc_alloc_interface.malloc_uncollectable(memory_pools->size); |
| 77 | + memory_pools->previous = NULL; |
82 | 78 | } |
83 | 79 |
|
84 | 80 | static inline size_t round_up(size_t num, size_t factor) |
85 | 81 | { |
86 | | - return num + factor - 1 - (num - 1) % factor; |
| 82 | + return num + factor - 1 - ((num + factor - 1) % factor); |
87 | 83 | } |
88 | 84 |
|
89 | 85 | static inline void pool_expand(size_t len) |
90 | 86 | { |
91 | | - list *newlist = NULL; |
92 | | - if (0==memory_pools) { |
93 | | - pool_init(); |
94 | | - } |
95 | | - /* Check if we have enough memory already */ |
96 | | - if (memory_pools->size - memory_pools->used >= len) { |
97 | | - return; |
| 87 | + OMCMemPoolBlock *newBlock = NULL; |
| 88 | + |
| 89 | + // The new block will be 1.5x the current block's size. More if we request a very large array. |
| 90 | + size_t new_size = 3*memory_pools->size / 2; |
| 91 | + // Align the new size to the initial block size (2MB right now) for easier debugging. |
| 92 | + new_size = round_up(new_size, OMC_INITIAL_BLOCK_SIZE); |
| 93 | + |
| 94 | + // Report an error if the size is too big. This will error out before the size request is |
| 95 | + // able to overflow the the size_t size (at 4GB) |
| 96 | + if (new_size >= OMC_ERROR_AT_EXPAND_REQUEST) { |
| 97 | + omc_assert_macro(0 && "Attempt to allocate an unusually large memory. The memory management does not seem to be working as intended. Please create an issue on https://github.com/OpenModelica/OpenModelica/issues."); |
98 | 98 | } |
99 | | - newlist = (list*) omc_alloc_interface.malloc_uncollectable(sizeof(list)); |
100 | | - newlist->next = memory_pools; |
101 | | - memory_pools = newlist; |
102 | | - memory_pools->used = 0; |
103 | | - memory_pools->size = upper_power_of_two(3*memory_pools->next->size/2 + len); /* expand by 1.5x the old memory pool. More if we request a very large array. */ |
104 | | - memory_pools->memory = omc_alloc_interface.malloc_uncollectable(memory_pools->size); |
| 99 | + |
| 100 | + newBlock = (OMCMemPoolBlock*) omc_alloc_interface.malloc_uncollectable(sizeof(OMCMemPoolBlock)); |
| 101 | + newBlock->used = 0; |
| 102 | + newBlock->size = new_size; |
| 103 | + newBlock->memory = omc_alloc_interface.malloc_uncollectable(newBlock->size); |
| 104 | + newBlock->previous = memory_pools; |
| 105 | + memory_pools = newBlock; |
105 | 106 | } |
106 | 107 |
|
107 | | -static void* pool_malloc(size_t sz) |
| 108 | +static void* pool_malloc(size_t requested_size) |
108 | 109 | { |
109 | 110 | void *res; |
110 | | - sz = round_up(sz,8); |
| 111 | + requested_size = round_up(requested_size, 8); |
| 112 | + |
111 | 113 | #if !defined(OMC_NO_THREADS) |
112 | 114 | pthread_mutex_lock(&memory_pool_mutex); |
113 | 115 | #endif |
114 | | - pool_expand(sz); |
| 116 | + |
| 117 | + /// If we forgot to explicitly initialize the pool, initialize it now. |
| 118 | + if (!memory_pools) { |
| 119 | + pool_init(); |
| 120 | + } |
| 121 | + |
| 122 | + /// If the current block does not have enough remaining space, expand the pool |
| 123 | + /// by creating another block. The new block should, at least, be as big as |
| 124 | + /// the requested size. Note that, this will update the global memory_pools pointer. |
| 125 | + if (memory_pools->size - memory_pools->used < requested_size) { |
| 126 | + pool_expand(requested_size); |
| 127 | + } |
| 128 | + |
115 | 129 | res = (void*)((char*)memory_pools->memory + memory_pools->used); |
116 | | - memory_pools->used += sz; |
| 130 | + memory_pools->used += requested_size; |
| 131 | + |
117 | 132 | #if !defined(OMC_NO_THREADS) |
118 | 133 | pthread_mutex_unlock(&memory_pool_mutex); |
119 | 134 | #endif |
120 | | - memset(res,0,sz); |
| 135 | + |
| 136 | + memset(res, 0, requested_size); |
121 | 137 | return res; |
122 | 138 | } |
123 | 139 |
|
124 | | -static int pool_free_extra_list(void) |
| 140 | +static int pool_collect_a_little() |
125 | 141 | { |
126 | | - list* current = memory_pools; |
127 | | - if (NULL == current) { |
128 | | - return 0; |
129 | | - } |
| 142 | + return 0; |
| 143 | +} |
| 144 | + |
| 145 | +static void print_mem_pool(OMCMemPoolBlock* chunk) { |
| 146 | + printf("----------------------------\n"); |
| 147 | + printf("%p, %ld, %ld, %p\n", chunk->memory, chunk->used, chunk->size, chunk->previous); |
| 148 | + printf("----------------------------\n"); |
| 149 | +} |
| 150 | + |
| 151 | +MemPoolState omc_util_get_pool_state() { |
| 152 | + MemPoolState state; |
| 153 | + state.block = memory_pools; |
| 154 | + state.used = memory_pools->used; |
130 | 155 |
|
131 | | - // Delete all chunks except the last one. |
132 | | - while (current->next) { |
133 | | - list *next = current->next; |
134 | | - omc_alloc_interface.free_uncollectable(current->memory); |
135 | | - omc_alloc_interface.free_uncollectable(current); |
136 | | - current->next = NULL; |
137 | | - current->size = 0; |
138 | | - current->used = 0; |
139 | | - current = next; |
| 156 | + return state; |
| 157 | +} |
| 158 | + |
| 159 | +void omc_util_restore_pool_state(MemPoolState in_state) { |
| 160 | + // printf("original state:\n"); |
| 161 | + // print_mem_pool(memory_pools); |
| 162 | + |
| 163 | + assert(in_state.block); |
| 164 | + |
| 165 | + OMCMemPoolBlock* currentBlock = memory_pools; |
| 166 | + /// Start from the current block and traverse the chain until we find the block |
| 167 | + /// that was saved in the state. |
| 168 | + /// Clean up the blocks as we go since they will no longer be reachable after updating |
| 169 | + /// to the saved state. |
| 170 | + while (currentBlock != in_state.block) { |
| 171 | + OMCMemPoolBlock* previous = currentBlock->previous; |
| 172 | + omc_alloc_interface.free_uncollectable(currentBlock->memory); |
| 173 | + currentBlock->memory = NULL; |
| 174 | + currentBlock->previous = NULL; |
| 175 | + currentBlock->size = 0; |
| 176 | + currentBlock->used = 0; |
| 177 | + omc_alloc_interface.free_uncollectable(currentBlock); |
| 178 | + currentBlock = previous; |
140 | 179 | } |
| 180 | + assert(currentBlock); |
141 | 181 |
|
142 | | - memory_pools = current; |
| 182 | + currentBlock->used = in_state.used; |
| 183 | + memory_pools = currentBlock; |
143 | 184 |
|
144 | | - return 0; |
| 185 | + // printf("updated state:\n"); |
| 186 | + // print_mem_pool(memory_pools); |
145 | 187 | } |
146 | 188 |
|
147 | 189 | void free_memory_pool() |
148 | 190 | { |
149 | | - pool_free_extra_list(); |
150 | | - if (memory_pools) { |
151 | | - omc_alloc_interface.free_uncollectable(memory_pools->memory); |
152 | | - omc_alloc_interface.free_uncollectable(memory_pools); |
153 | | - memory_pools = NULL; |
| 191 | + OMCMemPoolBlock* currentBlock = memory_pools; |
| 192 | + |
| 193 | + while (currentBlock) { |
| 194 | + OMCMemPoolBlock* previous = currentBlock->previous; |
| 195 | + omc_alloc_interface.free_uncollectable(currentBlock->memory); |
| 196 | + currentBlock->memory = NULL; |
| 197 | + currentBlock->previous = NULL; |
| 198 | + currentBlock->size = 0; |
| 199 | + currentBlock->used = 0; |
| 200 | + omc_alloc_interface.free_uncollectable(currentBlock); |
| 201 | + currentBlock = previous; |
154 | 202 | } |
| 203 | + |
| 204 | + memory_pools = NULL; |
155 | 205 | } |
156 | 206 |
|
157 | 207 | static void nofree(void* ptr) |
@@ -179,7 +229,7 @@ omc_alloc_interface_t omc_alloc_interface_pooled = { |
179 | 229 | pool_malloc, |
180 | 230 | (char*(*)(size_t)) malloc, |
181 | 231 | strdup, |
182 | | - pool_free_extra_list, |
| 232 | + pool_collect_a_little, /* No OP. Does not do anything. The pool requires explicit state save and restore. */ |
183 | 233 | malloc_zero, |
184 | 234 | free, |
185 | 235 | malloc, |
@@ -246,7 +296,7 @@ omc_alloc_interface_t omc_alloc_interface = { |
246 | 296 | pool_malloc, |
247 | 297 | (char*(*)(size_t)) malloc, |
248 | 298 | strdup, |
249 | | - pool_free_extra_list, |
| 299 | + pool_collect_a_little, /* No OP. Does not do anything. The pool requires explicit state save and restore. */ |
250 | 300 | malloc_zero /* calloc, but with malloc interface */, |
251 | 301 | free, |
252 | 302 | malloc, |
|
0 commit comments