Memory Safety without a GC or Ref Counting
F i L
witte2008 at gmail.com
Thu Jan 24 18:12:55 PST 2013
H. S. Teoh wrote:
> The problem is that quite often, the compiler will not be able
> to prove
> much about the data structure, esp. when you have non-trivial
> manipulation of pointers -- it requires solving the halting
> problem to
> achieve that level of analysis on arbitrary code -- so it will
> probably
> fall back to the GC quite often, depending on what the code
> does.
Well I misspoke on exactly what I meant by "falling back to
ref-counting".. I actually don't think you'd need anything like a
typical GC's or Ref-counting system, since this memory-model is
so rigid it gives a bit more guarantee on when something is
allocated and deleted, therefor when references need to be
cleaned can be a bit more optimized (at least I think).
For any local variables, most of the cleanup code can be
statically injected (like I illustrated above). Really the only
area you need to dynamically track and "nullify" references is
for dynamically allocating objects, like DynamicArrays. For them
we can fall-back on a sort of "Garbage Collection", however I
think it can be better (doesn't need cyclic detection, is
deterministic, etc). Let me illustrate...
In the compiler, our DynamicArray type might look like:
type DynamicArray(T) # template type
{
ptr pointer : T
var length : type(ptr)
ptr refs = MemoryPool(ref T[])
func => (index, r:ref T)
{
refs[index] += r
}
func -= (item)
{
...remove item...
var rfs = refs[getIndex(item)]
each r in rfs
{
if r ==> item
{
r => null
}
}
refs -= rfs
}
}
Basically, syntax like:
var foos = Foo[]
is just sugar for:
var foos = DynamicArray(Foo)
You'll notice that the DynamicArray type has a reference 'refs'
to a MemoryPool of type 'ref T[]'. This MemoryPool object would
probably be shared across multiple/all DynamicArrays for
optimization, but for now just think of it as a simple
dynamic-array-of-arrays itself.
The functions: '=>' & '-=' are operators, and happen whenever a
variable is referenced to an item, and when an item is removed,
respectively. To illustrate:
var nums = int[] # DynamicArray(int)
ref a, b : int
func main
{
nums += 1 # allocate
nums += 2 # allocate
nums += 3 # allocate
a => nums[2] # nums.=>(2, a)
b => a # nums.=>(2, b)
each i in nums
{
if runtime_condition
{
nums -= i # nums.-=(i)
}
}
}
So while this would search through a list of refs every time an
item was removed, it wouldn't have to do any complex cyclic
detection, or other such things. The MemoryPools could be a
universally managed heap of references that each DynamicArray
only held a slice-reference to, therefor allocating arrays would
still be cheap.
That's more of what I meant by falling back to "Ref-counting"
(more like reverse-referencing) type systems in cases where you
need them. Combined with all the optimizations that could occur
with local variables, I think a system like this could have a lot
of potential as an alternative to a thread-locking GC. Still... I
have this feeling like there's some obvious flaw in the design I
just can't see yet...
More information about the Digitalmars-d
mailing list