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.