On Monday, 8 September 2014 at 15:55:53 UTC, monarch_dodra wrote:
I'm starting this thread related to two issues I'm encountering in regards to avoiding the GC, and the new @nogc attribute.

1) Issue 1)
The first issue is in regards to Throwables. The issue here is that they are allocated using the GC, so it is currently almost impossible to throw an exception in a @nogc context. This is (I think) a serious limitations. Do we have any plans, ideas, on how to solve this?

A particularly relevant example of this issue is `RefCounted`: This struct uses malloc to ref count an object, give a deterministic life cycle, and avoid the GC. Yet, since malloc can fail, it does this:
_store = cast(Impl*) enforce(malloc(Impl.sizeof));
Can you see the issue? This object which specifically avoids using the GC, end up NOT being @nogc.

Any idea how to approach this problem?

I know there are "workarounds", such as static pre-allocation, but that also comes with its own set of problems.

Maybe we could change it to say it's not legal to "hold on" to exceptions for longer than they are being thrown? Then, we could create the exceptions via allocators, which could deterministically delete them at specific points in time (or by the GC, if it is still running)? Just a crazy idea...

2) Issue 2)
The second issue is that data which is placed in non-GC *may* still need to be scanned, if it holds pointers. You can check for this with hasIndirections!T. This is what RefCounted and Array currently do. This is usually smart. There's a catch though. If the object you are storing happens to hold pointers, but NOT to GC data, they are still scanned.

A tell-tale example of this problem is Array!int. You'd think it's @nogc, right? The issue is that Array has a "Payload" object, into which you place malloc'ed memory for your ints. The Payload itself is placed in a RefCounted object. See where this is going?

Even though we *know* the Payload is malloc'ed, and references malloc'ed data, it is still added to the GC's ranges of scanned data. Even *if* we solved issue 1, then Array!int would still *not* be @nogc, even though it absolutely does not use the GC.

Just the same, an Array!(RefCounted!int) would also be scanned by the GC, because RefCounted holds pointers...

A *possible solution* to this problem would be to add an extra parameter to these templates called "ScanGC", which would be initialized to "hasIndirection!T". EG:
struct Array(T, bool ScanGC = hasIndirections!T)

Does this seem like a good idea? I don't really see any other way around this if we want generic code with manual memory management, that is "GC friendly" yet still useable in a @nogc context.

It's not really a solution, but use of Nullable (or a theoretical option type) and a Result type in @nogc code can help remove the need for exceptions. An interesting data point is that Rust has a try! macro, which wraps an expression and translates into something like the following:

let ratio = try!(div(x, y));

becomes

let ratio = match div(x, y)
{
    Ok(val) => val,
    Err(msg) => { return Err; }
}

Maybe we need a similar solution for @nogc.

Reply via email to