Skip to content

Commit aaaed88

Browse files
committed
[malloc] Reimplement heap memory using inplace doubly linked list
1 parent da7670e commit aaaed88

File tree

2 files changed

+183
-74
lines changed

2 files changed

+183
-74
lines changed

src/usr/lib/stdlib.c

Lines changed: 183 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
#include <stdlib.h>
22
#include <stddef.h>
33
#include <process.h>
4+
#include <limits.h>
45
#include <stdio.h>
56
#include <sys/syscall.h>
67
#include <math.h>
78

9+
// heap memory parameter
10+
11+
// Full search on heap allocated memory can be a bit slow.
12+
// #define HEAP_MALLOC_SEARCH_MOST_APPROPRIATE_BLOCK
13+
14+
// During reallocating a block should extra space be created
15+
// as a new heap block.
16+
#define HEAP_MALLOC_SPLIT_ON_REALLOCATING_BLOCK
17+
18+
// During free operating should a block merge with free neighbours.
19+
#define HEAP_MALLOC_FREE_MERGE_NEIGHBOURS
20+
21+
822
int min(int a, int b) {
923
return (a<b)?a:b;
1024
}
@@ -156,19 +170,40 @@ cons
156170
memory_layout
157171
-------------
158172
_heap_start: # static marker defined at link time
159-
heap_entry{is_free, size}
160-
heap_entry{is_free, size}
161-
heap_entry{is_free, size}
173+
heap_entry{HEAP_BLOCK_FIRST, size=0}
174+
heap_entry{HEAP_BLOCK_ALLOCATED, size}
175+
heap_entry{HEAP_BLOCK_FREE, size}
176+
heap_entry{HEAP_BLOCK_ALLOCATED, size}
162177
...
163-
... heap_entry_count times
178+
heap_entry{HEAP_BLOCK_LAST, size=0}
164179
... unused memory
165180
_heap_end: # dynamic marker based on current esp
166181
167182
*/
183+
184+
static union heap_entry {
185+
// header of each allocated or unallocated block
186+
struct {
187+
// including the header size
188+
uint32_t size; // size of current block
189+
uint32_t prev_size; // size of prev block for doubly linked list
190+
uint8_t state;
191+
} content;
192+
uint32_t __padding[3]; // force heap_entry size to be 12 bytes.
193+
};
194+
195+
#define HEAP_HEADER_SIZE (sizeof(union heap_entry))
196+
197+
#define HEAP_BLOCK_FREE 0
198+
#define HEAP_BLOCK_ALLOCATED 1
199+
#define HEAP_BLOCK_FIRST 2
200+
#define HEAP_BLOCK_LAST 3
201+
168202
extern void* get_current_esp(); // defined in stdlib.asm
169-
extern char _heap_start[]; // defined in linker.ld
170-
static int heap_entry_count = 0;
203+
// symbol name is defined in linker.ld
204+
extern char _heap_start[];
171205
static const int heap_stack_safety_gap = 1024; // keep 1 kb free between stack and heap
206+
static int heap_initialized = 0; // false
172207

173208
// benchmarking
174209
static int benchmark_heap_inuse = 0;
@@ -184,104 +219,179 @@ int benchmark_get_heap_area() {
184219
return benchmark_heap_area;
185220
}
186221

187-
static union heap_entry {
188-
// header of each allocated or unallocated block
189-
struct {
190-
uint32_t size; // including the header size
191-
uint8_t is_free;
192-
} content;
193-
uint32_t __padding[2]; // force heap_entry size to be 8 bytes.
194-
};
222+
static inline void heap_panic(const char *msg) {
223+
printf("heap memory err: %s\n", msg);
224+
exit(-1);
225+
}
226+
227+
static inline void heap_assert(int is_not_panic, const char *msg) {
228+
if (!is_not_panic) {
229+
heap_panic(msg);
230+
}
231+
return;
232+
}
195233

