www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Precise GC state

reply Temtaime <temtaime gmail.com> writes:
Hi all !
https://github.com/dlang/druntime/pull/1603

Can someone investigate and bring it to us ?
4 years passed from gsoc 2013 and there's still no gc.
Many apps suffers from false pointers and bringing such a gc will 
help those who affected by it.
It seems all the tests are passed except win32 because of optlink 
failures.
Maybe there's some chance to accelerate this PR ?

Thanks all
Nov 22 2017
next sibling parent Adam Wilson <flyboynw gmail.com> writes:
On 11/22/17 02:53, Temtaime wrote:
 Hi all !
 https://github.com/dlang/druntime/pull/1603

 Can someone investigate and bring it to us ?
 4 years passed from gsoc 2013 and there's still no gc.
 Many apps suffers from false pointers and bringing such a gc will help
 those who affected by it.
 It seems all the tests are passed except win32 because of optlink failures.
 Maybe there's some chance to accelerate this PR ?

 Thanks all
+1 -- Adam Wilson IRC: LightBender import quiet.dlang.dev;
Nov 22 2017
prev sibling parent reply =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 22 November 2017 at 10:53:45 UTC, Temtaime wrote:
 Hi all !
 https://github.com/dlang/druntime/pull/1603
Only the Win32 build fails as Error: more than 32767 symbols in object file What's wrong?
Nov 22 2017
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Wednesday, 22 November 2017 at 13:23:54 UTC, Nordlöw wrote:
 On Wednesday, 22 November 2017 at 10:53:45 UTC, Temtaime wrote:
 Hi all !
 https://github.com/dlang/druntime/pull/1603
Only the Win32 build fails as Error: more than 32767 symbols in object file What's wrong?
Thats a linker(?) limitation for OMF (or whatever is the win32 object file format).
Nov 22 2017
next sibling parent Adam Wilson <flyboynw gmail.com> writes:
On 11/22/17 05:44, Nicholas Wilson wrote:
 On Wednesday, 22 November 2017 at 13:23:54 UTC, Nordlöw wrote:
 On Wednesday, 22 November 2017 at 10:53:45 UTC, Temtaime wrote:
 Hi all !
 https://github.com/dlang/druntime/pull/1603
Only the Win32 build fails as Error: more than 32767 symbols in object file What's wrong?
Thats a linker(?) limitation for OMF (or whatever is the win32 object file format).
We really should be using the MSVC linker on Windows for both x86 and x64. I propose we change the default behavior of -m32 to point to MSVC, keep the -m32mscoff, but mark it as deprecated, and add a -m32omf flag to retain the current behavior. -- Adam Wilson IRC: LightBender import quiet.dlang.dev;
Nov 22 2017
prev sibling parent reply =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Wednesday, 22 November 2017 at 13:44:22 UTC, Nicholas Wilson 
wrote:
 Thats a linker(?) limitation for OMF (or whatever is the win32 
 object file format).
Was just fixed! What improvements to D's concurrency model is made possible with this precise GC? I recall Martin Nowak saying at DConf 2016 that a precise GC will enable data with isolated or immutable indirections to be safely moved between threads
Nov 23 2017
parent reply Adam Wilson <flyboynw gmail.com> writes:
On 11/23/17 02:47, Nordlöw wrote:
 On Wednesday, 22 November 2017 at 13:44:22 UTC, Nicholas Wilson wrote:
 Thats a linker(?) limitation for OMF (or whatever is the win32 object
 file format).
Was just fixed! What improvements to D's concurrency model is made possible with this precise GC? I recall Martin Nowak saying at DConf 2016 that a precise GC will enable data with isolated or immutable indirections to be safely moved between threads
Rainer is awesome! That is certainly one aspect that it will enable. I would focus on a generational GC first for two reasons. The first is that you can delay the scans of the later gens if the Gen0 (nursery) has enough space, so for a lot of programs it would result in a significant cut-down in collection times. The second is that you still typically have to stop the execution of the thread on the Gen0 collection (the objects most likely to be hot). So with a non-generational concurrent collector you have to stop the thread for the entirety of the scan, because you have no way to know which objects are hot and which are cold. -- Adam Wilson IRC: LightBender import quiet.dlang.dev;
Nov 23 2017
next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Thursday, 23 November 2017 at 20:13:31 UTC, Adam Wilson wrote:
 I would focus on a generational GC first for two reasons. The
But generational GC only makes sense if many of your GC objects have a short life span. I don't think this fits well with sensible use of a language like D where you typically would try to put such allocations on the stack and/or use RAII or even an arena.
 The second is that you still typically have to stop the 
 execution of the thread on the Gen0 collection (the objects 
 most likely to be hot). So with a non-generational concurrent 
 collector you have to stop the thread for the entirety of the 
 scan, because you have no way to know which objects are hot and 
 which are cold.
How are you going to prove that references are all kept within the generation in D? There is some very costly book keeping involved that simply don't work well with D semantics.
Nov 23 2017
parent reply Adam Wilson <flyboynw gmail.com> writes:
On 11/23/17 13:40, Ola Fosheim Grøstad wrote:
 On Thursday, 23 November 2017 at 20:13:31 UTC, Adam Wilson wrote:
 I would focus on a generational GC first for two reasons. The
