September 15, 2014
> As I understand the issue it works if you make sure to transfer ownership explicitly before the other thread gains access?
>
> Maybe this is more clear:
>
> http://www.1024cores.net/home/lock-free-algorithms/object-life-time-management/differential-reference-counting

 Ah, I think I follow.

So in C++ terms:
 It basically requires either a global shared_ptr, or that you passed one around by reference between threads. And that you then killed it in one thread at the exact moment the control block was read in another thread. That blog post discusses a solution, I wonder if that is implemented in C++'s shared_ptr?


September 15, 2014
On 2014-09-15 16:45, Andrei Alexandrescu wrote:
> On 9/15/14, 2:50 AM, monarch_dodra wrote:
>> - Does not provide Forward range iteration that I can find. This makes
>> it unuseable for algorithms:
>>      find (myRCString, "hello"); //Nope
>> Also, adding "save" to make it forward might not be a good idea, since
>> it would also mean it becomes an RA range (which it isn't).
>
> If we move forward with this type, traits will recognize it as
> isSomeString.
>
>> - Does not provide any way to (even "unsafely") extract a raw array.
>> Makes it difficult to interface with existing functions. It would also
>> be important for "RCString aware" functions to be properly optimized (eg
>> memchr for searching etc...)
>
> I think a @system unsafeSlice() property would be needed indeed.
>
>> - No way to "GC-dup" the RCString. giving "dup"/"idup" members on
>> RCstring, for when you really just need to revert to pure un-collected
>> GC.
>
> Nice. But then I'm thinking, wouldn't people think .dup produces another
> RCString?

Yes, most likely. How about "gcDup" or something like that.

-- 
/Jacob Carlborg
September 15, 2014
On Monday, 15 September 2014 at 19:43:42 UTC, po wrote:
>  Ah, I think I follow.
>
> So in C++ terms:
>  It basically requires either a global shared_ptr, or that you passed one around by reference between threads. And that you then killed it in one thread at the exact moment the control block was read in another thread. That blog post discusses a solution, I wonder if that is implemented in C++'s shared_ptr?

I think you need to either have multiple shared_ptr objects (owned by threads) or use atomic_* in cpp?

If I got this right for regular RC you have to increment the refcount before handing it to the other thread who is then responsible for decrementing it, but if the reference is obtained through a global datastructure you need the strong semantics in the blog post at 1024cores since you need to increase the count to take (thread) ownership of it before accessing it?

(I could be wrong.)
September 15, 2014

On 15.09.2014 11:31, Ola Fosheim Gr wrote:
> On Monday, 15 September 2014 at 18:08:31 UTC, po wrote:
>>
>>>> I'm not sure about that discussion, but there's good evidence from
>>>> C++ that refcounting with atomics works. What was the smoking gun?
>>>
>>> http://www.gotw.ca/gotw/045.htm
>>
>>  I don't see how that link answers Andrei's question? He just compares
>> different methods of implementing COW.
>
> As I understand the issue it works if you make sure to transfer
> ownership explicitly before the other thread gains access?
>
> Maybe this is more clear:
>
> http://www.1024cores.net/home/lock-free-algorithms/object-life-time-management/differential-reference-counting
>

This describes the scenario I meant in the ARC discussions.

Thanks for the link, I didn't know a solution exists. I'll have to study the "differential" approach to see if it works for our case and at what cost it comes...
September 15, 2014

On 15.09.2014 10:24, Andrei Alexandrescu wrote:
>>
>> Hmm, seems fine when I try it. It feels like a bug in the type system,
>> though: when you make a copy of const(RCXString) to some RCXString, it
>> removes the const from the referenced RCBuffer struct mbuf!?
>
> The conversion relies on pure constructors. As I noted in the opening
> post, I also think there's something too lax in there. If you have a
> reduced example that shows a type system breakage without cast, please
> submit.

Here's an example:

module module2;

struct S
{
	union
	{
		immutable(char)* iptr;
		char* ptr;
	}
}

void main()
{
	auto s = immutable(S)("hi".ptr);
	S t = s;
	t.ptr[0] = 'A';
}

It seems the union is hiding the fact that there are mutable references. Only the first field is verified when copying the struct. Is this by design? (typeof(s.ptr) is "immutable(char*)")
September 16, 2014
On Monday, 15 September 2014 at 23:41:27 UTC, Rainer Schuetze wrote:
> Thanks for the link, I didn't know a solution exists. I'll have to study the "differential" approach to see if it works for our case and at what cost it comes...

Modern x86 has 128 bit CAS instruction too: lock cmpxchg16b
September 16, 2014
On 9/15/14, 12:43 PM, po wrote:
>
>> As I understand the issue it works if you make sure to transfer
>> ownership explicitly before the other thread gains access?
>>
>> Maybe this is more clear:
>>
>> http://www.1024cores.net/home/lock-free-algorithms/object-life-time-management/differential-reference-counting
>>
>
>   Ah, I think I follow.
>
> So in C++ terms:
>   It basically requires either a global shared_ptr, or that you passed
> one around by reference between threads. And that you then killed it in
> one thread at the exact moment the control block was read in another
> thread. That blog post discusses a solution, I wonder if that is
> implemented in C++'s shared_ptr?

No, and it neeedn't. The article is not that good. In C++, if a thread must increment a reference counter while it's going to zero due to another thread, that's 100% a programming error, not a concurrency error. That's a well known and well studied problem. As an aside, searching the net for differential reference counting yields pretty much only this article.


Andrei

September 16, 2014

On 15.09.2014 21:49, Andrei Alexandrescu wrote:
> On 9/15/14, 12:43 PM, po wrote:
>>
>>> As I understand the issue it works if you make sure to transfer
>>> ownership explicitly before the other thread gains access?
>>>
>>> Maybe this is more clear:
>>>
>>> http://www.1024cores.net/home/lock-free-algorithms/object-life-time-management/differential-reference-counting
>>>
>>>
>>
>>   Ah, I think I follow.
>>
>> So in C++ terms:
>>   It basically requires either a global shared_ptr, or that you passed
>> one around by reference between threads. And that you then killed it in
>> one thread at the exact moment the control block was read in another
>> thread. That blog post discusses a solution, I wonder if that is
>> implemented in C++'s shared_ptr?
>
> No, and it neeedn't. The article is not that good. In C++, if a thread
> must increment a reference counter while it's going to zero due to
> another thread, that's 100% a programming error, not a concurrency
> error. That's a well known and well studied problem. As an aside,
> searching the net for differential reference counting yields pretty much
> only this article.

Huuh? So you must not read a reference to a ref-counted object that might get changed in another thread? Maybe you mean destruction of the shared pointer?

Please note that the scenario is also described by Richard Jones in his 2nd edition of the "Handbook of Garbage Collection" (see algorithm 18.2 "Eager reference counting with CompareAndSwap is broken").

September 16, 2014
On 9/15/14, 10:22 PM, Rainer Schuetze wrote:
>
>
> On 15.09.2014 21:49, Andrei Alexandrescu wrote:
>> On 9/15/14, 12:43 PM, po wrote:
>>>
>>>> As I understand the issue it works if you make sure to transfer
>>>> ownership explicitly before the other thread gains access?
>>>>
>>>> Maybe this is more clear:
>>>>
>>>> http://www.1024cores.net/home/lock-free-algorithms/object-life-time-management/differential-reference-counting
>>>>
>>>>
>>>>
>>>
>>>   Ah, I think I follow.
>>>
>>> So in C++ terms:
>>>   It basically requires either a global shared_ptr, or that you passed
>>> one around by reference between threads. And that you then killed it in
>>> one thread at the exact moment the control block was read in another
>>> thread. That blog post discusses a solution, I wonder if that is
>>> implemented in C++'s shared_ptr?
>>
>> No, and it neeedn't. The article is not that good. In C++, if a thread
>> must increment a reference counter while it's going to zero due to
>> another thread, that's 100% a programming error, not a concurrency
>> error. That's a well known and well studied problem. As an aside,
>> searching the net for differential reference counting yields pretty much
>> only this article.
>
> Huuh? So you must not read a reference to a ref-counted object that
> might get changed in another thread?

I didn't say that.

> Maybe you mean destruction of the
> shared pointer?

I meant: by the time the smart pointer got to the thread, its reference count has increased already.

> Please note that the scenario is also described by Richard Jones in his
> 2nd edition of the "Handbook of Garbage Collection" (see algorithm 18.2
> "Eager reference counting with CompareAndSwap is broken").

I agree such a problem may occur in code generated automatically under the wraps for high-level languages, but not with shared_ptr (or COM objects etc).


Andrei

September 16, 2014
On Tuesday, 16 September 2014 at 05:22:15 UTC, Rainer Schuetze wrote:
> Huuh? So you must not read a reference to a ref-counted object that might get changed in another thread?

A slice is two words, concurrently reading and writing them is not thread-safe in current GC model too, as another thread can get in between writing length and ptr fields, so it's not a new behavior.