On Fri, Oct 19, 2018 at 9:45 AM Steven Schveighoffer via Digitalmars-d <digitalmars-d@puremagic.com> wrote: > > On 10/18/18 9:09 PM, Manu wrote: > > On Thu, Oct 18, 2018 at 5:30 PM Timon Gehr via Digitalmars-d > > <digitalmars-d@puremagic.com> wrote: > >> > >> On 18.10.18 23:34, Erik van Velzen wrote: > >>> If you have an object which can be used in both a thread-safe and a > >>> thread-unsafe way that's a bug or code smell. > >> > >> Then why do you not just make all members shared? Because with Manu's > >> proposal, as soon as you have a shared method, all members effectively > >> become shared. > > > > No they don't, only facets that overlap with the shared method. > > I tried to present an example before: > > > > struct Threadsafe > > { > > int x; > > Atomic!int y; > > void foo() shared { ++y; } // <- shared interaction only affects 'y' > > void bar() { ++x; ++y; } // <- not threadsafe, but does not violate > > foo's commitment; only interaction with 'y' has any commitment > > associated with it > > void unrelated() { ++x; } // <- no responsibilities are transposed > > here, you can continue to do whatever you like throughout the class > > where 'y' is not concerned > > } > > > > In practise, and in my direct experience, classes tend to have exactly > > one 'y', and either zero (pure utility), or many such 'x' members. > > Threadsafe API interacts with 'y', and the rest is just normal > > thread-local methods which interact with all members thread-locally, > > and may also interact with 'y' while not violating any threadsafety > > commitments. > > I promised I wouldn't respond, I'm going to break that (obviously). > > But that's because after reading this description I ACTUALLY understand > what you are looking for. > > I'm going to write a fuller post later, but I can't right now. But the > critical thing here is, you want a system where you can divvy up a type > into pieces you share and pieces you don't. But then you *don't* want to > have to share only the shared pieces. You want to share the whole thing > and be sure that it can't access your unshared pieces. > > This critical requirement makes things a bit more interesting. For the > record, the most difficult thing to reaching this understanding was that > whenever I proposed anything, your answer was something like 'I just > can't work with that', and when I asked why, you said 'because it's > useless', etc. Fully explaining this point is very key to understanding > your thinking. > > To be continued...
I'm glad that there's movement here... but I'm still not 100% convinced you understood me; perhaps getting close though. I only say that because your description above has a whole lot more words and complexity than is required to express my proposal. If you perceive that complexity in structural terms, then I am still not clearly understood. > "divvy up a type into pieces" This is an odd mental model of what I'm saying, and I can't sympathise with those words, but if they work for you, and we agree on the semantics, then sure... If you write an object with some const methods and some non-const methods, then take a const instance of the object... you can only call the const methods. Have you 'divvied up the type' into a const portion and a non-const portion? If the answer is yes, then I can accept your description. I would talk in terms of restriction: An object has 4 functions, 2 are mutable, 2 are const... you apply const to the type and you are *restricted* to only calling the 2 const functions. An object has 4 functions, 2 are unsahred, 2 are shared... you apply shared to the type and you are *restricted* to only calling the 2 shared (threadsafe) functions. I haven't 'broken the type up', I'm just restricting what you can do to it from within a particular context. In the const context, you can't mutate it. In the shared context, you can't do un-threadsafe to it, and the guarantee of that is embedded in the rules: 1. shared data can not be read or written 2. shared methods must be threadsafe a. this may require that they be @trusted at the low-level, like the methods of `Atomic(T)` b. no other method may violate the shared method's promise, otherwise it does not actually deliver its promise i. I have extensive experience with this and it's just not a problem in practise, but compiler technology to assist would be welcome! ii. This does NOT mean the not-shared methods are somehow shared; they just need to be careful when interacting with some (usually small) subset of members This design implies strong encapsulation, but that's a naturally occurring tendency implementing anything that's threadsafe. As a helpful best-practise to assure that non-shared methods don't undermine a shared method's commitment; prefer to interact with volatile members via accessors or properties that are themselves shared (can be private if you like). You will find that it's not actually hard to deliver on the object's commitment. If you write tooling that is at the level one-up from the bottom of the stack or higher, it should be unusual that you ever need to write a @trusted method, in which case delivering on *your* shared methods promise is implicit.