But generational GC only makes sense if many of your GC objects have a short life span. I don't think this fits well with sensible use of a language like D where you typically would try to put such allocations on the stack and/or use RAII or even an arena.
Sensible to whom? The great thing about D is that it works just we well for low-level people who need to wring the most out of the metal, AND the high-level productivity minded apps people, who don't. RAII+stack allocations make sense when I care about WHEN an object is released and wish to provide some semblance of control over deallocation (although as Andrei has pointed out numerous time, you have no idea how many objects are hiding under that RAII pointer). But there are plenty of use cases where I really don't care when memory is released, or even how long it takes to release. For example, all of the D apps that I've written and use in production, GC-allocate everything. I don't have a single struct in the code. But I don't care, because the program is so short lived and the working set so small that there will never be GC collection. And it's not like this is an uncommon or unwise pattern in D, DMD itself follows this exact pattern. Obviously my pattern isn't "wrong" or else DMD itself is "wrong". It's just not your definition of "correct". Another use case where RAII makes no sense is GUI apps. The object graph required to maintain the state of all those widgets can be an absolute performance nightmare on destruction. Closing a window can result in the destruction of tens-of-thousands of objects (I've worked on codebases like that), all from the GUI thread, causing a hang, and a bad user experience. Or you could use a concurrent GC and pass off the collection to a different thread. (See .NET)
 The second is that you still typically have to stop the execution of
 the thread on the Gen0 collection (the objects most likely to be hot).
 So with a non-generational concurrent collector you have to stop the
 thread for the entirety of the scan, because you have no way to know
 which objects are hot and which are cold.
How are you going to prove that references are all kept within the generation in D? There is some very costly book keeping involved that simply don't work well with D semantics.
Again, which semantics? If you compile with -betterc, the bookkeeping and it's overhead simply don't exist. Your arguments are based on a presupposition that D should only be used a certain way; a way that, I am sure, mirrors your own usage patterns. D supports a multitude of different usage patterns, some of which look nothing like what you are holding up as "correct". And this is what makes D special. To remove or dismiss as invalid those usage patterns would be detrimental to those of us who use them and be catastrophic to D in general. As a community, can we please stop it with the subjective judgements of what is and is not "sensible" in D, and start supporting the people using it, however they are using it, even if we are sure that they are "wrong"? -- Adam Wilson IRC: LightBender import quiet.dlang.dev;
Nov 23 2017
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Friday, 24 November 2017 at 05:34:14 UTC, Adam Wilson wrote:
 On 11/23/17 13:40, Ola Fosheim Grøstad wrote:
 On Thursday, 23 November 2017 at 20:13:31 UTC, Adam Wilson 
 wrote:
 I would focus on a generational GC first for two reasons. The
But generational GC only makes sense if many of your GC objects have a short life span. I don't think this fits well with sensible use of a language like D where you typically would try to put such allocations on the stack and/or use RAII or even an arena.
Sensible to whom? The great thing about D is that it works just we well for low-level people who need to wring the most out of the metal, AND the high-level productivity minded apps people, who don't.
The problem with generational GC is “write barriers” - pieces of code compiler needs to insert around every write of a pointer. Now given that we can cast pointer to integer and back (also unions), I don’t see how it would work unless we put write barriers everywhere on the off-chance it did wrote a pointer from OLD to NEW space. A better GC is a great direction. Generational one is not feasible unless we disallow quite a few of our features.
Nov 23 2017
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Friday, 24 November 2017 at 05:53:37 UTC, Dmitry Olshansky 
wrote:
 A better GC is a great direction. Generational one is not 
 feasible unless we disallow quite a few of our features.
What about safe?
Nov 25 2017
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Sunday, 26 November 2017 at 04:01:31 UTC, jmh530 wrote:
 On Friday, 24 November 2017 at 05:53:37 UTC, Dmitry Olshansky 
 wrote:
 A better GC is a great direction. Generational one is not 
 feasible unless we disallow quite a few of our features.
What about safe?
If all of the code is 100% safe (not system and not trusted) you have a different language where write barriers would be cheaper to implement. Sadly you can’t “skip” write barriers in your system code because it may run as part of larger safe. Which is where they are the most costly.
Nov 26 2017
next sibling parent Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
On Sunday, 26 November 2017 at 08:49:42 UTC, Dmitry Olshansky 
wrote:
 Sadly you can’t “skip” write barriers in your  system code 
 because it may run as part of larger  safe. Which is where they
Well, you can if you carefully lock the gc runtime or if you dont modify existing scannable pointers that points to existing objects (e.g. You could fill an empty array of pointers in unsafe code or pointers that point to something unscannable.), but all unsafe code would need vetting. So it isnt impossible technically, but it is impossible without a change of philosophy.
Nov 26 2017
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Sunday, 26 November 2017 at 08:49:42 UTC, Dmitry Olshansky 
wrote:
 If all of the code is 100%  safe (not system and not trusted) 
 you have a different language where write barriers would be 
 cheaper to implement.

 Sadly you can’t “skip” write barriers in your  system code 
 because it may run as part of larger  safe. Which is where they 
 are the most costly.
I was thinking you would use a generational or precise GC for safe code and then fall back to the normal GC with system/ trusted code. Not sure if that's possible or not, but in my head it would be a separate heap from the safe code. Certainly would be added complexity.
Nov 26 2017
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, November 26, 2017 18:58:04 jmh530 via Digitalmars-d wrote:
 On Sunday, 26 November 2017 at 08:49:42 UTC, Dmitry Olshansky

 wrote:
 If all of the code is 100%  safe (not system and not trusted)
 you have a different language where write barriers would be
 cheaper to implement.

 Sadly you can’t “skip” write barriers in your  system code
 because it may run as part of larger  safe. Which is where they
 are the most costly.
I was thinking you would use a generational or precise GC for safe code and then fall back to the normal GC with system/ trusted code. Not sure if that's possible or not, but in my head it would be a separate heap from the safe code. Certainly would be added complexity.
It wouldn't work. safe code and system code call each other all the time (using trusted where necessary), and they freely exchange stuff that was allocated on the GC heap. Heck, it's not even possible to have per-thread heaps (much as that would be desirable), because stuff can be passed between threads, and you can freely cast between shared and non-shared. You do have to be careful about it if you don't want to have problems, but just like safe code can call trusted code such that it's possible for safe code be calling code that was vetted for memory safety by a programmer rather than the compiler, you can have code that casts to and from shared and works perfectly well so long as the programmer vets it properly. We can't even have different heaps for immutable and mutable stuff, because it's very common to construct something as mutable and then cast it to immutable (either explicitly or because it's returned from a pure function which is able to do the cast implicitly, because it knows that the return value couldn't possibly have been passed into the function and thus had to have been allocated inside it). D's type system protects you from all kinds of dumb mistakes, but it has way too many backdoors for the kind of stuff that requires that things be locked down like they would be inside a VM like Java has. Ultimately, if you're talking about a GC and what it can and can't do, I expect that there's very little difference between C and D in terms of what you types of GC you can get away with. D does protect the programmer, but ultimately, it lets you do just about anything that C lets you do if you really want to, and any GC that we use has to work with that. - Jonathan M Davis
Nov 26 2017
next sibling parent Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
On Sunday, 26 November 2017 at 19:11:08 UTC, Jonathan M Davis 
wrote:
 We can't even have different heaps for immutable and mutable 
 stuff, because it's very common to construct something as 
 mutable and then cast it to immutable (either explicitly or
This is easy to fix, introduce a uniquely owned type (isolated) that only can transition to immutable. So it is more about being willing to tighten up the semantics. Same thing with GC, but everything has a cost. That said Adam has a point with getting more users, it isnt obvious that the costs wouldnt be offset by increased interest. in by gradually expanding into more performance oriented programming mechanisms... We'll see.
Nov 26 2017
prev sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Sunday, 26 November 2017 at 19:11:08 UTC, Jonathan M Davis 
wrote:
 It wouldn't work.  safe code and  system code call each other 
 all the time (using  trusted where necessary), and they freely 
 exchange stuff that was allocated on the GC heap. [snip]
I see. Fair enough.
Nov 26 2017
prev sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Sunday, 26 November 2017 at 18:58:04 UTC, jmh530 wrote:
 On Sunday, 26 November 2017 at 08:49:42 UTC, Dmitry Olshansky 
 wrote:
 If all of the code is 100%  safe (not system and not trusted) 
 you have a different language where write barriers would be 
 cheaper to implement.

 Sadly you can’t “skip” write barriers in your  system code 
 because it may run as part of larger  safe. Which is where 
 they are the most costly.
I was thinking you would use a generational or precise GC for safe code and then fall back to the normal GC with system/ trusted code.
Wishful thinking. Memory flows through a program and things allocated in safe get passed to system (including the last reference cases) and vise versa. Also in D generational hypothesis may not bear as much value due to stack-allocation, libc heap allocations for temporaries likely via RAII. Not to mention cheap (thread-local) Ref Counting, C++ and many other language have to use atomics which makes RC costly. Basically the GC heap is not all of the heap, so a young objects is even smaller part of that. Lastly it is telling that new low-latency concurrent GCs do away without explicit generations. I’m thinking a more efficient way to takle temporaries would be to add Regions to GC. So the moment you enter a region all GC things are allocated in a special segment, once you leave it free the segment. The last trick is to tweak memory protection as guard pages over it. Boom! Now if somebody touches it - DanglingPointerException :) Not a panacea but: - libraries using too much of GC can be controlled (unless they build global state) - manual memory management without memory corruption - fast bump allocation, which is what all of sensible VM languages do And no need to create entire separate heap with its own generational schema.
 Not sure if that's possible or not, but in my head it would be 
 a separate heap from the  safe code.
And then I do append in system on array allocated in safe. Boom!
 Certainly would be added complexity.
Nov 26 2017
parent reply Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
On Monday, 27 November 2017 at 05:47:49 UTC, Dmitry Olshansky 
wrote:
 likely via RAII. Not to mention cheap (thread-local) Ref 
 Counting, C++ and many other language have to use atomics which 
 makes RC costly.
No, you dont. Nobody in their right mind would do so in C++ as a general solution. Seems there is trend in doing D-advocacy based on the assumption that programmers using other languages are crazy these days. In C++ sync is manual, which is the only efficient way to do it. Proving correctness for an efficient general solution is an unsolved theoretical problem. You can do it for high level mechanisms, but not low level atm. Rust and Pony claims to have solutions, but they are not general. D most certainly does not have it and never will. When threading is a libray type then you cannot achieve more in D than you can achieve in C++, i.e. Shared is not going to do more than a C++ library type with a separate static analysis tool.
Nov 26 2017
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Monday, 27 November 2017 at 06:36:27 UTC, Ola Fosheim Grostad 
wrote:
 On Monday, 27 November 2017 at 05:47:49 UTC, Dmitry Olshansky 
 wrote:
 likely via RAII. Not to mention cheap (thread-local) Ref 
 Counting, C++ and many other language have to use atomics 
 which makes RC costly.
No, you dont. Nobody in their right mind would do so in C++ as a general solution. Seems there is trend in doing D-advocacy based on the assumption that programmers using other languages are crazy these days.
Last time I check shared_ptr can be safely shared across threads, hence RC is takling synchronization and most likely atomics since locks won’t be any better.
 In C++ sync is manual, which is the only efficient way to do
??? shared_ptr is nowhere manual. Sorry but the rest of the of post is take a direction into the fog, that I don’t want to follow. My post is about particular primitive in C++ std, what could be done instead or in addition to is not important.
Nov 26 2017
parent reply Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
On Monday, 27 November 2017 at 06:47:00 UTC, Dmitry Olshansky 
wrote:
 Last time I check shared_ptr can be safely shared across 
 threads, hence RC is takling synchronization and most likely 
 atomics since locks won’t be any better.
The controlblock can, but it is crazy to use shared_ptr for anything more than high level ownership. It is a general solution with weak pointers and extra indirection, not a typical RC implementation for datastructures.
 In C++ sync is manual, which is the only efficient way to do
??? shared_ptr is nowhere manual.
There is an upcoming atomic_shared_ptr, but it is not in the standard yet.
 My post is about particular primitive in C++ std, what could be 
 done instead or in addition to is not important.
Oh, but it is. 1. D currently does not provide what you says it does. 2. Sane C++ programmers rarely use shared_ptr for more than exchsnging ownership (suitable for sharing things like bitmap textures). There are plenty of other RC implementations for tracking memory. So you compare apples and oranges.
Nov 26 2017
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Monday, 27 November 2017 at 07:03:01 UTC, Ola Fosheim Grostad 
wrote:
 On Monday, 27 November 2017 at 06:47:00 UTC, Dmitry Olshansky 
 wrote:
 Last time I check shared_ptr can be safely shared across 
 threads, hence RC is takling synchronization and most likely 
 atomics since locks won’t be any better.
The controlblock can, but it is crazy to use shared_ptr for anything more than high level ownership. It is a general solution with weak pointers and extra indirection, not a typical RC implementation for datastructures.
Then watch Herb’s Sutter recent talk “Leak freedom by default”. Now THAT guy must be out of his mind :) I have no idea what are your typical C++ programmers.
 In C++ sync is manual, which is the only efficient way to do
