October 10, 2013
Michel Fortin wrote:
Le 29-juin-2013 à 6:08, Jacob Carlborg a écrit :

> On 29 jun 2013, at 06:42, Walter Bright wrote:
>
>> I think the simplest thing is to not allow ref counted classes to implement interfaces other than ones derived from IUnknown.
>
> What about Objective-C interfaces?

Implementing ARC for Objective-C is going to require some more compiler support anyway (for autoreleased objects notably). Tweaking the compiler it so accepts Objective-C interfaces should just be a matter of tweaking the boolean expression that makes this check. "if (classdelc->objc && interfacedecl->objc) return true;" or something like that.

As for which function the compiler should call, it'll probably need to be special-cased for Objective-C in the compiler too. Here's the list of changes that'd be needed for Objective-C ARC:

== Retain ==
COM:  if (obj) obj->AddRef();
ObjC: obj = objc_retain(obj);
ObjC blocks: obj = objc_retainBlock(obj); // objc_retainBlock might do a copy

== Release ==
COM:  if (obj) obj->Release();
ObjC: objc_release(obj);

== Assignment ==
COM:  if (obj) obj->AddRef(); if (var) var->Release(); var = obj;
ObjC: objc_storeStrong(&var, obj);
ObjC blocks: obj = objc_retainBlock(obj); objc_release(var); var = obj;

As long as Walter implements D ARC in a way we can make the above substitutions it shouldn't be too hard.

Then, support for autorelease is a matter of calling objc_autorelease on returned objects from autoreleasing functions, followed by objc_retain after the function call in the caller. We'll also have to check what ObjC ARC does for pointer write-backs to autoreleased variables and mimick that.

There's an optimized path for autoreleased return values that we should use, but I'd defer that to later. It involves a special no-op instruction to insert at the right place as a flag. Also, autoreleased returns should be eliminated when inlining.

October 10, 2013
Looks like this should go into the O-C DIP.


On 6/29/2013 5:38 AM, Michel Fortin wrote:
>
> Implementing ARC for Objective-C is going to require some more compiler support anyway (for autoreleased objects notably). Tweaking the compiler it so accepts Objective-C interfaces should just be a matter of tweaking the boolean expression that makes this check. "if (classdelc->objc && interfacedecl->objc) return true;" or something like that.
>
> As for which function the compiler should call, it'll probably need to be special-cased for Objective-C in the compiler too. Here's the list of changes that'd be needed for Objective-C ARC:
>
> == Retain ==
> COM:  if (obj) obj->AddRef();
> ObjC: obj = objc_retain(obj);
> ObjC blocks: obj = objc_retainBlock(obj); // objc_retainBlock might do a copy
>
> == Release ==
> COM:  if (obj) obj->Release();
> ObjC: objc_release(obj);
>
> == Assignment ==
> COM:  if (obj) obj->AddRef(); if (var) var->Release(); var = obj;
> ObjC: objc_storeStrong(&var, obj);
> ObjC blocks: obj = objc_retainBlock(obj); objc_release(var); var = obj;
>
> As long as Walter implements D ARC in a way we can make the above substitutions it shouldn't be too hard.
>
> Then, support for autorelease is a matter of calling objc_autorelease on returned objects from autoreleasing functions, followed by objc_retain after the function call in the caller. We'll also have to check what ObjC ARC does for pointer write-backs to autoreleased variables and mimick that.
>
> There's an optimized path for autoreleased return values that we should use, but I'd defer that to later. It involves a special no-op instruction to insert at the right place as a flag. Also, autoreleased returns should be eliminated when inlining.
>
October 10, 2013
Jacob Carlborg:

On 29 jun 2013, at 22:24, Walter Bright wrote:

> Looks like this should go into the O-C DIP.

Shouldn't all this reference counting be in its own DIP?
October 10, 2013
On 6/30/2013 1:50 AM, Jacob Carlborg wrote:
> On 29 jun 2013, at 22:24, Walter Bright wrote:
>
>> Looks like this should go into the O-C DIP.
> Shouldn't all this reference counting be in its own DIP?
>

Yes. It's not ready yet, though.
October 10, 2013
Michel Fortin wrote:
Le 25-juin-2013 à 17:00, Walter Bright  a écrit :

> 6. If a class or struct contains RC fields, calls to Release() for those fields will
> be added to the destructor, and a destructor will be created if one doesn't exist already.

Another thing to note that the above is dangerous if the destructor is called from the GC and RC objects are allocated from GC memory. Referenced objects might already have been destroyed and you'll be calling Release() on them. This will happen when the GC releases a cycle.

October 10, 2013
On 6/30/2013 12:35 PM, Michel Fortin wrote:
> Le 25-juin-2013 à 17:00, Walter Bright  a écrit :
>
>> 6. If a class or struct contains RC fields, calls to Release() for those fields will
>> be added to the destructor, and a destructor will be created if one doesn't exist already.
> Another thing to note that the above is dangerous if the destructor is called from the GC and RC objects are allocated from GC memory. Referenced objects might already have been destroyed and you'll be calling Release() on them. This will happen when the GC releases a cycle.
>

