mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-01-18 06:51:47 +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,
|
classDecl (ArrayList, List,
|
||||||
unsigned int realsize;
|
unsigned int realsize;
|
||||||
Object **elements;
|
Object **elements;
|
||||||
|
ObjRefs_t allrefs;
|
||||||
);
|
);
|
||||||
#define ARRAYLIST(o) ((ArrayList *)(o))
|
#define ARRAYLIST(o) ((ArrayList *)(o))
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@ classDecl (ArrayListIterator, Iterator,
|
||||||
unsigned int pos;
|
unsigned int pos;
|
||||||
unsigned int smods;
|
unsigned int smods;
|
||||||
qboolean alive;
|
qboolean alive;
|
||||||
|
ObjRefs_t allrefs;
|
||||||
);
|
);
|
||||||
#define ARRAYLISTITERATOR(o) ((ArrayListIterator *)(o))
|
#define ARRAYLISTITERATOR(o) ((ArrayListIterator *)(o))
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,12 @@
|
||||||
|
|
||||||
#define instanceOf(obj, cl) (Object_InstaceOf((Object *)obj, cl##_class))
|
#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 Object_s;
|
||||||
struct Class_s;
|
struct Class_s;
|
||||||
struct List_s;
|
struct List_s;
|
||||||
|
@ -70,10 +76,10 @@ typedef void (*ReplyHandler_t) (struct Object_s *retValue);
|
||||||
typedef struct Object_s {
|
typedef struct Object_s {
|
||||||
struct Class_s *cl;
|
struct Class_s *cl;
|
||||||
int refs;
|
int refs;
|
||||||
qboolean junked;
|
qboolean marked;
|
||||||
struct Object_s *next;
|
struct Object_s *next;
|
||||||
struct String_s * methodDecl(Object, toString);
|
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;
|
void *data;
|
||||||
|
|
||||||
} Object;
|
} Object;
|
||||||
|
@ -99,6 +105,8 @@ void Object_Delete (Object *obj);
|
||||||
Object *Object_Retain (Object *obj);
|
Object *Object_Retain (Object *obj);
|
||||||
Object *Object_Release (Object *obj);
|
Object *Object_Release (Object *obj);
|
||||||
qboolean Object_InstanceOf (Object *obj, Class *cl);
|
qboolean Object_InstanceOf (Object *obj, Class *cl);
|
||||||
|
void Object_AddToRoot (Object *obj);
|
||||||
|
void Object_RemoveFromRoot (Object *obj);
|
||||||
void Object_Init (void);
|
void Object_Init (void);
|
||||||
void Object_Garbage_Collect (void);
|
void Object_Garbage_Collect (void);
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,9 @@
|
||||||
#ifndef __garbage_h
|
#ifndef __garbage_h
|
||||||
#define __garbage_h
|
#define __garbage_h
|
||||||
|
|
||||||
void Garbage_Junk_Object (Object *o);
|
void Garbage_Do_Mark (Object *root);
|
||||||
unsigned int Garbage_Amount (void);
|
void Garbage_Do_Sweep (Object **allobjs);
|
||||||
void Garbage_Collect (unsigned int amount);
|
unsigned int Garbage_Pending (void);
|
||||||
|
void Garbage_Dispose (unsigned int amount);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -46,6 +46,12 @@ static __attribute__ ((unused)) const char rcsid[] =
|
||||||
|
|
||||||
#include "QF/classes/ArrayList.h"
|
#include "QF/classes/ArrayList.h"
|
||||||
|
|
||||||
|
static ObjRefs_t *
|
||||||
|
ArrayListIterator_AllRefs_f (Object *self)
|
||||||
|
{
|
||||||
|
return &ARRAYLISTITERATOR(self)->allrefs;
|
||||||
|
}
|
||||||
|
|
||||||
static Object *
|
static Object *
|
||||||
ArrayListIterator_Next_f (Iterator *self)
|
ArrayListIterator_Next_f (Iterator *self)
|
||||||
{
|
{
|
||||||
|
@ -75,9 +81,13 @@ static Object *
|
||||||
ArrayListIterator_Init_f (Object *self, ArrayList *list)
|
ArrayListIterator_Init_f (Object *self, ArrayList *list)
|
||||||
{
|
{
|
||||||
superInit (ArrayListIterator, self);
|
superInit (ArrayListIterator, self);
|
||||||
|
self->allRefs = ArrayListIterator_AllRefs_f;
|
||||||
ITERATOR(self)->next = ArrayListIterator_Next_f;
|
ITERATOR(self)->next = ArrayListIterator_Next_f;
|
||||||
ITERATOR(self)->hasNext = ArrayListIterator_HasNext_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)->smods = LIST(list)->smods;
|
||||||
ARRAYLISTITERATOR(self)->pos = 0;
|
ARRAYLISTITERATOR(self)->pos = 0;
|
||||||
ARRAYLISTITERATOR(self)->alive = COLLECTION(list)->count ? true : false;
|
ARRAYLISTITERATOR(self)->alive = COLLECTION(list)->count ? true : false;
|
||||||
|
@ -87,7 +97,15 @@ ArrayListIterator_Init_f (Object *self, ArrayList *list)
|
||||||
static void
|
static void
|
||||||
ArrayListIterator_Deinit_f (Object *self)
|
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
|
static void
|
||||||
|
@ -109,13 +127,8 @@ ArrayList_Set_f (List *self, unsigned int index, Object *o)
|
||||||
if (!Object_InstanceOf (o, COLLECTION(list)->type))
|
if (!Object_InstanceOf (o, COLLECTION(list)->type))
|
||||||
return false;
|
return false;
|
||||||
ArrayList_EnsureCapacity (list, index+1);
|
ArrayList_EnsureCapacity (list, index+1);
|
||||||
retain(o);
|
|
||||||
if (list->elements[index])
|
|
||||||
release (list->elements[index]);
|
|
||||||
list->elements[index] = o;
|
list->elements[index] = o;
|
||||||
} else if (index < COLLECTION(list)->count) {
|
} else if (index < COLLECTION(list)->count) {
|
||||||
if (list->elements[index])
|
|
||||||
release (list->elements[index]);
|
|
||||||
list->elements[index] = NULL;
|
list->elements[index] = NULL;
|
||||||
}
|
}
|
||||||
if (COLLECTION(list)->count < index+1)
|
if (COLLECTION(list)->count < index+1)
|
||||||
|
@ -151,7 +164,6 @@ ArrayList_InsertAt_f (List *self, unsigned int index, Object *o)
|
||||||
return false;
|
return false;
|
||||||
else {
|
else {
|
||||||
ArrayList_MakeRoomAt (list, index);
|
ArrayList_MakeRoomAt (list, index);
|
||||||
retain(o);
|
|
||||||
list->elements[index] = o;
|
list->elements[index] = o;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -165,8 +177,6 @@ ArrayList_RemoveAt_f (List *self, unsigned int index)
|
||||||
return NULL;
|
return NULL;
|
||||||
else {
|
else {
|
||||||
Object *o = list->elements[index];
|
Object *o = list->elements[index];
|
||||||
if (o)
|
|
||||||
release(o);
|
|
||||||
COLLECTION(list)->count--;
|
COLLECTION(list)->count--;
|
||||||
memmove(list->elements+index, list->elements+index+1, sizeof(Object *) * (COLLECTION(list)->count-index));
|
memmove(list->elements+index, list->elements+index+1, sizeof(Object *) * (COLLECTION(list)->count-index));
|
||||||
LIST(list)->smods++;
|
LIST(list)->smods++;
|
||||||
|
@ -183,8 +193,6 @@ ArrayList_Add_f (Collection *self, Object *o)
|
||||||
else {
|
else {
|
||||||
ArrayList_EnsureCapacity (list, ++COLLECTION(list)->count);
|
ArrayList_EnsureCapacity (list, ++COLLECTION(list)->count);
|
||||||
list->elements[COLLECTION(list)->count-1] = o;
|
list->elements[COLLECTION(list)->count-1] = o;
|
||||||
if (o)
|
|
||||||
retain(o);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,16 +215,13 @@ ArrayList_Init_f (Object *self, Class *type, Collection *source)
|
||||||
COLLECTION(self)->add = ArrayList_Add_f;
|
COLLECTION(self)->add = ArrayList_Add_f;
|
||||||
COLLECTION(self)->iterator = ArrayList_Iterator_f;
|
COLLECTION(self)->iterator = ArrayList_Iterator_f;
|
||||||
superInit(List, self, type, source);
|
superInit(List, self, type, source);
|
||||||
|
self->allRefs = ArrayList_AllRefs_f;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ArrayList_Deinit_f (Object *self)
|
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);
|
free(ARRAYLIST(self)->elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ static __attribute__ ((unused)) const char rcsid[] =
|
||||||
static String *
|
static String *
|
||||||
Double_ToString_f (Object *self)
|
Double_ToString_f (Object *self)
|
||||||
{
|
{
|
||||||
return new (String, va("%f", DOUBLE(self)->value));
|
return newFloat(String, va("%f", DOUBLE(self)->value));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -42,48 +42,61 @@ static __attribute__ ((unused)) const char rcsid[] =
|
||||||
|
|
||||||
#include "garbage.h"
|
#include "garbage.h"
|
||||||
|
|
||||||
unsigned int junked = 0;
|
|
||||||
Object *junk = NULL;
|
Object *junk = NULL;
|
||||||
|
unsigned int junked = 0;
|
||||||
|
|
||||||
void
|
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 (!root->marked) {
|
||||||
if (!o->junked && !o->refs) {
|
ObjRefs_t *allrefs;
|
||||||
o->next = junk;
|
root->marked = true;
|
||||||
junk = o;
|
Sys_DPrintf ("GC: Marked %s@%p.\n", root->cl->name, root);
|
||||||
o->junked = true;
|
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++;
|
junked++;
|
||||||
|
Sys_DPrintf ("GC: %s@%p is ready for disposal...\n", obj->cl->name, obj);
|
||||||
|
} else
|
||||||
|
*prevNext = obj->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int
|
unsigned int
|
||||||
Garbage_Amount (void)
|
Garbage_Pending (void)
|
||||||
{
|
{
|
||||||
return junked;
|
return junked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Garbage_Collect (unsigned int amount)
|
Garbage_Dispose (unsigned int amount)
|
||||||
{
|
{
|
||||||
Object *o;
|
Object *next;
|
||||||
|
|
||||||
if (!amount)
|
for (; junk && amount; junk = next, amount--) {
|
||||||
return;
|
next = junk->next;
|
||||||
Sys_DPrintf ("GC: Collecting %u objects...\n", amount);
|
Sys_DPrintf ("GC: Disposing of %s@%p...\n", junk->cl->name, junk);
|
||||||
|
Object_Delete (junk);
|
||||||
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;
|
|
||||||
}
|
|
||||||
junked--;
|
junked--;
|
||||||
}
|
}
|
||||||
junk = o;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,9 @@ static __attribute__ ((unused)) const char rcsid[] =
|
||||||
#include "QF/classes/Integer.h"
|
#include "QF/classes/Integer.h"
|
||||||
#include "QF/classes/Double.h"
|
#include "QF/classes/Double.h"
|
||||||
|
|
||||||
|
Object *allObjs = NULL;
|
||||||
|
ArrayList *rootObj = NULL;
|
||||||
|
|
||||||
static String *
|
static String *
|
||||||
Object_ToString_f (Object *self)
|
Object_ToString_f (Object *self)
|
||||||
{
|
{
|
||||||
|
@ -67,6 +70,7 @@ Object_ToString_f (Object *self)
|
||||||
static void
|
static void
|
||||||
Object_Init_f (Object *self)
|
Object_Init_f (Object *self)
|
||||||
{
|
{
|
||||||
|
self->allRefs = NULL;
|
||||||
self->toString = Object_ToString_f;
|
self->toString = Object_ToString_f;
|
||||||
Sys_DPrintf("%s@%p initing...\n", self->cl->name, self);
|
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);
|
Object *new = malloc (cl->size);
|
||||||
new->cl = cl;
|
new->cl = cl;
|
||||||
new->junked = false;
|
|
||||||
if (floating) {
|
if (floating) {
|
||||||
new->refs = 0;
|
new->refs = 0;
|
||||||
Garbage_Junk_Object (new);
|
|
||||||
} else
|
} else
|
||||||
new->refs = 1;
|
new->refs = 1;
|
||||||
|
new->marked = false;
|
||||||
|
new->next = allObjs;
|
||||||
|
allObjs = new;
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,8 +140,8 @@ Object_Retain (Object *obj)
|
||||||
Object *
|
Object *
|
||||||
Object_Release (Object *obj)
|
Object_Release (Object *obj)
|
||||||
{
|
{
|
||||||
if (obj->refs && --obj->refs == 0)
|
if (obj->refs)
|
||||||
Garbage_Junk_Object (obj);
|
obj->refs--;
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,6 +155,18 @@ Object_InstanceOf (Object *obj, Class *cl)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Object_AddToRoot (Object *obj)
|
||||||
|
{
|
||||||
|
methodCall(COLLECTION(rootObj), add, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Object_RemoveFromRoot (Object *obj)
|
||||||
|
{
|
||||||
|
methodCall(COLLECTION(rootObj), remove, obj);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
Object_Test (void)
|
Object_Test (void)
|
||||||
{
|
{
|
||||||
|
@ -169,6 +186,12 @@ Object_Test (void)
|
||||||
methodCall(LIST(list), insertAt, 2, newFloat(String, "Mr. Two!"));
|
methodCall(LIST(list), insertAt, 2, newFloat(String, "Mr. Two!"));
|
||||||
liststr = methodCall(OBJECT(list), toString);
|
liststr = methodCall(OBJECT(list), toString);
|
||||||
Sys_DPrintf("List: %s\n", liststr->str);
|
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;
|
Class *Object_class;
|
||||||
|
@ -206,7 +229,7 @@ Object_Init (void)
|
||||||
classInit(List);
|
classInit(List);
|
||||||
classInit(ArrayList);
|
classInit(ArrayList);
|
||||||
|
|
||||||
|
rootObj = new(ArrayList, classObj(Object), NULL);
|
||||||
|
|
||||||
/* Run test */
|
/* Run test */
|
||||||
Object_Test();
|
Object_Test();
|
||||||
|
@ -215,7 +238,18 @@ Object_Init (void)
|
||||||
void
|
void
|
||||||
Object_Garbage_Collect (void)
|
Object_Garbage_Collect (void)
|
||||||
{
|
{
|
||||||
unsigned int amount;
|
static unsigned int frames = 0;
|
||||||
if ((amount = Garbage_Amount()))
|
|
||||||
Garbage_Collect (amount / 2 + 1);
|
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) {
|
if (pr_gc_count >= pr_gc_interval->int_val) {
|
||||||
pr_gc_count = 0;
|
pr_gc_count = 0;
|
||||||
PR_GarbageCollect (&sv_pr_state);
|
PR_GarbageCollect (&sv_pr_state);
|
||||||
Object_Garbage_Collect ();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Make sure the count gets reset if the gc is disabled. I
|
// Make sure the count gets reset if the gc is disabled. I
|
||||||
// could use a callback, but I'm lazy
|
// could use a callback, but I'm lazy
|
||||||
pr_gc_count = 0;
|
pr_gc_count = 0;
|
||||||
}
|
}
|
||||||
|
Object_Garbage_Collect ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
Loading…
Reference in a new issue