??? shared_ptr is nowhere manual.
There is an upcoming atomic_shared_ptr, but it is not in the standard yet.
atomic_shared_pointer is nothing but what you seem to imply. It’s not manual sync for one.
 My post is about particular primitive in C++ std, what could 
 be done instead or in addition to is not important.
Oh, but it is.
You keep spreading FUD on this forum, I’m still not sure of your reasons though.
 1. D currently does not provide what you says it does.
RefCounted!T
 2. Sane C++ programmers rarely use shared_ptr for more than 
 exchsnging ownership (suitable for sharing things like bitmap 
 textures).
Including across threads btw.
 There are plenty of other RC implementations for tracking 
 memory.
Like what? In standard C++ all you’ve got is unique_ptr and shared_ptr (and weak but that besides the point). There is maybe a ton of reimplementations of said things.
Nov 27 2017
parent reply Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
On Monday, 27 November 2017 at 14:35:03 UTC, Dmitry Olshansky 
wrote:
 Then watch Herb’s Sutter recent talk “Leak freedom by default”. 
 Now THAT guy must be out of his mind :)
He could be, I havent seen it... Shared_ptr isnt frequently used, it is a last resort,
 atomic_shared_pointer is nothing but what you seem to imply. 
 It’s not manual sync for one.
Err... That was my point... Only assignment and reset is protected in shared_ptr, all other methods require manual sync.
 You keep spreading FUD on this forum, I’m still not sure of 
 your reasons though.
