mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-01-17 22:50:51 +00:00
Garbage collection:
Moved from reference counting over to a mark and sweep method. Objects are now required to implement an allRefs method which returns a pointer to a list of all other referenced objects. How the list is maintained is up to each object. The retain/release mechanism still works; objects with any references will not be garbage collected, even if the collector thinks they should be.
This commit is contained in:
parent
e498b196fe
commit
991fc06360
8 changed files with 121 additions and 58 deletions
|
@ -37,6 +37,7 @@
|
|||
classDecl (ArrayList, List,
|
||||
unsigned int realsize;
|
||||
Object **elements;
|
||||
ObjRefs_t allrefs;
|
||||
);
|
||||
#define ARRAYLIST(o) ((ArrayList *)(o))
|
||||
|
||||
|
@ -45,6 +46,7 @@ classDecl (ArrayListIterator, Iterator,
|
|||
unsigned int pos;
|
||||
unsigned int smods;
|
||||
qboolean alive;
|
||||
ObjRefs_t allrefs;
|
||||
);
|
||||
#define ARRAYLISTITERATOR(o) ((ArrayListIterator *)(o))
|
||||
|
||||
|
|
|
@ -61,6 +61,12 @@
|
|||
|
||||
#define instanceOf(obj, cl) (Object_InstaceOf((Object *)obj, cl##_class))
|
||||
|
||||
typedef struct ObjRefs_s {
|
||||
struct Object_s **objs;
|
||||
unsigned int count;
|
||||
struct ObjRefs_s *next;
|
||||
} ObjRefs_t;
|
||||
|
||||
struct Object_s;
|
||||
struct Class_s;
|
||||
struct List_s;
|
||||
|
@ -70,10 +76,10 @@ typedef void (*ReplyHandler_t) (struct Object_s *retValue);
|
|||
typedef struct Object_s {
|
||||
struct Class_s *cl;
|
||||
int refs;
|
||||
qboolean junked;
|
||||
qboolean marked;
|
||||
struct Object_s *next;
|
||||
struct String_s * methodDecl(Object, toString);
|
||||
void methodDecl(Object, message, const char *name, struct List_s *args, struct Object_s *sender, ReplyHandler_t *reply);
|
||||
ObjRefs_t * methodDecl(Object, allRefs);
|
||||
void *data;
|
||||
|
||||
} Object;
|
||||
|
@ -99,6 +105,8 @@ void Object_Delete (Object *obj);
|
|||
Object *Object_Retain (Object *obj);
|
||||
Object *Object_Release (Object *obj);
|
||||
qboolean Object_InstanceOf (Object *obj, Class *cl);
|
||||
void Object_AddToRoot (Object *obj);
|
||||
void Object_RemoveFromRoot (Object *obj);
|
||||
void Object_Init (void);
|
||||
void Object_Garbage_Collect (void);
|
||||
|
||||
|
|
|
@ -32,8 +32,9 @@
|
|||
#ifndef __garbage_h
|
||||
#define __garbage_h
|
||||
|
||||
void Garbage_Junk_Object (Object *o);
|
||||
unsigned int Garbage_Amount (void);
|
||||
void Garbage_Collect (unsigned int amount);
|
||||
void Garbage_Do_Mark (Object *root);
|
||||
void Garbage_Do_Sweep (Object **allobjs);
|
||||
unsigned int Garbage_Pending (void);
|
||||
void Garbage_Dispose (unsigned int amount);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -46,6 +46,12 @@ static __attribute__ ((unused)) const char rcsid[] =
|
|||
|
||||
#include "QF/classes/ArrayList.h"
|
||||
|
||||
static ObjRefs_t *
|
||||
ArrayListIterator_AllRefs_f (Object *self)
|
||||
{
|
||||
return &ARRAYLISTITERATOR(self)->allrefs;
|
||||
}
|
||||
|
||||
static Object *
|
||||
ArrayListIterator_Next_f (Iterator *self)
|
||||
{
|
||||
|
@ -75,9 +81,13 @@ static Object *
|
|||
ArrayListIterator_Init_f (Object *self, ArrayList *list)
|
||||
{
|
||||
superInit (ArrayListIterator, self);
|
||||
self->allRefs = ArrayListIterator_AllRefs_f;
|
||||
ITERATOR(self)->next = ArrayListIterator_Next_f;
|
||||
ITERATOR(self)->hasNext = ArrayListIterator_HasNext_f;
|
||||
ARRAYLISTITERATOR(self)->list = ARRAYLIST(retain(list));
|
||||
ARRAYLISTITERATOR(self)->list = ARRAYLIST(list);
|
||||
ARRAYLISTITERATOR(self)->allrefs.objs = (Object **)&ARRAYLISTITERATOR(self)->list;
|
||||
ARRAYLISTITERATOR(self)->allrefs.count = 1;
|
||||
ARRAYLISTITERATOR(self)->allrefs.next = NULL;
|
||||
ARRAYLISTITERATOR(self)->smods = LIST(list)->smods;
|
||||
ARRAYLISTITERATOR(self)->pos = 0;
|
||||
ARRAYLISTITERATOR(self)->alive = COLLECTION(list)->count ? true : false;
|
||||
|
@ -87,7 +97,15 @@ ArrayListIterator_Init_f (Object *self, ArrayList *list)
|
|||
static void
|
||||
ArrayListIterator_Deinit_f (Object *self)
|
||||
{
|
||||
release(ARRAYLISTITERATOR(self)->list);
|
||||
}
|
||||
|
||||
static ObjRefs_t *
|
||||
ArrayList_AllRefs_f (Object *self)
|
||||
{
|
||||
ARRAYLIST(self)->allrefs.objs = ARRAYLIST(self)->elements;
|
||||
ARRAYLIST(self)->allrefs.count = COLLECTION(self)->count;
|
||||
ARRAYLIST(self)->allrefs.next = NULL;
|
||||
return &ARRAYLIST(self)->allrefs;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -109,13 +127,8 @@ ArrayList_Set_f (List *self, unsigned int index, Object *o)
|
|||
if (!Object_InstanceOf (o, COLLECTION(list)->type))
|
||||
return false;
|
||||
ArrayList_EnsureCapacity (list, index+1);
|
||||
retain(o);
|
||||
if (list->elements[index])
|
||||
release (list->elements[index]);
|
||||
list->elements[index] = o;
|
||||
} else if (index < COLLECTION(list)->count) {
|
||||
if (list->elements[index])
|
||||
release (list->elements[index]);
|
||||
list->elements[index] = NULL;
|
||||
}
|
||||
if (COLLECTION(list)->count < index+1)
|
||||
|
@ -151,7 +164,6 @@ ArrayList_InsertAt_f (List *self, unsigned int index, Object *o)
|
|||
return false;
|
||||
else {
|
||||
ArrayList_MakeRoomAt (list, index);
|
||||
retain(o);
|
||||
list->elements[index] = o;
|
||||
return true;
|
||||
}
|
||||
|
@ -165,8 +177,6 @@ ArrayList_RemoveAt_f (List *self, unsigned int index)
|
|||
return NULL;
|
||||
else {
|
||||
Object *o = list->elements[index];
|
||||
if (o)
|
||||
release(o);
|
||||
COLLECTION(list)->count--;
|
||||
memmove(list->elements+index, list->elements+index+1, sizeof(Object *) * (COLLECTION(list)->count-index));
|
||||
LIST(list)->smods++;
|
||||
|
@ -183,8 +193,6 @@ ArrayList_Add_f (Collection *self, Object *o)
|
|||
else {
|
||||
ArrayList_EnsureCapacity (list, ++COLLECTION(list)->count);
|
||||
list->elements[COLLECTION(list)->count-1] = o;
|
||||
if (o)
|
||||
retain(o);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -207,16 +215,13 @@ ArrayList_Init_f (Object *self, Class *type, Collection *source)
|
|||
COLLECTION(self)->add = ArrayList_Add_f;
|
||||
COLLECTION(self)->iterator = ArrayList_Iterator_f;
|
||||
superInit(List, self, type, source);
|
||||
self->allRefs = ArrayList_AllRefs_f;
|
||||
return self;
|
||||
}
|
||||
|
||||
static void
|
||||
ArrayList_Deinit_f (Object *self)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < COLLECTION(self)->count; i++)
|
||||
if (ARRAYLIST(self)->elements[i])
|
||||
release(ARRAYLIST(self)->elements[i]);
|
||||
free(ARRAYLIST(self)->elements);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ static __attribute__ ((unused)) const char rcsid[] =
|
|||
static String *
|
||||
Double_ToString_f (Object *self)
|
||||
{
|
||||
return new (String, va("%f", DOUBLE(self)->value));
|
||||
return newFloat(String, va("%f", DOUBLE(self)->value));
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -42,48 +42,61 @@ static __attribute__ ((unused)) const char rcsid[] =
|
|||
|
||||
#include "garbage.h"
|
||||
|
||||
unsigned int junked = 0;
|
||||
Object *junk = NULL;
|
||||
unsigned int junked = 0;
|
||||
|
||||
void
|
||||
Garbage_Junk_Object (Object *o)
|
||||
Garbage_Do_Mark (Object *root)
|
||||
{
|
||||
Sys_DPrintf ("GC: %s@%p ready for collection.\n", o->cl->name, o);
|
||||
if (!o->junked && !o->refs) {
|
||||
o->next = junk;
|
||||
junk = o;
|
||||
o->junked = true;
|
||||
junked++;
|
||||
if (!root->marked) {
|
||||
ObjRefs_t *allrefs;
|
||||
root->marked = true;
|
||||
Sys_DPrintf ("GC: Marked %s@%p.\n", root->cl->name, root);
|
||||
if (root->allRefs)
|
||||
for (allrefs = methodCall(root, allRefs); allrefs; allrefs = allrefs->next) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < allrefs->count; i++)
|
||||
Garbage_Do_Mark (allrefs->objs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Garbage_Do_Sweep (Object **allobjs)
|
||||
{
|
||||
Object **prevNext;
|
||||
Object *obj;
|
||||
|
||||
for (prevNext = allobjs, obj = *allobjs; obj; obj = *prevNext) {
|
||||
if (obj->marked) {
|
||||
obj->marked = false;
|
||||
prevNext = &obj->next;
|
||||
} else if (!obj->refs) {
|
||||
*prevNext = obj->next;
|
||||
obj->next = junk;
|
||||
junk = obj;
|
||||
junked++;
|
||||
Sys_DPrintf ("GC: %s@%p is ready for disposal...\n", obj->cl->name, obj);
|
||||
} else
|
||||
*prevNext = obj->next;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int
|
||||
Garbage_Amount (void)
|
||||
Garbage_Pending (void)
|
||||
{
|
||||
return junked;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Garbage_Collect (unsigned int amount)
|
||||
Garbage_Dispose (unsigned int amount)
|
||||
{
|
||||
Object *o;
|
||||
Object *next;
|
||||
|
||||
if (!amount)
|
||||
return;
|
||||
Sys_DPrintf ("GC: Collecting %u objects...\n", amount);
|
||||
|
||||
for (o = junk; o && amount; o = junk) {
|
||||
junk = o->next;
|
||||
if (!o->refs) {
|
||||
Sys_DPrintf("GC: Collecting %s@%p...\n", o->cl->name, o);
|
||||
Object_Delete (o);
|
||||
amount--;
|
||||
} else {
|
||||
Sys_DPrintf("GC: %s@%p gained references, not collecting...\n", o->cl->name, o);
|
||||
o->junked = false;
|
||||
}
|
||||
for (; junk && amount; junk = next, amount--) {
|
||||
next = junk->next;
|
||||
Sys_DPrintf ("GC: Disposing of %s@%p...\n", junk->cl->name, junk);
|
||||
Object_Delete (junk);
|
||||
junked--;
|
||||
}
|
||||
junk = o;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,9 @@ static __attribute__ ((unused)) const char rcsid[] =
|
|||
#include "QF/classes/Integer.h"
|
||||
#include "QF/classes/Double.h"
|
||||
|
||||
Object *allObjs = NULL;
|
||||
ArrayList *rootObj = NULL;
|
||||
|
||||
static String *
|
||||
Object_ToString_f (Object *self)
|
||||
{
|
||||
|
@ -67,6 +70,7 @@ Object_ToString_f (Object *self)
|
|||
static void
|
||||
Object_Init_f (Object *self)
|
||||
{
|
||||
self->allRefs = NULL;
|
||||
self->toString = Object_ToString_f;
|
||||
Sys_DPrintf("%s@%p initing...\n", self->cl->name, self);
|
||||
}
|
||||
|
@ -108,12 +112,13 @@ Object_Create (Class *cl, qboolean floating)
|
|||
{
|
||||
Object *new = malloc (cl->size);
|
||||
new->cl = cl;
|
||||
new->junked = false;
|
||||
if (floating) {
|
||||
new->refs = 0;
|
||||
Garbage_Junk_Object (new);
|
||||
} else
|
||||
new->refs = 1;
|
||||
new->marked = false;
|
||||
new->next = allObjs;
|
||||
allObjs = new;
|
||||
return new;
|
||||
}
|
||||
|
||||
|
@ -135,8 +140,8 @@ Object_Retain (Object *obj)
|
|||
Object *
|
||||
Object_Release (Object *obj)
|
||||
{
|
||||
if (obj->refs && --obj->refs == 0)
|
||||
Garbage_Junk_Object (obj);
|
||||
if (obj->refs)
|
||||
obj->refs--;
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -150,6 +155,18 @@ Object_InstanceOf (Object *obj, Class *cl)
|
|||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Object_AddToRoot (Object *obj)
|
||||
{
|
||||
methodCall(COLLECTION(rootObj), add, obj);
|
||||
}
|
||||
|
||||
void
|
||||
Object_RemoveFromRoot (Object *obj)
|
||||
{
|
||||
methodCall(COLLECTION(rootObj), remove, obj);
|
||||
}
|
||||
|
||||
static void
|
||||
Object_Test (void)
|
||||
{
|
||||
|
@ -169,6 +186,12 @@ Object_Test (void)
|
|||
methodCall(LIST(list), insertAt, 2, newFloat(String, "Mr. Two!"));
|
||||
liststr = methodCall(OBJECT(list), toString);
|
||||
Sys_DPrintf("List: %s\n", liststr->str);
|
||||
|
||||
list = newFloat(ArrayList, classObj(Object), NULL);
|
||||
methodCall(list, add, newFloat(String, "Don't free me!"));
|
||||
methodCall(list, add, newFloat(Integer, 5));
|
||||
methodCall(list, add, newFloat(Double, 3.14));
|
||||
Object_AddToRoot (OBJECT(methodCall(list, iterator)));
|
||||
}
|
||||
|
||||
Class *Object_class;
|
||||
|
@ -206,7 +229,7 @@ Object_Init (void)
|
|||
classInit(List);
|
||||
classInit(ArrayList);
|
||||
|
||||
|
||||
rootObj = new(ArrayList, classObj(Object), NULL);
|
||||
|
||||
/* Run test */
|
||||
Object_Test();
|
||||
|
@ -215,7 +238,18 @@ Object_Init (void)
|
|||
void
|
||||
Object_Garbage_Collect (void)
|
||||
{
|
||||
unsigned int amount;
|
||||
if ((amount = Garbage_Amount()))
|
||||
Garbage_Collect (amount / 2 + 1);
|
||||
static unsigned int frames = 0;
|
||||
|
||||
frames++;
|
||||
|
||||
if (frames % 2000 == 0) {
|
||||
Sys_DPrintf("GC: Marking...\n");
|
||||
Garbage_Do_Mark (OBJECT(rootObj));
|
||||
Sys_DPrintf("GC: Sweeping...\n");
|
||||
Garbage_Do_Sweep (&allObjs);
|
||||
}
|
||||
if (frames % 50 == 0 && Garbage_Pending()) {
|
||||
Sys_DPrintf("GC: Disposing...\n");
|
||||
Garbage_Dispose (Garbage_Pending()/2 + 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1907,13 +1907,13 @@ SV_GarbageCollect (void)
|
|||
if (pr_gc_count >= pr_gc_interval->int_val) {
|
||||
pr_gc_count = 0;
|
||||
PR_GarbageCollect (&sv_pr_state);
|
||||
Object_Garbage_Collect ();
|
||||
}
|
||||
} else {
|
||||
// Make sure the count gets reset if the gc is disabled. I
|
||||
// could use a callback, but I'm lazy
|
||||
pr_gc_count = 0;
|
||||
}
|
||||
Object_Garbage_Collect ();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Loading…
Reference in a new issue