/* * %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_ */