And there we go ad hominem as usual... With no argument to back it up... Bye.
Nov 27 2017
next sibling parent Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
Btw, it would improve the discourse if people tried to 
distinguish between language constructs and library constructs...
Nov 27 2017
prev sibling next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Monday, 27 November 2017 at 15:56:09 UTC, Ola Fosheim Grostad 
wrote:
 On Monday, 27 November 2017 at 14:35:03 UTC, Dmitry Olshansky 
 wrote:
 Then watch Herb’s Sutter recent talk “Leak freedom by 
 default”. Now THAT guy must be out of his mind :)
He could be, I havent seen it... Shared_ptr isnt frequently used, it is a last resort,
Ahhhaaahhha. Really, shared_ptr is the most contagious primitive of modern C++. To quote MS STL guy “I’m surprised we had no non-inteusive ref-counted ptr in std lib for so long”. Going Native videos are full of questions on that.
 atomic_shared_pointer is nothing but what you seem to imply. 
 It’s not manual sync for one.
Err... That was my point... Only assignment and reset is protected in shared_ptr, all other methods require manual sync.
For the benefit of others, let me destroy that: - shared_ptr allows to share T with thread-safe ownership, ref-counts are accounted atomically (sharing copies of shared_ptr pointing to the same block). Copy of shared_ptr is thread safe and does the count. - atomic_shared_prt would also allow one to initialize a shared variable (eg global) of type shared_ptr safely from multiple threads The manual synchronization part comes in if you try to work with payload T itself. THAT is manual. Since C++ doesn’t see a difference at type level of shared vs local, there is no thread-local variation of shared_ptr. It would be too unsafe even for those guys, contrary to what Ola responses imply.
 You keep spreading FUD on this forum, I’m still not sure of 
 your reasons though.
And there we go ad hominem as usual... With no argument to back it up... Bye.
Well I can clearly see misinformation and describe it as such. See your point about atomic_shared_ptr.
Nov 27 2017
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 27 November 2017 at 17:16:50 UTC, Dmitry Olshansky 
wrote:
 Really, shared_ptr is the most contagious primitive of modern 
 C++.
Not really. Unique_ptr is, though.
 To quote MS STL guy “I’m surprised we had no non-inteusive 
 ref-counted ptr in std lib for so long”.
 Going Native videos are full of questions on that.
