ccontainersprogramming-languagesrefcounting

How to avoid freeing objects that are stored in containers with the same reference count


I have been working on some features of a custom programming language written in c. Currently i'm working on a system that does reference counting for objects in the language, which in c are represented as structs with among other things, a reference count.
There also is a feature which can free all currently allocated objects (say before the exit of the program to clean up all memory). Now here lies the problem exactly.

I have been thinking about how to do it best but i'm running into some problems. Let me sketch out the situation a bit:

2 new integers are allocated. both have reference count of 1
1 new list is allocated, also with a reference count of 1

now both integers go in the list, which gives them a reference count of 2
after these actions both integers go out of scope for some reason, so their reference count drops to 1 as they are still in the list.

Now i'm done with these objects so i run the function to delete all tracked objects. However, as you might have noticed both the list and the objects in the list have the same reference count (1). This means there is no way to decide which object to free first.

If i would free the integers before the list, the list will try to decrement the reference count on the integers which were freed before, which will segfault.

If the list would be freed before the integers, it would decrement the reference count of the integers to 0, which automatically frees them too and no further steps need to be taken to free the integers. They aren't tracked anymore.

Currently i have a system that works most of the time but not for the example i give above, where i free the objects based on their reference count. Highest count latest. This obviously only works as long as the integers have higher reference count than the list which is as visible in the example above, not always the case. (It only works assuming the integers didn't drop out of scope so they still have a higher reference count than the list)

Note: i have already found one way which i really don't like: adding a flag to every object indicating it is in a container so cant be freed. I don't like this because it adds some memory overhead to every allocated object, and when there is a circular dependency no object would be freed. Of course a cycle detector could fix this but preferably i'd like to do this with the reference counting only.

Let me give a concrete example of the described steps above:


//this initializes and sets a garbage collector object. 
//Basically it's a datastructure which records every allocated object,
//and is able to free them all or in the future 
//run some cycle detection on all objects. 
//It has to be set before allocating objects 

garbagecollector *gc = init_garbagecollector();
set_garbagecollector(gc);

//initialize a tracked object fromthe c integer value 10
myobject * a = myinteger_from_cint(10); 
myobject * b = myinteger_from_cint(10);

myobject * somelist = mylist_init();
mylist_append(somelist,a);
mylist_append(somelist,b);

// Simulate the going out of scope of the integers.
// There are no functions yet so i can't actually do it but this
// is a situation which can happen and has happened a couple of times
DECREF(a);
DECREF(b);

//now the program is done. all objects have a refcount of 1

//delete the garbagecollector and with that all tracked objects
//there is no way to prevent the integers being freed before the list
delete_garbagecollector(gc);

what of course should happen is that 100% of the time, the list is freed before the integers are.

What would be a smarter way of freeing all existing objects, in a way such that objects stored in containers aren't freed before the containers they're in?


Solution

  • It depends on your intention with:

    There also is a feature which can free all currently allocated objects (say before the exit of the program to clean up all memory).

    If the goal is to forcibly deallocate every single object regardless of its ref count, then I would have a separate chunk of code that walks the object graph and frees each object without touching its ref count. The ref count itself is going to end up freed too, so there's little point in updating it.

    If the goal is to just tell the system "We don't need the objects anymore" then another option is to simply walk the roots and decrement their ref counts. If there are no other references to them, they'll hit zero. They will then decrement the ref counts of everything they refer to before being deallocated. That in turn percolates through the object graph. If the roots are the only thing holding onto references at the point that you call this, it will effectively free everything.