On 6/25/2013 6:09 PM, Michel Fortin wrote:
> ## Some general comments
>
> While its a start, this is hardly enough for Objective-C. Mostly for legacy reasons, most Objective-C methods return autoreleased objects (deferred release using an autorelease pool) based on a naming convention. Also, Objective-C objects can't be allocated from the D heap, so to avoid cycles we need weak pointers. More on Objective-C later.
>
> While it's good that a direct call to AddRef/Release is forbidden in @safe code, I think it should be forbidden in @system code too. The reason is that if the compiler is inserting calls to these automatically and you're also adding your own explicitly in the same function, it becomes practically impossible to reason about the reference counts, short of looking at the assembly. Instead, I think you should create a @noarc attribute for functions: it'll prevent the compiler for inserting any of those calls so it becomes the responsibility of the author to make those calls (which are then allowed). @noarc would be incompatible with @safe, obviously.

It's a good point, but adding such an attribute to the function may be too coarse, for one, and may cause composition problems, for another. Maybe just disallowing it altogether is the best solution.

>
> Finally, that's a nitpick but I wish you'd use function names that fit D better, such as opRetain and opRelease. Then you can add a "final void opRetain() { AddRef(); }" function to the IUnknown COM interface and we could do the same for Objective-C.

Makes sense.

>
> ## Objective-C autoreleased objects
>
> Objective-C is a special case. In Objective-C we need to know whether the returned object of a function is already retained or if it is deferred released (autoreleased). This is easily deducted from the naming convention. Occasionally, we might need to create autorelease pools too, but that can probably stay @system.
>
> (Note: all this idea of autoreleased objects might sound silly, but it was a great help before ARC, and Objective-C ARC has to be compatible with legacy code so it conforms to those conventions.)
>
> You can easily implement ARC for COM using an implementation of ARC for Objective-C, the reverse is not true because COM does not have this (old but still needed) concept of autorelease pools and deferred release where you need to know at each function boundary whether returned values (including those returned by pointer arguments) whether the object is expected to be retained or not.
>
> If I were you Walter, I would just not care about Objective-C idioms while implementing this feature at first. It'll have to be special cased anyway. Here's how I expect that'll be done:

From reading over that clang document, O-C arc is far more complex than I'd anticipated. I think it is way beyond what we'd want in regular D. It also comes with all kinds of pointer and function annotations - something I strongly want to avoid.

>
> What will need to be done later when adding Objective-C support is to add an internal "autoreleasedReturn" flag to a function that'll make codegen call "autorelease" in the callee when returning an object and "retain" in the caller where it receives an object from a function with that flag. Also, the same flag and behaviour is needed for out parameters (to mimick those cases where an object is returned by pointer). That flag will then be set automatically internally depending on the function name (only for Objective-C member functions), and it should be possible to override it explicitly with an attribute or a pragma of some sort. This is what Clang is doing, and we must match that to allow things to work.

I agree that this complexity should only be in O-C code.

>
> Checking for null is redundant in the Objective-C case: that check is done by the runtime. That's of minor importance, but it might impact performance and should probably special-cased in this case.
>
> ## Optimizations
>
> With Apple's implementation of reference counting (using global hash tables protected by spin locks), it is more efficient to update many counters in one operation. The codegen for Objective-C ARC upon assignement to a variable calls "objc_storeStrong(id *object, id value)", incrementing and decrementing the two counters presumably in one operation (as well as replacing the content of the variable pointed by the first argument with the new value).
>
> Ideally, the codegen for Objective-C ARC in D would call the same functions so we have the same performance. This means that codegen should make a call "objc_retain" when first initializing a variable, "objc_storeStrong" when doing an assignment, and "objc_release" when destructing a variable.
>
> As for returning autoreleased objects, there are two functions to choose from depending on whether the object needs to be retained at the same time nor not. (In general, the object needs to be retained prior autoreleasing if it comes from a variable not part of the function's stack frame.)
>
> Here's Clang's documentation for how it implements ARC:
> http://clang.llvm.org/docs/AutomaticReferenceCounting.html
>
> ## Objective-C weak pointers
>
> Weak pointers are essential in order to break retain cycles in Objective-C where there is no GC. They are implemented with the same kind of function calls as strong pointers. Unfortunately, Apple's Objective-C implementation won't sit well with D the way it works now.
>
> Weak pointers are implemented in Objective-C by registering the address of the pointer with the runtime. This means that when a pointer is moved from one location to another, the need to be notified of that through a call to objc_moveWeak. This breaks one assumption of D that you can move memory at will without calling anything.
>
> While we could still implement a working weak pointer with a template struct, that struct would have to allocate a pointer on the heap (where it is guarantied to not move) so it can store the true weak pointer recognized by the runtime. I'm not sure that would be acceptable, but at least it would work.
>
> ## More on reference counting
>
> I feel like I should share some of my thoughts here about a broader use of reference counting in D.
>
> First, we don't have to assume the reference counter has to be part of the object. Apple implements reference counting using global hash tables where the key is the address. It works very well.
>
> If we added a hash table like this for all memory allocated from the GC, we'd just have to find the base address of any memory block to get to its reference counter. I know you were designing with only classes in mind, but I want to point out that it is possible to reference-count everything the GC allocates if we want to.

D would need manual, RC and GC to coexist peacefully.

>
> The downside is that every assignment to a pointer anywhere has to call a function. While this is some overhead, it is more predictable than overhead from a GC scan and would be preferred in some situation (games I guess). Another downside is you have an object retained by being present on the stack frame of a C function, it'd have to be explicitly retained from elsewhere.

Doesn't this make it impractical to mix vanilla C with D code? An important feature of D is this capability, without worrying about a "JNI" style interface. As for D switching to a full refcounted GC for everything, I'm very hesitant for such a step. For one thing, reading the clang spec on all the various pointer and function annotations necessary is very off-putting.


>
> As for pointers not pointing to GC memory, the generic addRef/release functions can ignore those pointers just like the GC ignores them today when it does its scan.
>
> Finally, cycles can still be reclaimed by having the GC scan for them. Those scans should be less frequent however since most of the memory can be reclaimed through reference counting.
>
>
>

Reply via email to