Yeah, they tend to pretend that C++ is a high level language, it makes for good talks. Let me put it this way, C++ is an acceptable low level language, but it is a rather poor high level language. So if you talk with people who use C++ as a high level language you probably see different usage patterns.
 Err... That was my point... Only assignment and reset is 
 protected in shared_ptr, all other methods require manual sync.
For the benefit of others, let me destroy that:
Oh well, looks like reset and assignment isn't required per the spec to be protected against races either. That doesn't weaken my point, the control block is protected but not the methods of shared_ptr. I hardly ever use shared_ptr. In practical programming you usually need to exchange more than a single entity so you need manual sync anyway.
 - shared_ptr allows to share T with thread-safe ownership, 
 ref-counts are accounted atomically (sharing copies of 
 shared_ptr pointing to the same block). Copy of shared_ptr is 
 thread safe and does the count.
Not sure if I follow that description. Shared shared_ptr's are not thread safe, a shared control block is. (Technically you share a pointer, not an object of type T, but that is a minor detail.) shared_ptr is pointing to a shared control block that in turn has a pointer that points to the resource. This control block contains two counters. These counters are incremented and decremented atomically. If you access the same shared_ptr from two threads then you have a potential race condition per the spec.
 - atomic_shared_prt would also allow one to initialize a shared 
 variable (eg global) of type shared_ptr safely from multiple 
 threads
The main point is that the methods of atomic_shared_ptr are protected against races. It is needed because you usually would have have ownership pointers embedded in a another shared object. So having a protected control-block is not sufficient outside the trivial toy-program.
 The manual synchronization part comes in if you try to work 
 with payload T itself. THAT is manual.
No, none of the methods on shared_ptr guarantees that races won't happen. And in general you usually want to access more than one unit in a critical section. So in C++ you typically need manual sync. There are no suitable language features to get around that in C++ and library features are inadequate in the general sense. Go and Pony have language features that are helpful. C++ doesn't.
 Since C++ doesn’t see a difference at type level of shared vs 
 local, there is no thread-local variation of shared_ptr. It 
 would be too unsafe even for those guys, contrary to what Ola 
 responses imply.
Sigh. I implied that shared_ptr on a single thread is mostly useless. But this: struct OurSharedCache { shared_ptr<T> something; shared_ptr<T> somethingelse } Not safe.
 Well I can clearly see misinformation and describe it as such. 
 See your point about atomic_shared_ptr.
What about it? «If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur» http://en.cppreference.com/w/cpp/memory/shared_ptr «The class template atomic_shared_ptr provides thread-safe atomic pointer operations over a std::shared_ptr.» http://en.cppreference.com/w/cpp/experimental/atomic_shared_ptr
Nov 27 2017
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Monday, 27 November 2017 at 18:29:56 UTC, Ola Fosheim Grøstad 
wrote:
 On Monday, 27 November 2017 at 17:16:50 UTC, Dmitry Olshansky 
 wrote:
 Really, shared_ptr is the most contagious primitive of modern 
 C++.
Not really. Unique_ptr is, though.
 To quote MS STL guy “I’m surprised we had no non-inteusive 
 ref-counted ptr in std lib for so long”.
 Going Native videos are full of questions on that.
Yeah, they tend to pretend that C++ is a high level language, it makes for good talks. Let me put it this way, C++ is an acceptable low level language, but it is a rather poor high level language. So if you talk with people who use C++ as a high level language you probably see different usage patterns.
I’ve seen a tech giant that works on uber high-performance things making heavy use of STL, and being fond of C++14 “high-level” features. I almost took their job offer, it is a nice place. Comparatively I’ve worked at Google where none of the new goodies are in use. There are some great things in their “std”, but most have horrible interfaces and people routinelly discuss how having at least STL-like primitives would be a huge boon.
 Err... That was my point... Only assignment and reset is 
 protected in shared_ptr, all other methods require manual 
 sync.
For the benefit of others, let me destroy that:
Oh well, looks like reset and assignment isn't required per the spec to be protected against races either. That doesn't weaken my point, the control block is protected but not the methods of shared_ptr. I hardly ever use shared_ptr. In practical programming you
That must be why you seriously have no idea how people use it. Otherwise you’d know that nobody shares a reference to shared pointer but rather a copy. The whole point of smart pointer is to avoid naked references.
 - shared_ptr allows to share T with thread-safe ownership, 
 ref-counts are accounted atomically (sharing copies of 
 shared_ptr pointing to the same block). Copy of shared_ptr is 
 thread safe and does the count.
Not sure if I follow that description. Shared shared_ptr's are not thread safe
They are if you use them as intended - value types, that pretend to be pointers.
 a shared control block is. (Technically you share a pointer, 
 not an object of type T, but that is a minor detail.)
No, you actually share an object and the last one to decrement the ref will destroy it. Now that is actually thread-safe thanks to atomic counter.
 shared_ptr is pointing to a shared control block that in turn 
 has a pointer that points to the resource. This control block  
 contains two counters. These counters are incremented and 
 decremented atomically.
That is my original point, which you now violently agree with :)
 If you access the same shared_ptr from two threads then you 
 have a potential race condition per the spec.
Just underlines that you don’t understand how it is supposed to be used.
 - atomic_shared_prt would also allow one to initialize a 
 shared variable (eg global) of type shared_ptr safely from 
 multiple threads
The main point is that the methods of atomic_shared_ptr are protected against races. It is needed because you usually would have have ownership pointers embedded in a another shared object. So having a protected control-block is not sufficient outside the trivial toy-program.
Sufficient for what? Smart pointers are about ownership not protecting from races. I also don’t understand what are you trying to prove. My point was: C++ has to do atomic counting, because it has no concept of shared vs local. All of things you’ve said just prove it or are talking about something I’m not discussing here.
 The manual synchronization part comes in if you try to work 
 with payload T itself. THAT is manual.
