Memory Safety without a GC or Ref Counting
F i L
witte2008 at gmail.com
Mon Jan 21 09:37:36 PST 2013
So I've been thinking about this problem off and on for awhile,
and I think I have a rather simple solution. I highly doubt I'm
the first to think of it, but haven't found any info about a
similar idea on the net. So in the interest of science I'm
posting it here so anyone interested can attempt to poke holes in
the idea, which you folks have been so good at in the past.
This isn't really D related, but I usually like the feedback I
get from this community, plus, if there's any value in the idea,
maybe it would be a worth while discussion for future directions
D might make use of.
I'll be illustrating with Pseudo-code. The basic idea spawns from
conceptually separating variables as either "memory owners" or
"memory references". Basically, the pseudo-code has three types:
var - Memory Owner, never null, deleted at end of scope**
ref - Memory Reference, can't "own" memory
ptr - Unsafe manually managed memory pointer (like C pointers)
So, 'ptr's aside for now, 'var's would be the only way to
allocate memory, and memory is automatically allocated to them at
their definition**. Vars would be deleted at the end of their
scope (or when they're removed from dynamic-arrays, etc) unless
they're returned, in which case they're "injected" into the
callers scope, and deleted at the end of that scope. Without too
much lengthy explanation, here's some code:
type Foo
{
var bar = 0
}
func main
{
ref f : Foo
ref b : int
scope
{
var foo = Foo # allocates Foo
f => foo # f points to foo
f.bar = 1 # foo.bar = 1
b => f.bar # b points to foo.bar
b = 3 # foo.bar = 3
# auto 'clean up code' at end of scope:
#
# Memory.delete(foo)
# f => null
#
}
f.bar = 4 # ERROR: f is null
var n = 6
b => n
b = 7 # n = 7
# No clean-up code needed, 'n' is stack-allocated
#
# Also, notice we didn't need to set 'b' to 'null' at
# the end of the scope, since it wasn't accessed
# directly after the end of the scope.
}
So this is sort of like ref-counted scopes (smart scopes), except
the compiler has a higher degree of guarantee on the lifetime of
memory, and there's a lot of static analysis that can happen to
optimize the clean up code (like how we didn't need to assign 'b'
to null in the example above). All "ref counting" would be done
through static-analysis at compile-time, and injected by the
compiler, so it wouldn't have the runtime overhead of
ref-counting, nor would there be a need for "weak references"
since references are guaranteed to never "own" memory.
There's more to it of course. For instance, in order to have
expanding memory, you'd need the concepts of a dynamic-array and
type inheritance. You'd also need to not clean up memory that was
returned... so...
type Foo
{
var name : text
init { name = "Foo" }
func write { Console.write(name) }
}
type Bar : Foo
{
init { name = "Bar" }
}
ref global_foo : Foo
func main
{
var foos = Foo[]
foos += Foo # allocation
foos += Foo # allocation
foos += Bar # allocation
foos[].write() # prints: 'Foo', 'Foo', 'Bar'
foos -= foos[1] # deallocation
foos[].write() # prints: 'Foo', 'Bar'
# ---
func getFoo
{
var f = Foo
f.name = "Fip"
return f
# no cleanup, since 'f' is returned
}
global_foo = getFoo()
global_foo.write() # prints: 'Fip'
# auto cleanup:
#
# Memory.delete(foos)
# Memory.delete(global_foo)
# global_foo => null
#
}
Okay, so with that you could have global var arrays, and assign
global refs to function return values, etc. If you need extra
control, there's also 'ptr' types, which would require manual
clean-up. However, 'ptr's would be automatically cleaned when the
program exist, so they would be safe to use for global references
which only sometimes needed to point to usable memory.
Anyways.. comments and criticisms would be great. Sorry this
isn't more D related. Like I said, I hope it does have value D
might make use of at some point.
More information about the Digitalmars-d
mailing list