From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- erts/emulator/beam/erl_nmgc.h | 364 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 364 insertions(+) create mode 100644 erts/emulator/beam/erl_nmgc.h (limited to 'erts/emulator/beam/erl_nmgc.h') diff --git a/erts/emulator/beam/erl_nmgc.h b/erts/emulator/beam/erl_nmgc.h new file mode 100644 index 0000000000..b207dd37fa --- /dev/null +++ b/erts/emulator/beam/erl_nmgc.h @@ -0,0 +1,364 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2004-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +#ifndef __ERL_NMGC_H__ +#define __ERL_NMGC_H__ + +#ifdef INCREMENTAL +#include /* offsetof() */ +#include "erl_process.h" + +#define INC_FULLPAGE (INC_PAGESIZE + offsetof(INC_Page,start) / sizeof(void*)) + +#define BOXED_NEED(PTR,HDR) \ + (((HDR) & _HEADER_SUBTAG_MASK) == SUB_BINARY_SUBTAG ? \ + header_arity(HDR) + 2 : \ + ((HDR) & _HEADER_SUBTAG_MASK) == FUN_SUBTAG ? \ + header_arity(HDR) + ((ErlFunThing*)(PTR))->num_free + 2 : \ + header_arity(HDR) + 1) + + +#define INC_DECREASE_WORK(n) inc_words_to_go -= (n); + +#define INC_COPY_CONS(FROM,TO,PTR) \ +do { \ + TO[0] = FROM[0]; \ + TO[1] = FROM[1]; \ + INC_MARK_FORWARD(FROM,TO); \ + *(PTR) = make_list(TO); \ + INC_DECREASE_WORK(2); \ + (TO) += 2; \ +} while(0) + +#define INC_COPY_BOXED(FROM,TO,PTR) \ +do { \ + Sint nelts; \ + Eterm hdr = *(FROM); \ + \ + ASSERT(is_header(hdr)); \ + INC_MARK_FORWARD(FROM,TO); \ + *(PTR) = make_boxed(TO); \ + *(TO)++ = *(FROM)++; \ + nelts = header_arity(hdr); \ + switch ((hdr) & _HEADER_SUBTAG_MASK) { \ + case SUB_BINARY_SUBTAG: nelts++; break; \ + case FUN_SUBTAG: nelts+=((ErlFunThing*)(FROM-1))->num_free+1; break;\ + } \ + INC_DECREASE_WORK(nelts + 1); \ + while (nelts--) \ + *(TO)++ = *(FROM)++; \ +} while(0) + + +/* Things copied to the old generation are not marked in the blackmap. + * This is ok since the page they are copied to (aging) is not part of + * the sweep. + */ +#define COPYMARK_CONS(FROM,TO,PTR,LIMIT) \ +do { \ + if (ptr_within(FROM,inc_fromspc,inc_fromend)) { \ + if (INC_IS_FORWARDED(FROM)) { \ + *PTR = make_list(INC_FORWARD_VALUE(FROM)); \ + } else if (TO + 2 <= LIMIT) { \ + INC_STORE(gray,TO,2); \ + INC_COPY_CONS(FROM,TO,PTR); \ + } else { \ + Eterm *hp = erts_inc_alloc(2); \ + INC_STORE(gray,hp,2); \ + INC_COPY_CONS(FROM,hp,PTR); \ + } \ + } else if (ptr_within(FROM,global_old_heap,global_old_hend) && \ + (blackmap[FROM - global_old_heap] == 0)) { \ + blackmap[FROM - global_old_heap] = 2; \ + INC_DECREASE_WORK(2); \ + INC_STORE(gray,FROM,2); \ + } \ +} while(0) + +#define COPYMARK_BOXED(FROM,TO,PTR,LIMIT) \ +do { \ + if (ptr_within(FROM,inc_fromspc,inc_fromend)) { \ + int size = BOXED_NEED(FROM,*FROM); \ + if (INC_IS_FORWARDED(FROM)) { \ + *PTR = make_boxed(INC_FORWARD_VALUE(FROM)); \ + } else if (TO + size <= LIMIT) { \ + INC_STORE(gray,TO,size); \ + INC_COPY_BOXED(FROM,TO,PTR); \ + } else { \ + Eterm *hp = erts_inc_alloc(size); \ + INC_STORE(gray,hp,size); \ + INC_COPY_BOXED(FROM,hp,PTR); \ + } \ + } else if (ptr_within(FROM,global_old_heap,global_old_hend) && \ + (blackmap[FROM - global_old_heap] == 0)) { \ + int size = BOXED_NEED(FROM,*FROM); \ + if (size > 254) { \ + blackmap[FROM - global_old_heap] = 255; \ + *(int*)((long)(&blackmap[FROM - \ + global_old_heap] + 4) & ~3) = size; \ + } else \ + blackmap[FROM - global_old_heap] = size; \ + INC_DECREASE_WORK(size); \ + INC_STORE(gray,FROM,size); \ + } \ +} while(0) + +#define INC_MARK_FORWARD(ptr,dst) fwdptrs[(ptr) - inc_fromspc] = (dst); +#define INC_IS_FORWARDED(ptr) (fwdptrs[(ptr) - inc_fromspc] != 0) +#define INC_FORWARD_VALUE(ptr) fwdptrs[(ptr) - inc_fromspc] + +/* Note for BM_TIMER: Active timer should always be 'system' when IncAlloc + * is called! + */ +#define IncAlloc(p, sz, objv, nobj) \ + (ASSERT_EXPR((sz) >= 0), \ + (((inc_alloc_limit - global_htop) <= (sz)) ? \ + erts_incremental_gc((p),(sz),(objv),(nobj)) : 0), \ + ASSERT_EXPR(global_hend - global_htop > (sz)), \ + global_htop += (sz), global_htop - (sz)) + + +/************************************************************************ + * INC_STORAGE, a dynamic circular storage for objects (INC_Object). * + * Use INC_STORE to add objects to the storage. The storage can then * + * be used either as a queue, using INC_STORAGE_GET to retreive * + * values, or as a stack, using INC_STORAGE_POP. It is OK to mix calls * + * to GET and POP if that is desired. * + * An iterator can be declared to traverse the storage without removing * + * any elements, and INC_STORAGE_STEP will then return each element in * + * turn, oldest first. * + ***********************************************************************/ + +/* Declare a new storage; must be in the beginning of a block. Give + * the storage a name that is used in all later calls to the storage. + * If this is an external declaration of the storage, pass the keyword + * external as the first argument, otherwise leave it empty. + */ +#define INC_STORAGE_DECLARATION(ext,name) \ + ext INC_Storage *name##head; \ + ext INC_Storage *name##tail; \ + ext INC_Object *name##free; \ + ext INC_Object *name##last_free; \ + ext int name##size; + + +/* Initialize the storage. Note that memory allocation is involved - + * don't forget to erase the storage when you are done. + */ +#define INC_STORAGE_INIT(name) do { \ + name##head = (INC_Storage*)erts_alloc(ERTS_ALC_T_OBJECT_STACK, \ + sizeof(INC_Storage)); \ + name##head->next = name##head; \ + name##head->prev = name##head; \ + name##tail = name##head; \ + name##free = name##head->data; \ + name##last_free = name##free + INC_STORAGE_SIZE - 1; \ + name##size = 0; \ +} while(0) + + +/* +#define INC_STORAGE_SWAP(s1,s2) do { \ + INC_Storage *tmphead = s1##head; \ + INC_Storage *tmptail = s1##tail; \ + INC_Object *tmpfree = s1##free; \ + INC_Object *tmplast = s1##last_free; \ + int tmpsize = s1##size; \ + s1##head = s2##head; \ + s1##tail = s2##tail; \ + s1##free = s2##free; \ + s1##last_free = s2##last_free; \ + s1##size = s2##size; \ + s2##head = tmphead; \ + s2##tail = tmptail; \ + s2##free = tmpfree; \ + s2##last_free = tmplast; \ + s2##size = tmpsize; \ +} while(0) +*/ + + +/* Return and remove the youngest element - treat the storage as a + * stack. Always check that there are elements in the queue before + * using INC_STORAGE_POP! + */ +#define INC_STORAGE_POP(name) (ASSERT_EXPR(name##size != 0), \ + name##size--, \ + (--name##free != name##head->data - 1) ? \ + name##free : (name##head = name##head->prev, \ + name##free = name##head->data + INC_STORAGE_SIZE - 1)) + + +/* Return and remove the oldest element - treat the storage as a + * queue. Always check that there are elements in the queue before + * using INC_STORAGE_GET! + */ +#define INC_STORAGE_GET(name) (ASSERT_EXPR(name##size != 0), \ + name##size--, \ + (++name##last_free != name##tail->data + INC_STORAGE_SIZE) ? \ + name##last_free : (name##tail = name##tail->next, \ + name##last_free = name##tail->data)) + + +/* Advance the head to the next free location. If the storage is full, + * a new storage is allocated and linked into the list. + */ +#define INC_STORAGE_NEXT(name) do { \ + if (name##free == name##last_free) { \ + name##tail = (INC_Storage*)erts_alloc(ERTS_ALC_T_OBJECT_STACK, \ + sizeof(INC_Storage)); \ + memcpy(name##tail->data,name##head->data, \ + INC_STORAGE_SIZE * sizeof(INC_Object)); \ + name##tail->next = name##head->next; \ + name##head->next = name##tail; \ + name##tail->prev = name##tail->next->prev; \ + name##tail->next->prev = name##tail; \ + name##last_free = ((void*)name##tail + \ + ((void*)name##last_free - (void*)name##head)); \ + } \ + name##free++; \ + name##size++; \ + if (name##free == name##head->data + INC_STORAGE_SIZE) { \ + name##head = name##head->next; \ + name##free = name##head->data; \ + } \ +} while(0) + + +/* The head of this storage is the next free location. This is where + * the next element will be stored. + */ +#define INC_STORAGE_HEAD(name) (name##free) + + +/* Return the top - the youngest element in the storage. */ +/* #define INC_STORAGE_TOP(name) (name##free - 1 with some magic..) */ + + +/* True if the storage is empty, false otherwise */ +#define INC_STORAGE_EMPTY(name) (name##size == 0) + + +/* Store a new element in the head of the storage and advance the head + * to the next free location. + */ +#define INC_STORE(name,ptr,sz) do { \ + INC_STORAGE_HEAD(name)->this = ptr; \ + INC_STORAGE_HEAD(name)->size = sz; \ + INC_STORAGE_NEXT(name); \ +} while(0) + + +/* An iterator. Use it together with INC_STORAGE_STEP to browse throuh + * the storage. Please note that it is not possible to remove an entry + * in the middle of the storage, use GET or POP to remove enties. + */ +#define INC_STORAGE_ITERATOR(name) \ + INC_Storage *name##iterator_head = name##tail; \ + INC_Object *name##iterator_current = name##last_free; \ + int name##iterator_left = name##size; + + +/* Return the next element in the storage (sorted by age, oldest + * first) or NULL if the storage is empty or the last element has been + * returned already. + */ +#define INC_STORAGE_STEP(name) (name##iterator_left == 0 ? NULL : \ + (name##iterator_left--, \ + (++name##iterator_current != name##iterator_head->data + \ + INC_STORAGE_SIZE) ? name##iterator_current : \ + (name##iterator_head = name##iterator_head->next, \ + name##iterator_current = name##iterator_head->data))) + + +/* Erase the storage. */ +#define INC_STORAGE_ERASE(name)do { \ + name##head->prev->next = NULL; \ + while (name##head != NULL) { \ + name##tail = name##head; \ + name##head = name##head->next; \ + erts_free(ERTS_ALC_T_OBJECT_STACK,(void*)name##tail); \ + } \ + name##tail = NULL; \ + name##free = NULL; \ + name##last_free = NULL; \ + name##size = 0; \ +} while(0) + +/* + * Structures used by the non-moving memory manager + */ + +typedef struct +{ + Eterm *this; + unsigned long size; +} INC_Object; + +typedef struct inc_storage { + struct inc_storage *next; + struct inc_storage *prev; + INC_Object data[INC_STORAGE_SIZE]; +} INC_Storage; + +typedef struct inc_mem_block +{ + unsigned long size; + struct inc_mem_block *prev; + struct inc_mem_block *next; +} INC_MemBlock; + +typedef struct inc_page +{ + struct inc_page *next; + Eterm start[1]; /* Has to be last in struct, this is where the data start */ +} INC_Page; + + +/* + * Heap pointers for the non-moving memory area. + */ +extern INC_Page *inc_used_mem; +extern INC_MemBlock *inc_free_list; +extern unsigned char *blackmap; + +extern Eterm **fwdptrs; +extern Eterm *inc_fromspc; +extern Eterm *inc_fromend; +extern Process *inc_active_proc; +extern Process *inc_active_last; +extern Eterm *inc_alloc_limit; +extern int inc_words_to_go; + +INC_STORAGE_DECLARATION(extern,gray); +INC_STORAGE_DECLARATION(extern,root); + +void erts_init_incgc(void); +void erts_cleanup_incgc(void); +void erts_incremental_gc(Process *p, int sz, Eterm* objv, int nobj); +Eterm *erts_inc_alloc(int need); + +#else +# define INC_STORE(lst,ptr,sz) +# define INC_MARK_FORWARD(ptr) +# define INC_IS_FORWARDED(ptr) +# define INC_FORWARD_VALUE(ptr) +#endif /* INCREMENTAL */ + +#endif /* _ERL_NMGC_H_ */ -- cgit v1.2.3