No, none of the methods on shared_ptr guarantees that races won't happen.
Just as you quoted below - all const are, including copy & destructor.
 Since C++ doesn’t see a difference at type level of shared vs 
 local, there is no thread-local variation of shared_ptr. It 
 would be too unsafe even for those guys, contrary to what Ola 
 responses imply.
Sigh. I implied that shared_ptr on a single thread is mostly useless.
You did imply it’s useless on single thread and not used in concurrency because you need manual sync. I’d argue you don’t need a sync to access shared_ptr, you’d need it for object it points to. It still solves the ownership and deterministic destruction in the presense of concurrent shared_ptrs of the same object. But again - you countered a different point all together, see above.
 But this:

 struct OurSharedCache {
    shared_ptr<T> something;
    shared_ptr<T> somethingelse
 }
Not that I talked about it. This has nothing to do with shared_ptr and/or things I stated originally.
 Well I can clearly see misinformation and describe it as such. 
 See your point about atomic_shared_ptr.
What about it? «If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur»
Copy & destruction is actually fine, which you seem to ignore. Also accessing const methods of payload is fine. New C++ implies const == thread safe btw, at least in all of STL.
Nov 27 2017
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 27 November 2017 at 20:13:35 UTC, Dmitry Olshansky 
wrote:
 I’ve seen a tech giant that works on uber high-performance 
 things making heavy use of STL, and being fond of C++14 
 “high-level” features.
Look, I am not against "high level" features, but shared_ptr is nothing like the thread local ref-counting you were talking about which was at least introduced by Walter and Andrei as a competing solution to tracking of borrowed pointers in Rust.
 That must be why you seriously have no idea how people use it. 
 Otherwise you’d know that nobody shares a reference to shared 
 pointer but rather a copy. The whole point of smart pointer is 
 to avoid naked references.
I have an idea of how people use it… why all the ad hominem? I just don't find it very useful. In practice I'm usually better off exchanging through a facade and having the exchanged resource wrapped up in a custom type. It is more maintainable and easy to deal with in terms of correctness.
 They are if you use them as intended - value types, that 
 pretend to be pointers.
Sure. Not that this is how they are defined to be used. It is a very limited use case.
 a shared control block is. (Technically you share a pointer, 
 not an object of type T, but that is a minor detail.)
No, you actually share an object and the last one to decrement the ref will destroy it. Now that is actually thread-safe thanks to atomic counter.
Ok, sure, you share an ownership relation to an object, but it isn't general enough, which was what I tried to suggest. For instance, if you have a resource on a GPU with an integer ID that is also a resource, but shared_ptr does not work (unless you accept extra indirections). So, I'd rather see an owned<T>, which also can handle non-pointed-to-objects. Then one might ask, why not just have "unique<owningptr<T>>" or "shared<owningptr<T>>" that also would work with "unique<Resource>" and "shared<Resource>"? Then you can build generic ADTs with a generic notion of ownership . The basic idea is that an owned resource that has to go through a pointer isn't really necessary or as generic a type as it could have been.
 That is my original point, which you now violently agree with :)
Ok, then I didn't get your original point.
 Just underlines that you don’t understand how it is supposed to 
 be used.
I know how it can be used. That doesn't mean that it is as generally useful as it should be to be useful for what I want to do.
 I also don’t understand what are you trying to prove. My point 
 was: C++ has to do atomic counting, because it has no concept 
 of shared vs local.
That I disagree with. In C++ correct syncing is entirely dependent on the programmer. Even if atomic_shared_ptr provide some guarantees these guarantees will not hold if you depend on more than one entity. So there is no mechanism in C++ that provide something to ensure correctness when it comes to concurrency. I.e. you totally depend on the programmer's ability. You can create a concept of D-style local/shared by just creating a wrapper type and use a lint-style verification. This is the route C++ is heading in general for various properties related to pointer types it seems. (e.g. wrap with borrowed, owned, nullable etc). Which is also basically what D has, except shared is a builtin, but semantically I see no difference. To get a semantic difference that matter you need concurrency to be part of the language itself. If you cannot express propositions about concurrency in the language, then there is a very limited ability to do something interesting with it.
 You did imply it’s useless on single thread and not used in 
 concurrency because you need manual sync. I’d argue you don’t 
 need a sync to access shared_ptr, you’d need it for object it 
 points to.
Listen, if you build a shared graph with shared_ptrs then you are in trouble. So you would need atomic_shared_ptrs. That's a perfectly reasonable use case. You are presuming that the shared_ptr objects are thread local and that they are not embedded in other objects that are reachable by another thread. Those a very limiting presumptions.
 It still solves the ownership and deterministic destruction in 
 the presense of concurrent shared_ptrs of the same object.
But it doesn't solve correctness. You still rely on the programmer to get correctness. Which is a tall order when you use encapsulation. That only works if you never embed shared_ptr in a class.
 Copy & destruction is actually fine, which you seem to ignore. 
 Also accessing const methods of payload is fine. New C++ 
 implies const == thread safe btw, at least in all of STL.
As far as I can see from the description at cppreference.com there is no guarantee of freedom for races for anything if both threads reference the same shared_ptr object (not the control block but the object pointing to the control block). You can obviously transmit it to another thread using a mutex-like/messaging-like setup if that is what you mean.
Nov 27 2017
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, November 27, 2017 15:56:09 Ola Fosheim Grostad via Digitalmars-d 
wrote:
 On Monday, 27 November 2017 at 14:35:03 UTC, Dmitry Olshansky

 wrote:
 Then watch Herb’s Sutter recent talk “Leak freedom by default”.
 Now THAT guy must be out of his mind :)