Amended as:

6. If a class or struct contains RC fields, calls to Release() for those fields will
be added to the destructor, and a destructor will be created if one doesn't exist already.
Release() implementations should take care to not destroy objects that are already destroyed,
which can happen if the objects are allocated on the GC heap and the GC removes a cycle of
refcounted objects.
October 10, 2013
Michel Fortin wrote:
Le 2013-06-30 à 16:32, Walter Bright  a écrit :

> Amended as:
>
> 6. If a class or struct contains RC fields, calls to Release() for those fields will
> be added to the destructor, and a destructor will be created if one doesn't exist already.
> Release() implementations should take care to not destroy objects that are already destroyed,
> which can happen if the objects are allocated on the GC heap and the GC removes a cycle of
> refcounted objects.

Good advice. But... how do you implement that? For one thing, I doubt there's an API in the GC you can query for deleted objects, and if there was it'd be inefficient to call it for every call to Release. And also, will a virtual call to a function of a destroyed object work in the first place? It all seems quite fragile to me.

October 10, 2013
On 6/30/2013 3:05 PM, Michel Fortin wrote:
> Le 2013-06-30 à 16:32, Walter Bright  a écrit :
>
>> Amended as:
>>
>> 6. If a class or struct contains RC fields, calls to Release() for those fields will
>> be added to the destructor, and a destructor will be created if one doesn't exist already.
>> Release() implementations should take care to not destroy objects that are already destroyed,
>> which can happen if the objects are allocated on the GC heap and the GC removes a cycle of
>> refcounted objects.
> Good advice. But... how do you implement that? For one thing, I doubt there's an API in the GC you can query for deleted objects, and if there was it'd be inefficient to call it for every call to Release. And also, will a virtual call to a function of a destroyed object work in the first place? It all seems quite fragile to me.
>

The GC doesn't actually delete anything while it is doing a collection cycle. So the refcount could simply be checked.

October 10, 2013
Steven Schveighoffer wrote:

On Jun 30, 2013, at 6:11 PM, Walter Bright wrote:

>
> On 6/30/2013 3:05 PM, Michel Fortin wrote:
>> Le 2013-06-30 à 16:32, Walter Bright  a écrit :
>>
>>> Amended as:
>>>
>>> 6. If a class or struct contains RC fields, calls to Release() for those fields will
>>> be added to the destructor, and a destructor will be created if one doesn't exist already.
>>> Release() implementations should take care to not destroy objects that are already destroyed,
>>> which can happen if the objects are allocated on the GC heap and the GC removes a cycle of
>>> refcounted objects.
>> Good advice. But... how do you implement that? For one thing, I doubt there's an API in the GC you can query for deleted objects, and if there was it'd be inefficient to call it for every call to Release. And also, will a virtual call to a function of a destroyed object work in the first place? It all seems quite fragile to me.
>>
>
> The GC doesn't actually delete anything while it is doing a collection cycle. So the refcount could simply be checked.

AFAIK, this isn't a requirement of the GC.  May want to add it.  I have bad experiences with trying to second guess the GC and when it actually kills the object.

Note, if this is the case, then inc/dec refcount cannot depend on vtable, since that is zeroed.  I'm wondering if the GC shouldn't set the RC to size_t.max when destructing, or even just +1 it, to ensure the ref count destructor doesn't accidentally free it before the reaper does.

-Steve
October 10, 2013
Michel Fortin wrote:
Le 30-juin-2013 à 18:11, Walter Bright  a écrit :

> On 6/30/2013 3:05 PM, Michel Fortin wrote:
>> Le 2013-06-30 à 16:32, Walter Bright  a écrit :
>>
>>> Amended as:
>>>
>>> 6. If a class or struct contains RC fields, calls to Release() for those fields will
>>> be added to the destructor, and a destructor will be created if one doesn't exist already.
>>> Release() implementations should take care to not destroy objects that are already destroyed,
>>> which can happen if the objects are allocated on the GC heap and the GC removes a cycle of
>>> refcounted objects.
>> Good advice. But... how do you implement that? For one thing, I doubt there's an API in the GC you can query for deleted objects, and if there was it'd be inefficient to call it for every call to Release. And also, will a virtual call to a function of a destroyed object work in the first place? It all seems quite fragile to me.
>
> The GC doesn't actually delete anything while it is doing a collection cycle. So the refcount could simply be checked.


... checked and decremented, and if it reaches zero in the thread the GC is currently running then it doesn't have to delete the object as, in theory, it should be destructed as part of the same run. Ok, I get it now.

You should add a requirement that the reference counter be atomic because the GC can run in any thread and you still need to decrement counters of referenced objects in destructor.

Honestly, I think it'd be much easier if the runtime provided its own base object you could use for reference counting with the GC to collect cycles. The provided implementation could rely on internal details of the GC since both would be part of druntime. There isn't much room for alternate implementations when the GC is involved anyway.