how to assign to shared obj.systime?

Arafel er.krali at gmail.com
Mon Jul 13 13:24:25 UTC 2020


On 13/7/20 14:18, Steven Schveighoffer wrote:
> 
> cast() will remove as little as possible, but for most cases, including 
> classes and struts, this means the entire tree referenced is now unshared.
> 

Yeah, but the whole lvalue cast looks just non-obvious and ugly to me:

```
cast() foo = bar;
```

It looks like an ad-hoc hack, and I haven't seen it used anywhere else. 
I don't even think it's well-documented (it's probably somewhere in the 
grammar, without much explanation of what it does or what it would be 
useful for). I know I had to asks in the forums because I couldn't even 
assign to a shared SysTime!

> An AA does something really useless, which I didn't realize.
> 
> If you have a shared int[int], and use cast() on it, it becomes 
> shared(int)[int]. Which I don't really understand the point of.
> 
> But in any case, casting away shared is doable, even if you need to type 
> a bit more.
> 

Sure, it's doable, but the readability suffers a lot, and also it's just 
too error-prone.

> The intent is to cast away shared on the ENTIRE aggregate, and then use 
> everything in the aggregate as unshared.
> 
> I can imagine something like this:
> 
> ref T unshared(T)(return ref shared(T) item) { return *(cast(T*)&item); }
> 
> with(unshared(this)) {
>      // implementation using unshared things
> }
> 
> I wasn't suggesting that for each time you access anything in a shared 
> object, you need to do casting. In essence, it's what you are looking 
> for, but just opt-in instead of automatic.
> 

Yes, that would be nice as a workaround, although ideally I'd like a 
more comprehensive and general solution.

Sometimes you don't need to strip shared only from `this`, sometimes 
only it's only from some parts, and sometimes also from some external 
objects.

To be clear, I'm so far assuming it's explicitly opt-in by the user.

I wouldn't mind seen something done with `synchronized` classes, but 
that's probably a much more complex issue.

> 
> Yeah, this looks suspiciously like the with statement above. We seem to 
> be on the same page, even if having different visions of who should 
> implement it.
> 

I think we're in "violent agreement" territory here :-)

I honestly would be happy if there were a reliable library solution that 
worked even now, because so far for any non-trivial situation I have to 
spend more time casting from and to shared than doing the actual work, 
and the code becomes a mess to follow afterwards.

> 
> You are better off separating the implementation of the shared and 
> unshared parts. That is, you have synchronized methods, but once you are 
> synchronized, you cast away shared and all the implementation is normal 
> looking.
> 
> Compare:
> 
> class TimeCount {
>      public:
>      void startClock() {
>          startTime = Clock.currTime;
>      }
>      synchronized void startClock() shared {
>         (cast()this).startClock();
>      }
>      void endClock() {
>          endTime = Clock.currTime;
>      }
>      synchronized void endClock() shared {
>         (cast()this).endClock();
>      }
>      void calculateDuration() {
>          timeEllapsed = endTime - startTime;
>      }
>      synchronized void calculateDuration() shared {
>          (cast()this).calculateDuration();
>      }
> 
>      private:
>      SysTime startTime;
>      SysTime endTime;
>      Duration timeEllapsed;
> }
> 
> I would imagine a mixin could accomplish a lot of this, but you have to 
> be careful that the locking properly protects all the data.
> 
> A nice benefit of this approach is that no locking is needed when the 
> instance is thread-local.
> 

Just thinking of the amount of boilerplate makes my head spin. Even if a 
mixin could somehow automate it, I still think there should be a 
"proper" way to do it, without that much hacking around.

Furthermore, In my case I'm trying to do fine-grained locking, and I 
might have to get different locks within the same function. Of course I 
could split the function, but it would be constantly interrupting the 
"natural flow" of what I'm trying to do, and it would become so much 
harder to understand and to reason about.

And these functions wouldn't make sense by themselves, would probably 
need access to locals from the parent function, and would only be called 
from one place... so I see them as a kind of anti-pattern.

Also, `shared` and `synchronized` would become in this case pretty much 
useless then when applied to a class / structure.

> 
> I think we may have been battling a strawman here. I assumed you were 
> asking for synchronized to be this mechanism, when it seems you actually 
> were asking for *any* tool. I just don't want the locking to be 
> conflated with "OK now I can safely access any data because something 
> was locked!". It needs to be opt-in, because you understand the risks.
> 
> I think those tools are necessary for shared to have a good story, 
> whether the compiler implements it, or a library does.
> 
> -Steve

I totally agree with this. As I mentioned, I wouldn't mind 
`synchronized` classes becoming apt for the trivial cases (i.e. you just 
have a shared counter, or something equally simple). Of course they 
would have to be much more restricted in what they can do than they are 
now, so it's probably not going to happen (code breakage and everything).

In fact, I'm working on a POD-Proxy that would automatically guard 
access to members with a per-instance lock, and I think it's the kind of 
situation `synchronized` classes could be useful for.

But that's orthogonal to the issue here of being able to have something 
like @system or @trusted for `shared`.

A.


More information about the Digitalmars-d-learn mailing list