He could be, I havent seen it... Shared_ptr isnt frequently used, it is a last resort,
I don't understand this. I would expect most modern C++ programs to be using shared_ptr as the default for most pointers and thus use it heavily. Any time that I've been able to use at least TR1 for C++, I've used it everywhere, and when I've been stuck with C++98, I've used a home-grown equivalent. shared_ptr is a _huge_ boon for avoiding memory problems. - Jonathan M Davis
Nov 27 2017
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 27 November 2017 at 18:00:59 UTC, Jonathan M Davis 
wrote:
 I don't understand this. I would expect most modern C++ 
 programs to be using shared_ptr as the default for most 
 pointers and thus use it heavily.
You get this: shared_ptr -> control_block -> object Instead of this: unique_ptr -> object my_ref_ptr ->object
 shared_ptr is a _huge_ boon for avoiding memory problems.
Yes, if you don't mind the inefficiency, i.e. if you are doing high-level programming in C++.
Nov 27 2017
parent reply Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
On Monday, 27 November 2017 at 18:32:39 UTC, Ola Fosheim Grøstad 
wrote:
 You get this:

 shared_ptr -> control_block -> object
Actually, seems like the common implementation uses 16 bytes, so that it has a direct pointer as well. So twice the size of unique_ptr.
Nov 27 2017
parent Temtaime <temtaime gmail.com> writes:
And 2k18 passes and there will be no precise gc this year it 
seems.
Great D language. Better to think to move from it away.
Feb 03 2018
prev sibling parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Monday, 27 November 2017 at 06:36:27 UTC, Ola Fosheim Grostad 
wrote:
 On Monday, 27 November 2017 at 05:47:49 UTC, Dmitry Olshansky 
 wrote:
 likely via RAII. Not to mention cheap (thread-local) Ref 
 Counting, C++ and many other language have to use atomics 
 which makes RC costly.
No, you dont. Nobody in their right mind would do so in C++ as a general solution. Seems there is trend in doing D-advocacy based on the assumption that programmers using other languages are crazy these days. In C++ sync is manual, which is the only efficient way to do it. Proving correctness for an efficient general solution is an unsolved theoretical problem. You can do it for high level mechanisms, but not low level atm. Rust and Pony claims to have solutions, but they are not general. D most certainly does not have it and never will. When threading is a libray type then you cannot achieve more in D than you can achieve in C++, i.e. Shared is not going to do more than a C++ library type with a separate static analysis tool.
What I think Dmitry meant is that shared_ptr<T> uses atomic instructions for the reference counting (though you need to use atomic_shared_ptr, or atomic_* function if you want to modify the shared_ptr itself) and you can't opt out of that even if you're not sharing the shared_ptr with other threads. On the other hand in D, a properly designed SharedPtr(T) will use atomic instructions for reference counting, only if it's payload type T is has the 'shared' type qualifier. And if you have 'shared(SharedPtr(T))', only then you'll have atomic instructions for the SharedPtr struct itself, but unlike shared_ptr<T>, you won't have access to the non-thread safe methods.
Nov 26 2017
parent reply Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
On Monday, 27 November 2017 at 06:59:30 UTC, Petar Kirov 
[ZombineDev] wrote:
 the shared_ptr itself) and you can't opt out of that even if 
 you're not sharing the shared_ptr with other threads.
Well, the compiler can in theory ellide atomics if it csn prove that the memory cannot be accessed by another thread. But it kinda is missing the point that if it only is in a single thread then it would typically only have only one assignment. Shared_ptr is for holding a resource not for using it...
Nov 26 2017
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 27 November 2017 at 07:09:25 UTC, Ola Fosheim Grostad 
wrote:
 But it kinda is missing the point that if it only is in a 
 single thread then it would typically only have only one 
 assignment. Shared_ptr is for holding a resource not for using 
 it...
Just to expand a bit on this: What is lost here is that what has been proposed for D is to have a RC solution to solve what Rust does with borrowed pointers. Not unlike Swift. In C++ life time management of borrowed pointers is fully manual, so it relies on algorithmic design rather than a programming mechanism. Although for C++ there is upcoming wrapper-types to allow for separate static analysis tooling that is comparable to Rust. The intended usage is not comparable. (In C++ you typically will have per-type or per-application RC, wrapped up where you need it by encapsulation and move-semantics.)
Nov 27 2017
parent reply Temtaime <temtaime gmail.com> writes:
Please stop this flame and make first real step into bringing 
precise GC to us.
Current GC in D is shit and all this speaking won't improve 
situation.
The PR is not merged although it passed all the tests.
Nov 27 2017
next sibling parent Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
On Monday, 27 November 2017 at 09:38:52 UTC, Temtaime wrote:
 Please stop this flame
There is no flaming.
 Current GC in D is shit and all this speaking won't improve 
 situation.
If so, why are you here? But you are fundamentally wrong. Precise GC will not bring a general improvement, for that you need advanced pointer analysis. So you need a change of philosophy to get a performant GC: semantic changes.
Nov 27 2017
prev sibling parent reply codephantom <me noyb.com> writes:
On Monday, 27 November 2017 at 09:38:52 UTC, Temtaime wrote:
 Current GC in D is shit