196-
static union heap_entry *malloc_allocate_new_block(union heap_entry *block, size_t size) {
197-
void* max_loc = get_current_esp()-heap_stack_safety_gap-size;
198-
if ((void*)block>max_loc) {
199-
// panic if no memory to allocate
200-
printf("failed to allocate more memory\n");
201-
exit(-1);
234+
static inline void heap_may_init() {
235+
if (!heap_initialized) {
236+
union heap_entry* first = &(((union heap_entry*)_heap_start)[0]);
237+
union heap_entry* last = &(((union heap_entry*)_heap_start)[1]);
238+
first->content.state = HEAP_BLOCK_FIRST;
239+
last->content.state = HEAP_BLOCK_LAST;
240+
first->content.prev_size = 0;
241+
last->content.size = HEAP_HEADER_SIZE;
242+
first->content.size = last->content.prev_size = HEAP_HEADER_SIZE;
243+
heap_initialized = 1;
202244
}
203-
benchmark_heap_area = max(benchmark_heap_area, (int)(((void*)block) + size - (void*)_heap_start));
245+
}
204246

205-
block->content.size = size;
206-
block->content.is_free = 1; // new block is free at init.
207-
heap_entry_count++;
208-
return block;
247+
static inline union heap_entry *heap_next_block(union heap_entry *current) {
248+
return (((void*)current)+current->content.size);
209249
}
210250

211-
static union heap_entry *malloc_split_block(union heap_entry *block, size_t size) {
212-
// returns the first one of the two splitted blocks
251+
static inline union heap_entry *heap_prev_block(union heap_entry *current) {
252+
return (((void*)current)-current->content.prev_size);
253+
}
213254

214-
// [ --------- old-free-block-------------- ]
215-
// [ allocating-block] [new-left-over-block]
216-
size_t left_over_size = block->content.size - size;
217-
if (left_over_size < sizeof(union heap_entry)+8) {
218-
// do not split block if left-over block for less than 8 bytes allocation.
219-
// i.e. do not split block
220-
return block;
221-
}
222-
// allocate new-left-over-block
223-
union heap_entry *block_second = malloc_allocate_new_block((union heap_entry *)(((void*)block)+size), left_over_size);
224-
// resize first block
225-
block->content.size = size;
226-
return block;
255+
static inline union heap_entry *heap_push_back(union heap_entry *olast, size_t new_size) {
256+
heap_assert(olast->content.state == HEAP_BLOCK_LAST,
257+
"heap_push_back not called on last");
258+
void* _heap_end = get_current_esp()-heap_stack_safety_gap;
259+
260+
olast->content.state = HEAP_BLOCK_FREE;
261+
olast->content.size = new_size;
262+
union heap_entry *nlast = heap_next_block(olast);
263+
264+
benchmark_heap_area = max(benchmark_heap_area, (int)nlast-(int)_heap_start);
265+
266+
// panic if no memory to allocate
267+
// using nlast instead of heap_next_block(nlast) as diff
268+
// should be negligible compared to heap_stack_safety_gap.
269+
heap_assert(_heap_end>(void*)nlast, "failed to allocate more memory");
270+
271+
// new last block
272+
nlast->content.state = HEAP_BLOCK_LAST;
273+
nlast->content.size = HEAP_HEADER_SIZE;
274+
nlast->content.prev_size = new_size;
275+
return olast;
227276
}
228277

229-
static void malloc_merge_onfree(union heap_entry *block) {
230-
// merge recently free block to neighboring block
231-
// if they are free are also free to form large free
232-
// block.
233-
if (!block->content.is_free) return;
278+
static inline union heap_entry *heap_freeblock_split(union heap_entry *node, size_t first_size) {
279+
heap_assert(node->content.state == HEAP_BLOCK_FREE,
280+
"heap_freeblock_split not called on free block");
281+
282+
// before: [ -------- old-free-block--------- ] [out-block]
283+
// after : [ new-first-block][new-second-block] [out-block]
284+
285+
size_t old_size = node->content.size;
286+
size_t second_block_size = old_size-first_size;
287+
288+
heap_assert(first_size<=old_size,
289+
"heap_freeblock_split !(first_size<=old_size)");
290+
291+
if (second_block_size < HEAP_HEADER_SIZE + 4) {
292+
// do not split block if second_block_data_size < 4
293+
return node;
294+
}
234295

235-
// TODO(scopeinfinity): implementation pending.
296+
union heap_entry *out_block = heap_next_block(node);
297+
node->content.size = first_size;
298+
union heap_entry *second = heap_next_block(node);
299+
second->content.state = second->content.state;
300+
second->content.size = second_block_size;
301+
second->content.prev_size = node->content.size;
302+
303+
out_block->content.prev_size = second->content.size;
304+
return node;
236305
}
237306

238-
static union heap_entry *malloc_find_freeblock(size_t size) {
239-
// size includes header size
240-
void* loc = (void*)_heap_start; // start
241-
int block_id = 0;
242-
while(1) {
307+
static inline union heap_entry *heap_freeblocks_merge(union heap_entry *node) {
308+
heap_assert(node->content.state == HEAP_BLOCK_FREE,
309+
"heap_freeblocks_merge not called on free block");
310+
311+
// before: [prev] [ node ] [next]
312+
// after : [prev] [ node ] [next] OR
313+
// after : [prev + node ] [next] OR
314+
// after : [prev] [ node + next] OR
315+
// after : [prev + node + next]
316+
317+
union heap_entry *prev = heap_prev_block(node);
318+
union heap_entry *next = heap_prev_block(node);
319+
if (prev == HEAP_BLOCK_FREE) {
320+
// merge
321+
prev->content.size += node->content.size;
322+
next->content.prev_size = prev->content.size;
323+
node = prev;
324+
}
325+
if (next == HEAP_BLOCK_FREE) {
326+
// merge
327+
union heap_entry *next_2 = heap_prev_block(next);
328+
node->content.size += next->content.size;
329+
next_2->content.prev_size = node->content.size;
330+
}
331+
return node;
332+
}
243333

244-
union heap_entry *block = loc;
245-
if (block_id==heap_entry_count) {
246-
// create new node
247-
return malloc_allocate_new_block(block, size);
334+
static inline union heap_entry *heap_get_free_block(size_t new_size) {
335+
union heap_entry* block = (union heap_entry *)_heap_start; // start
336+
uint32_t most_appropriate_value = UINT_MAX;
337+
union heap_entry* most_appropriate_block = NULL;
338+
while(1) {
339+
if (block->content.state == HEAP_BLOCK_LAST) {
340+
break;
248341
}
249-
if ((!block->content.is_free) || block->content.size < size) {
250-
// block is not free
251-
// not sufficient memory in this block
252-
loc += block->content.size;
253-
block_id++;
254-
continue;
342+
if (block->content.state == HEAP_BLOCK_FREE && block->content.size >= new_size) {
343+
// block is free and have sufficient memory in this block
344+
uint32_t my_value = (block->content.size - new_size);
345+
if (my_value<most_appropriate_value) {
346+
most_appropriate_value = my_value;
347+
most_appropriate_block = block;
348+
#ifndef HEAP_MALLOC_SEARCH_MOST_APPROPRIATE_BLOCK
349+
break;
350+
#endif
351+
}
255352
}
256-
// returns the block after split or no-split
257-
return malloc_split_block(block, size);
353+
block = heap_next_block(block);
354+
}
355+
if (most_appropriate_block == NULL) {
356+
return heap_push_back(block, new_size);
357+
} else {
358+
#ifdef HEAP_MALLOC_SPLIT_ON_REALLOCATING_BLOCK
359+
return heap_freeblock_split(most_appropriate_block, new_size);
360+
#else
361+
return most_appropriate_block;
362+
#endif
258363
}
259364
}
260365

261366
void* malloc(size_t size) {
367+
heap_may_init();
262368
// allocates in chunk of 4
263369
size = ((size + 3) >> 2 ) << 2;
264370
// allocate header
265-
size += sizeof(union heap_entry);
371+
size += HEAP_HEADER_SIZE;
266372

267-
union heap_entry *header = malloc_find_freeblock(size);
373+
union heap_entry *header = heap_get_free_block(size);
268374

269375
benchmark_heap_inuse += size;
270-
header->content.is_free = 0; // false
271-
return (((void*)header)+sizeof(union heap_entry));
376+
header->content.state = HEAP_BLOCK_ALLOCATED;
377+
return (((void*)header)+HEAP_HEADER_SIZE);
272378
}
273379

274380
void free(void* ptr) {
275381
if(ptr == NULL) return;
382+
heap_may_init();
383+
276384
// current version of malloc is non-optimal and doesn't
277385
// do any free operation.
278-
union heap_entry *header = ptr-sizeof(union heap_entry);
279-
if (header->content.is_free!=0) {
386+
union heap_entry *header = ptr-HEAP_HEADER_SIZE;
387+
if (header->content.state != HEAP_BLOCK_ALLOCATED) {
280388
// trying to free unallocated memory
281389
return;
282390
}
283-
header->content.is_free = 1; // true
284-
size_t size = header->content.size;
285-
benchmark_heap_inuse -= size;
286-
malloc_merge_onfree(header);
391+
header->content.state = HEAP_BLOCK_FREE;
392+
benchmark_heap_inuse -= header->content.size;
393+
394+
#ifdef HEAP_MALLOC_FREE_MERGE_NEIGHBOURS
395+
heap_freeblocks_merge(header);
396+
#endif
287397
}

src/usr/local/src/logo.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,5 @@ int main(int argc,char *argv[]) {
450450
// graphics started
451451
std::atexit(cleanup_graphics);
452452
start_logo();
453-
std::cout<<" all over \n";
454453
return 0;
455454
}

0 commit comments

Comments
 (0)