From: ceriel Date: Tue, 6 Jan 1987 11:25:09 +0000 (+0000) Subject: Initial revision X-Git-Tag: release-5-5~5067 X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=3788350d7c5814ae923bcbb2d88e1cc0fec4a98d;p=ack.git Initial revision --- diff --git a/modules/src/malloc/Makefile b/modules/src/malloc/Makefile new file mode 100644 index 000000000..994d09967 --- /dev/null +++ b/modules/src/malloc/Makefile @@ -0,0 +1,38 @@ +EMHOME = ../../.. +INSTALL = $(EMHOME)/modules/install +COMPARE = $(EMHOME)/modules/compare +CFLAGS = -O -I$(EMHOME)/modules/h + +MALLOCSRC = READ_ME size_type.h param.h impl.h check.h log.h phys.h \ + mal.c log.c phys.c check.c + +all: malloc.o + +install: all + $(INSTALL) lib/malloc.o + +cmp: all + $(COMPARE) lib/malloc.o + +malloc1.c: $(MALLOCSRC) Makefile add_file + rm -f malloc1.c + for i in $(MALLOCSRC) ; do add_file $$i >> malloc1.c ; done + +malloc.c: malloc1.c + cclash -l7 -c malloc1.c > clashes + cid -Fclashes < malloc1.c > malloc.c + +pr: + @pr Makefile add_file $(MALLOCSRC) + +opr: + make pr | opr + +clean: + rm -f *.o clashes malloc1.c size_type.h getsize malloc.c + +size_type.h: getsize + getsize > size_type.h + +getsize: getsize.o + $(CC) -o getsize getsize.o diff --git a/modules/src/malloc/READ_ME b/modules/src/malloc/READ_ME new file mode 100644 index 000000000..37be2152d --- /dev/null +++ b/modules/src/malloc/READ_ME @@ -0,0 +1,27 @@ +/* + PROGRAM + malloc(), free(), realloc() + AUTHOR + Dick Grune, Free University, Amsterdam + Modified by Ceriel Jacobs, Free University, Amsterdam, + to make it faster + VERSION + $Header$ + DESCRIPTION + This is an independent rewrite of the malloc/free package; it is + fast and efficient. Free blocks are kept in doubly linked lists, + list N holding blocks with sizes between 2**N and 2**(N+1)-1. + Consequently neither malloc nor free have to do any searching: + the cost of a call of malloc() (or free()) is constant, however + many blocks you have got. + + If you switch on the NON_STANDARD macro (see param.h) every block + costs 2 pointers overhead (otherwise it's 4). +*/ +/* + There is an organisational problem here: during devellopment + I want the package divided into modules, which implies external + names for the communication. The only external names I want in + the finished product are malloc, realloc and free. This requires + some hanky-panky. +*/ diff --git a/modules/src/malloc/add_file b/modules/src/malloc/add_file new file mode 100755 index 000000000..7a5eace59 --- /dev/null +++ b/modules/src/malloc/add_file @@ -0,0 +1,14 @@ +echo '' +echo '/**********************************************************/' +echo '/*' +echo '/* ' This was file $1 +echo '/*' +echo '/**********************************************************/' +echo '' +cat $1 | +sed ' + /#include[ ].*"/d + s/^public/private/ + s/^publicdata/static/ +' +echo '' diff --git a/modules/src/malloc/check.c b/modules/src/malloc/check.c new file mode 100644 index 000000000..cb88d471c --- /dev/null +++ b/modules/src/malloc/check.c @@ -0,0 +1,297 @@ +#include +#include "param.h" +#include "impl.h" +#include "check.h" +#include "phys.h" +#include "log.h" + +#ifdef CHECK /* otherwise this whole file is skipped */ + +private acquire_malout(), check_ml_last(); +private dump_all_mallinks(), dump_free_list(), dump_mallink(), print_loop(); +private working_on(); +private unsigned int checksum(); +static FILE *malout; + +public mallink *free_list_entry(); + +#define for_free_list(i,p) \ + for (p = free_list_entry(i); p; p = log_next_of(p)) + +#define for_all_mallinks(ml) /* backwards! */ \ + for (ml = ml_last; ml; \ + ml = first_mallink(ml) ? MAL_NULL : phys_prev_of(ml)) + +/* Maldump */ + +static int pr_cnt = 0; + +maldump(n) { + /* Dump pertinent info in pseudo-readable format; + abort afterwards if n != 0. + */ + static int dumping = 0; + int i; + + if (dumping) + return; + dumping++; + acquire_malout(); + fprintf(malout, + ">>>>>>>>>>>>>>>> DUMP OF ALL MALLINKS <<<<<<<<<<<<<<<<"); + fprintf(malout, " ml_last = %ld\n", (long)ml_last); + if (++pr_cnt == 100) pr_cnt = 0; + dump_all_mallinks(); + fprintf(malout, + ">>>>>>>>>>>>>>>> DUMP OF FREE_LISTS <<<<<<<<<<<<<<<<\n"); + if (++pr_cnt == 100) pr_cnt = 0; + for (i = 0; i < MAX_FLIST; i++) + dump_free_list(i); + fprintf(malout, + ">>>>>>>>>>>>>>>> END OF DUMP <<<<<<<<<<<<<<<<\n"); + fclose(malout); + dumping--; + if (n) + abort(); +} + +private +acquire_malout() { + static char buf[BUFSIZ]; + + if (!malout) { + malout = fopen("mal.out", "w"); + setbuf(malout, buf); + } +} + +private +dump_all_mallinks() { + mallink *ml; + + for_all_mallinks (ml) { + if (print_loop(ml)) + return; + dump_mallink((char *)0, ml); + } +} + +private +dump_free_list(i) { + mallink *ml = free_list_entry(i); + + if (!ml) + return; + fprintf(malout, "%2d: ", i); + for_free_list(i, ml) { + if (print_loop(ml)) + return; + fprintf(malout, "%ld ", ml); + } + fprintf(malout, "<\n"); +} + +private int +print_loop(ml) mallink *ml; { + if (print_of(ml) == pr_cnt) { + fprintf(malout, "... PRINT LOOP\n"); + return 1; + } + set_print(ml, pr_cnt); + return 0; +} + +private +dump_mallink(s, ml) char *s; mallink *ml; { + acquire_malout(); + if (s) + fprintf(malout, "%s: ", s); + fprintf(malout, "@: %ld;", (long)ml); + if (ml && checksum_of(ml) != checksum(ml)) + fprintf(malout, ">>>> CORRUPTED <<<<"); + if (!ml) { + fprintf(malout, "\n"); + return; + } + if (free_of(ml)) { + fprintf(malout, " l_p: %ld;", (long)_log_prev_of(ml)); + fprintf(malout, " l_n: %ld;", (long)_log_next_of(ml)); + } + fprintf(malout, " p_s: %ld;", (long)prev_size_of(ml)); + fprintf(malout, " t_s: %ld;", (long)_this_size_of(ml)); + fprintf(malout, " sz: %ld;", (long)size_of(ml)); + fprintf(malout, " fr: %d;", free_of(ml)); + fprintf(malout, "\n"); +} + +/* Check_mallinks() checks the total data structure as accessible + through free_list[] and ml_last. All check_sums should be OK, + except those held in the small array off_colour. This is a + trick to allow to continue checking even when a few mallinks + are temporarily out of order. + Check_mallinks() tests for a lot of internal consistency. +*/ + +/* Some arbitrary constants */ +#define IN_ML_LAST 93 +#define IN_FREE_LIST 57 /* and in ml_last */ +#define CLEAR 21 + +#define VRIJ 1 +#define BEZET 2 + +public +check_mallinks(s) char *s; { + mallink *ml; + unsigned int size; + int i; + char stat; + + check_ml_last(s); + stat = BEZET; + for_all_mallinks(ml) { + if (checksum_of(ml) != checksum(ml)) + Error("mallink info at %ld corrupted", s, ml); + if (working_on(ml)) { + stat = BEZET; + continue; + } + if ( !last_mallink(ml) && + phys_prev_of(phys_next_of(ml)) != ml + ) + Error("upward chain bad at %ld", s, ml); + if ( !first_mallink(ml) && + phys_next_of(phys_prev_of(ml)) != ml + ) + Error("downward chain bad at %ld", s, ml); + if (free_of(ml)) { + if (stat == VRIJ) + Error("free mallink at %ld follows free mallink", + s, ml); + stat = VRIJ; + } + else + stat = BEZET; + set_mark(ml, IN_ML_LAST); + } + + for (i = 0, size = MIN_SIZE; i < MAX_FLIST; i++, size *= 2) { + for_free_list(i, ml) { + if (working_on(ml)) + continue; + if (!free_of(ml)) + Error("occupied mallink %ld occurs in free_list", s, ml); + switch (mark_of(ml)) { + case IN_ML_LAST: + set_mark(ml, IN_FREE_LIST); + break; + case IN_FREE_LIST: + Error("mallink %ld occurs in 2 free_lists", + s, ml); + default: + Error("unknown mallink %ld in free_list", + s, ml); + } + if (size_of(ml) < size) + Error("size of mallink %ld too small", s, ml); + if (size_of(ml) >= 2*size) + Error("size of mallink %ld too large", s, ml); + } + } + for_all_mallinks (ml) { + if (working_on(ml)) + continue; + if (free_of(ml) && mark_of(ml) != IN_FREE_LIST) + Error("free mallink %ld is in no free_list", s, ml); + set_mark(ml, CLEAR); + } +} + +private +check_ml_last(s) char *s; { + if (ml_last && _this_size_of(ml_last) == 0) + Error("size of ml_last == 0, at %ld", s, ml_last); +} + +private unsigned int +checksum(ml) mallink *ml; { + unsigned int sum = 0; + + if (free_of(ml)) { + sum += (unsigned int)_log_prev_of(ml); + sum += (unsigned int)_log_next_of(ml); + } + sum += (unsigned int)prev_size_of(ml); + sum += (unsigned int)_this_size_of(ml); + return sum; +} + +public +calc_checksum(ml) mallink *ml; { + set_checksum(ml, checksum(ml)); +} + +#define N_COLOUR 10 +static mallink *off_colour[N_COLOUR]; + +public +started_working_on(ml) mallink *ml; { + int i; + + for (i = 0; i < N_COLOUR; i++) + if (off_colour[i] == MAL_NULL) { + off_colour[i] = ml; + return; + } + Error("out of off_colour array at %ld", "started_working_on", ml); +} + +public +stopped_working_on(ml) mallink *ml; { + int i; + + for (i = 0; i < N_COLOUR; i++) + if (off_colour[i] == ml) { + off_colour[i] = MAL_NULL; + return; + } + Error("stopped working on mallink %ld", "stopped_working_on", ml); +} + +private int +working_on(ml) mallink *ml; { + int i; + + for (i = 0; i < N_COLOUR; i++) + if (off_colour[i] == ml) + return 1; + return 0; +} + +public +check_work_empty(s) char *s; { + int i; + int cnt = 0; + + for (i = 0; i < N_COLOUR; i++) + if (off_colour[i] != MAL_NULL) + cnt++; + if (cnt != 0) + Error("off_colour not empty", s, MAL_NULL); +} + +public int +Error(fmt, s, ml) char *fmt, *s; mallink *ml; { + printf("%s: ", s); + printf(fmt, (long)ml); + printf("\n"); + acquire_malout(); + fprintf(malout, "%s: ", s); + fprintf(malout, fmt, (long)ml); + fprintf(malout, "\n"); + fflush(stdout); + maldump(1); + return 0; /* to satisfy lint */ +} + +#endif CHECK diff --git a/modules/src/malloc/check.h b/modules/src/malloc/check.h new file mode 100644 index 000000000..c9697c7ec --- /dev/null +++ b/modules/src/malloc/check.h @@ -0,0 +1,15 @@ +#ifdef CHECK + +public check_mallinks(), calc_checksum(), check_work_empty(); +public started_working_on(), stopped_working_on(); + +#else ifndef CHECK + +#define maldump(n) abort() +#define check_mallinks(s) 0 +#define calc_checksum(ml) 0 +#define started_working_on(ml) 0 +#define stopped_working_on(ml) 0 +#define check_work_empty(s) 0 + +#endif CHECK diff --git a/modules/src/malloc/getsize.c b/modules/src/malloc/getsize.c new file mode 100644 index 000000000..d7cd3a491 --- /dev/null +++ b/modules/src/malloc/getsize.c @@ -0,0 +1,19 @@ +/* find out if a pointer-sized integer, preferably unsigned, + must be declared as an unsigned int or a long +*/ + +#include + +main() +{ + if (sizeof(unsigned int) == sizeof(char *)) { + puts("typedef unsigned int size_type;"); + return 0; + } + if (sizeof(long) == sizeof(char *)) { + puts("typedef long size_type;"); + return 0; + } + fputs(stderr, "funny pointer size\n"); + return 1; +} diff --git a/modules/src/malloc/global.c b/modules/src/malloc/global.c new file mode 100644 index 000000000..0061ed4e4 --- /dev/null +++ b/modules/src/malloc/global.c @@ -0,0 +1,6 @@ +#include "param.h" +#include "impl.h" + +/* The only global data item: +*/ +mallink *ml_last; /* link to the world */ diff --git a/modules/src/malloc/impl.h b/modules/src/malloc/impl.h new file mode 100644 index 000000000..8c39f9543 --- /dev/null +++ b/modules/src/malloc/impl.h @@ -0,0 +1,73 @@ +/* This file essentially describes how the mallink info block + is implemented. +*/ + +#define MIN_SIZE (1<>= 1; + mlp++; + } + while (n >= MIN_SIZE); + + ml1 = *mlp; + set_log_prev(ml, MAL_NULL); + set_log_next(ml, ml1); + calc_checksum(ml); + if (ml1) { + /* link backwards + */ + set_log_prev(ml1, ml); + calc_checksum(ml1); + } + *mlp = ml; +} + +public +unlink_free_chunk(ml) + register mallink *ml; +{ + /* Unlinks a free chunk from (the middle of) the + logical chain. + */ + register mallink *next = log_next_of(ml); + register mallink *prev = log_prev_of(ml); + + if (!prev) { + /* it is the first in the chain */ + register mallink **mlp = &free_list[-1]; + register unsigned int n = size_of(ml); + + assert(n < (1 << LOG_MAX_SIZE)); + do { + n >>= 1; + mlp++; + } + while (n >= MIN_SIZE); + *mlp = next; + } + else { + set_log_next(prev, next); + calc_checksum(prev); + } + if (next) { + set_log_prev(next, prev); + calc_checksum(next); + } +} + +public mallink * +search_free_list(class, n) + unsigned int n; +{ + /* Searches the free_list[class] for a chunk of at least size n; + since it is searching a slightly undersized list, + such a block may not be there. + */ + register mallink *ml; + + for (ml = free_list[class]; ml; ml = log_next_of(ml)) + if (size_of(ml) >= n) + return ml; + return MAL_NULL; /* nothing found */ +} + +public mallink * +first_present(class) + int class; +{ + /* Find the index i in free_list[] such that: + i >= class && free_list[i] != MAL_NULL. + Return MAL_NULL if no such i exists; + Otherwise, return the first block of this list, after + unlinking it. + */ + register mallink **mlp, *ml; + + for (mlp = &free_list[class]; mlp < &free_list[MAX_FLIST]; mlp++) { + if ((ml = *mlp) != MAL_NULL) { + + *mlp = log_next_of(ml); /* may be MAL_NULL */ + if (*mlp) { + /* unhook backward link + */ + set_log_prev(*mlp, MAL_NULL); + calc_checksum(*mlp); + } + return ml; + } + } + return MAL_NULL; +} + +#ifdef CHECK +public mallink * +free_list_entry(i) { + /* To allow maldump.c access to log.c's private data. + */ + return free_list[i]; +} +#endif CHECK diff --git a/modules/src/malloc/log.h b/modules/src/malloc/log.h new file mode 100644 index 000000000..c69f2f669 --- /dev/null +++ b/modules/src/malloc/log.h @@ -0,0 +1,12 @@ +/* Algorithms to manipulate the doubly-linked lists of free + chunks. +*/ + +public link_free_chunk(), unlink_free_chunk(); +public mallink *first_present(), *search_free_list(); + +#define set_log_prev(ml,e) (_log_prev_of(ml) = (e)) +#define log_prev_of(ml) (mallink *) (_log_prev_of(ml)) + +#define set_log_next(ml,e) (_log_next_of(ml) = (e)) +#define log_next_of(ml) (mallink *) (_log_next_of(ml)) diff --git a/modules/src/malloc/mal.c b/modules/src/malloc/mal.c new file mode 100644 index 000000000..629318cfa --- /dev/null +++ b/modules/src/malloc/mal.c @@ -0,0 +1,293 @@ +#include "param.h" +#include "impl.h" +#include "check.h" +#include "log.h" +#include "phys.h" + +/* Malloc space is traversed by N doubly-linked lists of chunks, each + containing a couple of house-keeping data addressed as a + 'mallink' and a piece of useful space, called the block. + The N lists are accessed through their starting pointers in + free_list[]. Free_list[n] points to a list of chunks between + 2**(n+LOG_MIN_SIZE) and 2**(n+LOG_MIN_SIZE+1)-1, which means + that the smallest chunk is 2**LOG_MIN_SIZE (== MIN_SIZE). +*/ + +#ifdef SYSTEM +#include +#define SBRK sys_break +#else +#define SBRK sbrk +#define ILL_BREAK (char *)(-1) /* funny failure value */ +#endif +extern char *SBRK(); +#ifdef STORE +#define MAX_STORE 32 +private do_free(), sell_out(); +privatedata mallink *store[MAX_STORE]; +#endif STORE + +char * +malloc(n) + register unsigned int n; +{check_mallinks("malloc entry");{ + register mallink *ml; + register int min_class; + + if (n < MIN_SIZE) n = align(MIN_SIZE); else n = align(n); +#ifdef STORE + if (n <= MAX_STORE*MIN_SIZE) { + /* look in the store first */ + register mallink **stp = &store[(n >> LOG_MIN_SIZE) - 1]; + + if (ml = *stp) { + *stp = log_next_of(ml); + check_mallinks("malloc fast exit"); + return block_of_mallink(ml); + } + } +#endif STORE + + check_work_empty("malloc, entry"); + + /* Acquire a chunk of at least size n if at all possible; + Try everything. + */ + { + /* Inline substitution of "smallest". + */ + register unsigned int n1 = n; + + assert(n1 < (1 << LOG_MAX_SIZE)); + min_class = 0; + + while (n1 >= MIN_SIZE) { + n1 >>= 1; + min_class++; + } + } + + if (min_class >= MAX_FLIST) + return (char *) 0; /* we don't deal in blocks that big */ + ml = first_present(min_class); + if (ml == MAL_NULL) { + /* Try and extend */ + register char *p; +#define GRABSIZE 4096 /* Power of 2 */ + register unsigned int req = + (n+mallink_size()+GRABSIZE-1)&~(GRABSIZE-1); + + if (!ml_last) { + /* first align SBRK() */ + + p = SBRK(0); + SBRK((int) (align((size_type) p) - (size_type) p)); + } + + p = SBRK((int)req); + if (p == ILL_BREAK) { + /* Now this is bad. The system will not give us + more memory. We can only liquidate our store + and hope it helps. + */ +#ifdef STORE + sell_out(); + ml = first_present(min_class); + if (ml == MAL_NULL) { +#endif STORE + /* In this emergency we try to locate a suitable + chunk in the free_list just below the safe + one; some of these chunks may fit the job. + */ + ml = search_free_list(min_class - 1, n); + if (!ml) /* really out of space */ + return (char *) 0; + started_working_on(ml); + unlink_free_chunk(ml); + check_mallinks("suitable_chunk, forced"); +#ifdef STORE + } + else started_working_on(ml); +#endif STORE + } + else { + ml = create_chunk(p, req); + } + check_mallinks("suitable_chunk, extended"); + } + else started_working_on(ml); + + /* we have a chunk */ + set_free(ml, 0); + calc_checksum(ml); + check_mallinks("suitable_chunk, removed"); + n += mallink_size(); + if (n + MIN_SIZE <= size_of(ml)) { + truncate(ml, n); + } + stopped_working_on(ml); + check_mallinks("malloc exit"); + check_work_empty("malloc exit"); + return block_of_mallink(ml); +}} + +free(addr) + char *addr; +{check_mallinks("free entry");{ + register mallink *ml = mallink_of_block(addr); +#ifdef STORE + + if (free_of(ml)) + return; /* user frees free block */ + if (size_of(ml) <= MAX_STORE*MIN_SIZE) { + /* return to store */ + mallink **stp = &store[(size_of(ml) >> LOG_MIN_SIZE) - 1]; + + set_log_next(ml, *stp); + *stp = ml; + check_mallinks("free fast exit"); + } + else { + do_free(ml); + check_mallinks("free exit"); + } +}} + +private +do_free(ml) + register mallink *ml; +{{ +#endif + +#ifndef STORE + if (free_of(ml)) return; +#endif STORE + started_working_on(ml); + set_free(ml, 1); + calc_checksum(ml); + if (! last_mallink(ml)) { + register mallink *next = phys_next_of(ml); + + if (free_of(next)) coalesce_forw(ml, next); + } + + if (! first_mallink(ml)) { + register mallink *prev = phys_prev_of(ml); + + if (free_of(prev)) { + coalesce_backw(ml, prev); + ml = prev; + } + } + link_free_chunk(ml); + stopped_working_on(ml); + check_work_empty("free"); + + /* Compile-time checks on param.h */ + switch (0) { + case MIN_SIZE < OFF_SET * sizeof(mallink): break; + case 1: break; + /* If this statement does not compile due to duplicate case + entry, the minimum size block cannot hold the links for + the free blocks. Either raise LOG_MIN_SIZE or switch + off NON_STANDARD. + */ + } + switch(0) { + case sizeof(char *) != sizeof(size_type): break; + case 1: break; + /* If this statement does not compile due to duplicate + case entry, size_type is not defined correctly. + Redefine and compile again. + */ + } +}} + +char * +realloc(addr, n) + char *addr; + register unsigned int n; +{check_mallinks("realloc entry");{ + register mallink *ml = mallink_of_block(addr), *ph_next; + register unsigned int size; + + if (n < MIN_SIZE) n = align(MIN_SIZE); else n = align(n); + if (free_of(ml)) { + unlink_free_chunk(ml); + set_free(ml, 0); /* user reallocs free block */ + } + started_working_on(ml); + size = size_of(ml); + if ( /* we can simplify the problem by adding the next chunk: */ + n > size && + !last_mallink(ml) && + (ph_next = phys_next_of(ml), free_of(ph_next)) && + n <= size + mallink_size() + size_of(ph_next) + ) { + /* add in the physically next chunk */ + unlink_free_chunk(ph_next); + combine_chunks(ml, ph_next); + size = size_of(ml); + check_mallinks("realloc, combining"); + } + if (n > size) { /* this didn't help */ + char *new; + register char *l1, *l2 = addr; + + stopped_working_on(ml); + if (!(new = l1 = malloc(n))) return (char *) 0; /* no way */ + while (size--) *l1++ = *l2++; + free(addr); + check_work_empty("mv_realloc"); + return new; + } + /* it helped, but maybe too well */ + n += mallink_size(); + if (n + MIN_SIZE <= size_of(ml)) { + truncate(ml, n); + } + stopped_working_on(ml); + check_mallinks("realloc exit"); + check_work_empty("realloc"); + return addr; +}} + +/* Auxiliary routines */ + +#ifdef STORE +private +sell_out() { + /* Frees all block in store. + */ + register mallink **stp; + + for (stp = &store[0]; stp < &store[MAX_STORE]; stp++) { + register mallink *ml = *stp; + + while (ml) { + *stp = log_next_of(ml); + do_free(ml); + ml = *stp; + } + } + +} +#endif STORE + +#ifdef ASSERT +public +m_assert(fn, ln) + char *fn; +{ + char ch; + + while (*fn) + write(2, fn++, 1); + write(2, ": malloc assert failed in line ", 31); + ch = (ln / 100) + '0'; write(2, &ch, 1); ln %= 100; + ch = (ln / 10) + '0'; write(2, &ch, 1); ln %= 10; + ch = (ln / 1) + '0'; write(2, &ch, 1); + write(2, "\n", 1); + maldump(1); +} +#endif ASSERT diff --git a/modules/src/malloc/param.h b/modules/src/malloc/param.h new file mode 100644 index 000000000..a89e6b99c --- /dev/null +++ b/modules/src/malloc/param.h @@ -0,0 +1,51 @@ +#include "size_type.h" + +/*# define NON_STANDARD /* If defined, the contents of a block + will NOT be left undisturbed after it + is freed, as opposed to what it says + in the manual (malloc(2)). + Setting this option reduces the memory + overhead considerably. I personally + consider the specified behaviour an + artefact of the original + implementation. + */ + +/*# define ASSERT /* If defined, some inexpensive tests + will be made to ensure the + correctness of some sensitive data. + It often turns an uncontrolled crash + into a controlled one. + */ + +/*# define CHECK /* If defined, extensive and expensive + tests will be done, inculding a + checksum on the mallinks (chunk + information blocks). The resulting + information will be printed on a file + called mal.out . + Additionally a function + maldump(n) int n; + will be defined, which will dump + pertinent info in pseudo-readable + form; it aborts afterwards if n != 0. + */ + +/*# define EXTERN /* If defined, all static names will + become extern, which is a help in + using adb(1) or prof(1) + */ + +# define STORE /* If defined, separate free lists will + be kept of chunks with small sizes, + to speed things up a little. + */ + +# define SYSTEM /* If defined, the system module is used. + Otherwise, "sbrk" is called directly. + */ + +#define ALIGNMENT 8 + /* alignment common to all types */ +#define LOG_MIN_SIZE 3 +#define LOG_MAX_SIZE 24 diff --git a/modules/src/malloc/phys.c b/modules/src/malloc/phys.c new file mode 100644 index 000000000..da42536eb --- /dev/null +++ b/modules/src/malloc/phys.c @@ -0,0 +1,93 @@ +#include "param.h" +#include "impl.h" +#include "check.h" +#include "phys.h" + +/* Physical manipulations. + The blocks concerned are not in any logical chain. +*/ + +public mallink * +create_chunk(p, n) + char *p; + unsigned int n; +{ + /* The newly acquired piece of memory at p, of length n, + is turned into a free chunk, properly chained in the + physical chain. + The address of the chunk is returned. + */ + register mallink *ml; + /* All of malloc memory is followed by a virtual chunk, the + mallink of which starts mallink_size() bytes past the last + byte in memory. + Its use is prevented by testing for ml == ml_last first. + */ + register mallink *last = ml_last; + + assert(!last || p == (char *)phys_next_of(last) - mallink_size()); + ml = (mallink *)(p + mallink_size()); /* bump ml */ + started_working_on(ml); + set_free(ml, 1); + set_phys_prev(ml, last); + ml_last = ml; + + set_phys_next(ml, (mallink *)((char *)ml + n)); + calc_checksum(ml); + assert(size_of(ml) + mallink_size() == n); + if (last && free_of(last)) { + coalesce_backw(ml, last); + ml = last; + } + check_mallinks("create_chunk, phys. linked"); + return ml; +} + +public +truncate(ml, size) + register mallink *ml; + unsigned int size; +{ + /* The chunk ml is truncated. + The chunk at ml is split in two. + The remaining part is then freed. + */ + register mallink *new = (mallink *)((char *)ml + size); + register mallink *ph_next = phys_next_of(ml); + + set_free(new, 1); + set_phys_prev(new, ml); + set_phys_next(new, ph_next); + calc_checksum(new); + if (! last_mallink(ml)) { + set_phys_prev(ph_next, new); + calc_checksum(ph_next); + if (free_of(ph_next)) coalesce_forw(new, ph_next); + } + else ml_last = new; + set_phys_next(ml, new); + calc_checksum(ml); + + started_working_on(new); + link_free_chunk(new); + stopped_working_on(new); + check_mallinks("truncate"); +} + +public +combine_chunks(ml1, ml2) + register mallink *ml1, *ml2; +{ + /* The chunks ml1 and ml2 are combined. + */ + register mallink *ml3 = phys_next_of(ml2); + + set_phys_next(ml1, ml3); + calc_checksum(ml1); + if (!last_mallink(ml2)) { + set_phys_prev(ml3, ml1); + calc_checksum(ml3); + } + if (ml_last == ml2) + ml_last = ml1; +} diff --git a/modules/src/malloc/phys.h b/modules/src/malloc/phys.h new file mode 100644 index 000000000..f11d4765f --- /dev/null +++ b/modules/src/malloc/phys.h @@ -0,0 +1,61 @@ +/* Algorithms to manipulate the doubly-linked list of physical + chunks. +*/ +publicdata mallink *ml_last; + +#define __free_of(ml) ((size_type)_phys_prev_of(ml) & 01) +#define __phys_prev_of(ml) (mallink *)((size_type)_phys_prev_of(ml) & ~01) +#define prev_size_of(ml) ((char *)(ml) - \ + (char *)__phys_prev_of(ml) - \ + mallink_size() \ + ) +#define set_phys_prev(ml,e) \ + (_phys_prev_of(ml) = (mallink *) ((char *)e + __free_of(ml))) + +#ifdef CHECK +public Error(); +#define phys_prev_of(ml) (mallink *) \ + (first_mallink(ml) ? \ + (char *)Error("phys_prev_of first_mallink %ld", "somewhere", ml) : \ + (char *)__phys_prev_of(ml) \ + ) +#else ndef CHECK +#define phys_prev_of(ml) __phys_prev_of(ml) +#endif CHECK + +#define first_mallink(ml) (int) (__phys_prev_of(ml) == 0) +#define last_mallink(ml) (int) ((ml) == ml_last) + +/* There is an ambiguity in the semantics of phys_next_of: sometimes + one wants it to return MAL_NULL if there is no next chunk, at + other times one wants the address of the virtual chunk at the + end of memory. The present version returns the address of the + (virtual) chunk and relies on the user to test last_mallink(ml) + first. +*/ +#define size_of(ml) (_this_size_of(ml) - mallink_size()) +#define set_phys_next(ml,e) \ + (_this_size_of(ml) = (unsigned int)((char *)(e) - (char *)(ml))) +#define phys_next_of(ml) (mallink *) ((char *)(ml) + _this_size_of(ml)) + +#define set_free(ml,e) \ + (_phys_prev_of(ml) = (mallink *) \ + ((e) ? (size_type) _phys_prev_of(ml) | 01 : \ + (size_type) _phys_prev_of(ml) & ~01)) +#define free_of(ml) (__free_of(ml)) + +#define coalesce_forw(ml,nxt) ( unlink_free_chunk(nxt), \ + combine_chunks((ml), (nxt))) + +#define coalesce_backw(ml,prv) ( unlink_free_chunk(prv), \ + stopped_working_on(ml), \ + combine_chunks((prv), (ml)), \ + started_working_on(prv)) + +#ifdef CHECK +#define set_print(ml,e) (_print_of(ml) = (e)) +#define print_of(ml) (_print_of(ml)) +#endif CHECK + +public truncate(), combine_chunks(); +public mallink *create_chunk();