Can you elaborate? "D is totally useless"..."Dlang is a toy in outer space"... "GC in D is shit" .. I'm very open minded to these different argument styles, and occassionaly make use of them myself. But in a discussion about GC, some technical details might prove to be very useful to those of us following this discussion. I encourage you to further refine your argument... ;-) https://en.wikipedia.org/wiki/Argument
Nov 27 2017
parent Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
On Monday, 27 November 2017 at 10:13:41 UTC, codephantom wrote:
 But in a discussion about GC, some technical details might 
 prove to be very useful to those of us following this 
 discussion.
Precise scanning of pointers makes sense when you have many cachelines on the GC with no pointers in them. But if you mostly have pointers (a large graph or a tree) then it makes little difference. You need to add a more extensive whole program type analysis where you prove that the GC memory heap isnt reachable from a type... I.e. "pointers reachable through class T can provably never point into the a GC heap in this specific program, so therefore we can ignore all pointers to T".
Nov 27 2017
prev sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Friday, 24 November 2017 at 05:34:14 UTC, Adam Wilson wrote:
 RAII+stack allocations make sense when I care about WHEN an 
 object is released and wish to provide some semblance of 
 control over deallocation (although as Andrei has pointed out 
 numerous time, you have no idea how many objects are hiding 
 under that RAII pointer). But there are plenty of use cases 
 where I really don't care when memory is released, or even how 
 long it takes to release.
A GC makes most sense when the compiler fail at disproving that an object can be part of cycle of references that can be detached. So it makes most sense for typically long lived objects. Imagine if you spend all the effort you would need to put into getting a generational GC to work well into implementing pointer analysis to improve automatic deallocation instead… In order to speed up generated code that allows a generational GC to run you still would need a massive amount of work put into pointer analysis + all the work of making a state of the art GC runtime. Most people consider designing and implementing pointer analysis to be difficult. The regular data flow analysis that the D compiler has now is trivial in comparison. Might need a new IR for it, not sure.
 Obviously my pattern isn't "wrong" or else DMD itself is 
 "wrong". It's just not your definition of "correct".
Well, you could redefine the semantics of D so that you disallow unsafe code and possibly some other things. Then maybe have generational GC would be easy to implement if you don't expect better performance than any other high level language.
 Another use case where RAII makes no sense is GUI apps. The 
 object graph required to maintain the state of all those 
 widgets can be an absolute performance nightmare on 
 destruction. Closing a window can result in the destruction of 
 tens-of-thousands of objects (I've worked on codebases like 
 that), all from the GUI thread, causing a hang, and a bad user 
 experience. Or you could use a concurrent GC and pass off the 
 collection to a different thread. (See .NET)
Sounds like someone didn't do design before the started coding and just kept adding stuff. Keep in mind that OS-X and iOS use reference counting for all objects and it seems to work for them. But they also have put a significant effort into pointer-analysis to reduce ref-count overhead, so still quite a lot more work for the compiler designer than plain RAII.
 Your arguments are based on a presupposition that D should only 
 be used a certain way;
No, it is based on what the D language semantics are and the stated philosophy and the required changes that it would involve. I have no problem with D switching to generational GC. Like you I think most programs can be made to work fine with the overhead, but then you would need to change the philosophy that Walter is following. You would also need to either invest a lot into pointer analysis to keep a clean separation between GC-references and non-GC references, or create a more unforgiving type system that ensure such separation. I think that having a generational GC (or other high level low-latency solutions) probably would be a good idea, but I don't see how anyone could convince Walter to change his mind on such issues. Especially as there are quite a few semantic flaws in D that would be easy to fix, that Walter will not fix because he like D as it is or thinks it would be too much of a breaking change. You would need to change the D philosophy from "performant with some convenience" to "convenience with some means to write performant code". I agree with you that the latter philosophy probably would attract more users. It is hard to compete with C++ and Rust on the former. But I am not sure if Walter's goal is to attract as many users as possible.
Nov 23 2017
parent codephantom <me noyb.com> writes:
On Friday, 24 November 2017 at 07:48:03 UTC, Ola Fosheim Grøstad 
wrote:
 But I am not sure if Walter's goal is to attract as many users 
 as possible.
Given all the bullshit bugs I have to deal with, I'm starting to think it's the opposite.
Nov 24 2017
prev sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Thursday, 23 November 2017 at 20:13:31 UTC, Adam Wilson wrote:
 a precise GC will enable data with isolated or immutable 
 indirections to
 be safely moved between threads
I think you could in any case. What precise allows is to copy object during collection if there is no conservative pointers to the object, of course.
 I would focus on a generational GC first for two reasons. The 
 first is that you can delay the scans of the later gens if the 
 Gen0 (nursery) has enough space, so for a lot of programs it 
 would result in a significant cut-down in collection times.
This is like saying that having a large heap will allow you to cut-down collection time. The reason gen0 is fast is that it's mostly dead and you limit the amount of memory Gen0 scans to that of live set in Gen0 heap + remebered set in old gen. It has nothing to do with delaying the scan, in fact Gen0 scans are way more frequent then scans of a simple single-gen GC.
 The second is that you still typically have to stop the 
 execution of the thread on the Gen0 collection (the objects 
 most likely to be hot).
If your Gen0 collector is Stop the World, like all of Java collectors except for the most recent ones - ZGC and Shenandoah. Ironically both are not generational :)
 So with a non-generational concurrent collector you have to 
 stop the thread for the entirety of the scan,
False again. Any "mostly" concurrent GC can scan with a super small pause that is typically used only to mark stacks of Threads. See also the 2 collectors I mentioned. Go's GC is also non-generational (and SUUUPER popular) to put the last nail in the coffin of "I need generational GC because you can't do X without it?".
 because you have no way to know which objects are hot and which 
 are cold.
Why would that stop collector? There is no need to know what is hot, and garbage is always cold.
Nov 24 2017