On 10/16/18 4:26 PM, Manu wrote:
On Tue, Oct 16, 2018 at 11:30 AM Steven Schveighoffer via
Digitalmars-d <digitalmars-d@puremagic.com> wrote:

On 10/16/18 2:10 PM, Manu wrote:
On Tue, Oct 16, 2018 at 6:35 AM Steven Schveighoffer via Digitalmars-d
<digitalmars-d@puremagic.com> wrote:

On 10/16/18 9:25 AM, Steven Schveighoffer wrote:
On 10/15/18 2:46 PM, Manu wrote:

  From there, it opens up another critical opportunity; T* -> shared(T)*
promotion.
Const would be useless without T* -> const(T)* promotion. Shared
suffers a similar problem.
If you write a lock-free queue for instance, and all the methods are
`shared` (ie, threadsafe), then under the current rules, you can't
interact with the object when it's not shared, and that's fairly
useless.


Oh, I didn't see this part. Completely agree with Timon on this, no
implicit conversions should be allowed.

Why?

int x;

shared int *p = &x; // allow implicit conversion, currently error

passToOtherThread(p);

useHeavily(&x);

What does this mean? It can't do anything... that's the whole point here.
I think I'm struggling here with people bringing presumptions to the
thread. You need to assume the rules I define in the OP for the
experiment to work.

OK, I wrote a whole big response to this, and I went and re-quoted the above, and now I think I understand what the point of your statement is.

I'll first say that if you don't want to allow implicit casting of shared to mutable, then you can't allow implicit casting from mutable to shared. Because it's mutable, races can happen.

There is in fact, no difference between:

int *p;
shared int *p2 = p;
int *p3 = cast(int*)p2;

and this:

int *p;
shared int *p2 = p;
int *p3 = p;

So really, the effort to prevent the reverse cast is defeated by allowing the implicit cast.

There is a reason we disallow assigning from mutable to immutable without a cast. Yet, it is done in many cases, because you are sometimes building an immutable object with mutable pieces, and want to cast the final result.

In this case, it's ON YOU to make sure it's correct, and the traditional mechanism for the compiler giving you the responsibility is to require a cast.

-----

OK, so here is where I think I misunderstood your point. When you said a lock-free queue would be unusable if it wasn't shared, I thought you meant it would be unusable if we didn't allow the implicit cast. But I realize now, you meant you should be able to use a lock-free queue without it being actually shared anywhere.

What I say to this is that it doesn't need to be usable. I don't care to use a lock-free queue in a thread-local capacity. I'll just use a normal queue, which is easy to implement, and doesn't have to worry about race conditions or using atomics. A lock free queue is a special thing, very difficult to get right, and only really necessary if you are going to share it. And used for performance reasons!

Why would I want to incur performance penalties when using a lock-free queue in an unshared mode? I would actually expect 2 separate implementations of the primitives, one for shared one for unshared.

What about primitives that would be implemented the same? In that case, the shared method becomes:

auto method() { return (cast(Queue*)&this).method; }

Is this "unusable"? Without a way to say, you can call this on shared or unshared instances, then we need to do it this way.

But I would trust the queue to handle this properly depending on whether it was typed shared or not.

-Steve

Reply via email to