Thread overview
An idea for GC and freeing of resources without finalization
Jun 17, 2021
rikki cattermole
Jun 17, 2021
rikki cattermole
Jun 17, 2021
rikki cattermole
June 17, 2021

Yesterday I got an idea of how we could remove destructors from classes and still free resources in situations where you rely on having a GC. I am sure some high level languages do something similar in their runtimes.

So the basic idea is that all resource handles is given a special type so that when you trace live objects you also record live handles (setting a bit in a bit array).

Then a resource manager (part of the GC infrastructure) can tell that some resource handles are no longer reachable and free them based on the dependency chains it has recorded.

E.g. opening a database might be one resource, opening queries on that database might be another set of resources that depends on the database. When query-handles are no longer reachable they get freed. When there are no resources depending on the database and no handle to the database is reachable the database is closed.

What is needed then is way to create resource managers and hook them up to the GC infrastructure. All allocation of resources happen through those managers.

The advantage of this is that one can just free memory instantly completely disregarding any handles that are being wiped out.

And there is no need to write destructor code.

But the lack of precise scanning is still a problem.

June 17, 2021
Alternatively we could make scope actually work for us.

With scope as a storage class we can assume that there is an owning point on the stack for a give handle (the language nor druntime is aware of the handle itself).

When that owning point goes out of scope, either the container gets destroyed or an operator overload gets called saying scope ends now!

Along with another operator overload that allows you to return a seperete reference to that handle when you try to escape scope, done!
June 17, 2021
On Thursday, 17 June 2021 at 10:50:41 UTC, rikki cattermole wrote:
> Alternatively we could make scope actually work for us.
>
> With scope as a storage class we can assume that there is an owning point on the stack for a give handle (the language nor druntime is aware of the handle itself).
>
> When that owning point goes out of scope, either the container gets destroyed or an operator overload gets called saying scope ends now!
>
> Along with another operator overload that allows you to return a seperete reference to that handle when you try to escape scope, done!

I am thinking that one does not exclude the other! :-D

So you would encourage people to free up resources explicitly, to avoid frequent collection, but maybe you want to quickly tear down a task, or maybe an exception was thrown in such a way that a resource handle was lost (e.g. in a constructor). Then you can still assume that the resource will be collected.

Either way, since all objects are traced anyway, and resource handles are fairly rare, it won't cost much to tag them when scanning pointers. I think the amortized cost would be close to zero!?

The core focus is to speed up collection, making source code cleaner and execution more robust. But yes, freeing resources early should be the main strategy, but I think people will fail to do this in more complicated patterns where resources are involved in more async code that stuff resources handles into a graph of some sort.




June 17, 2021
On 17/06/2021 11:02 PM, Ola Fosheim Grøstad wrote:
> I am thinking that one does not exclude the other! :-D

Having the GC act as a backup wouldn't be a bad thing!

More complex patterns shouldn't matter. Once you have an owning point on the stack, which may not be explicitly known (as long as the scope rules are meet, you should be able to figure it out locally).

But yes, exceptions worry me.
June 17, 2021

On Thursday, 17 June 2021 at 11:14:05 UTC, rikki cattermole wrote:

>

Having the GC act as a backup wouldn't be a bad thing!

Exactly, if it is there anyway, why not use it?

>

More complex patterns shouldn't matter. Once you have an owning point on the stack, which may not be explicitly known (as long as the scope rules are meet, you should be able to figure it out locally).

That won't work if you have many async connections. Then they are stored in a graph on the heap. My goal is to get rid of the need for finalizers and destructors, to speed up collection and make code easier to write.

So, for instance, if a fileobject is constructed with the file path and then has an open() and close() method. Then you could in Python-like fashion do something like

with Url("some/path/file.txt") as f {
    … f.read() …
}

Which basically calls f.open() then waits for the data to be ready and continues, then calls f.close().

Or you could

store_on_heap_read_when_ready(Url("some/path/file.txt").open())

Not necessarily this syntax, but you get the idea.

June 18, 2021
On 18/06/2021 12:02 AM, Ola Fosheim Grøstad wrote:
> That won't work if you have many async connections. Then they are stored in a graph on the heap. My goal is to get rid of the need for finalizers and destructors, to speed up collection and make code easier to write.

async like you are describing isn't limited to one thread, therefore yeah, need a different solution.