jdk9 VarHandle and Fence methods

classic Classic list List threaded Threaded
73 messages Options
1234
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

oleksandr otenko
At least you will have HB edges that preclude certain ordering.

I may still use volatiles to share my data, but use a failing tryLock to prove it shall be visible to anyone who releases the lock - because that's a reasonable thing to assume that the volatile reads following the release will be ordered strictly after the failing tryLock in the total synchronization order. Even though there is no synchronizes-with edge between the lock release and the failing tryLock, tryLock cannot be placed after the lock release, and, therefore, all subsequent volatile reads are also after the volatile writes preceding the failing tryLock, thus they are bound to synchronize-with. This, you see, still requires the specification how a failing tryLock is ordered with respect to acquires and releases. The fact that it is not specified is exactly the reason to bring this up.

Alex

On 04/09/2015 04:32, David Holmes wrote:

I don’t agree that is a general use-case. You can always postulate a need for a happens-before edge between two things. I don’t think it is reasonable to expect one on a failing tryLock. Any shared state will need to be protected by a lock or else volatile – and those should be what establishes the HB edges in my opinion.

 

David

 

From: [hidden email] [[hidden email]] On Behalf Of Vitaly Davidovich
Sent: Friday, September 4, 2015 1:10 PM
To: [hidden email]
Cc: thurston; [hidden email]
Subject: Re: [concurrency-interest] jdk9 VarHandle and Fence methods

 

If thread A releases a lock and threads B and C tryLock it, with one succeeding, the failing thread may want to do something else but wants a happens-before edge with the lock release - that's the general use case.  As a simple example, consider two threads tryLock'ing to acquire the exclusive right to close a socket and then perform some additional actions that require ordering of actions done by the releasing thread.  The thread failing to acquire the lock will skip closing the socket but will proceed to do some work that requires happens-before edge.  This is typically done using CAS, with one thread successfully flipping the state, and the others just skip that action that's guarded by the CAS, but can proceed with doing subsequent work.  In other words, one may want to piggyback on the unlock/tryLock to provide the ordering rather than introducing additional dedicated state for this.

sent from my phone

On Sep 3, 2015 7:26 PM, "David Holmes" <[hidden email]> wrote:

j.u.c also adopts the same principle:

 

Actions prior to "releasing" synchronizer methods such as Lock.unlock, Semaphore.release, and CountDownLatch.countDown happen-before actions subsequent to a successful "acquiring" method such as Lock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.await on the same synchronizer object in another thread.

 

Note the use of “successful” which already indicates tryLock is not included here.

 

David

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Oleksandr Otenko
Sent: Friday, September 4, 2015 8:48 AM
To: [hidden email]; 'Vitaly Davidovich'; 'thurston'
Cc: [hidden email]
Subject: Re: [concurrency-interest] jdk9 VarHandle and Fence methods

 

Sure, lock acquires and releases in JMM don't guarantee ordering until the lock is acquired. But also they don't have a tryLock in JMM - as it really concerns the synchronized, doesn't it.

Alex

On 03/09/2015 23:34, David Holmes wrote:

Not sure what “everything” is. If Thread A releases the lock and thread B acquires it, then B sees everything that happened in A before the release. If thread C now does a tryLock and sees the lock is already owned you are suggesting it should see what thread B sees because if it had acquired the lock then that would be the case. But it didn’t acquire it, it only sees that it is already acquired by another thread. So I don’t see there is any transitive relationship that has to be applied here. Implementation wise it is likely but in terms of the model I think expectations and requirements should be nil.

 

David

 

From: [hidden email] [[hidden email]] On Behalf Of Oleksandr Otenko
Sent: Friday, September 4, 2015 8:18 AM
To: [hidden email]; 'Vitaly Davidovich'; 'thurston'
Cc: [hidden email]
Subject: Re: [concurrency-interest] jdk9 VarHandle and Fence methods

 

You should at least see everything preceding the lock acquire - since you see the lock acquired - and therefore everything preceding the lock release.

Alex

On 03/09/2015 21:57, David Holmes wrote:

tryLock seems a non-issue to me. If you acquire a lock you are guaranteed to see all changes made by previous owners of the lock. If you fail to acquire the lock then … you should not be expecting anything.

 

David

 

From: [hidden email] [[hidden email]] On Behalf Of Oleksandr Otenko
Sent: Friday, September 4, 2015 4:19 AM
To: Vitaly Davidovich; thurston
Cc: [hidden email]
Subject: Re: [concurrency-interest] jdk9 VarHandle and Fence methods

 

Has anyone come up with the answer about ordering for tryLock, or have I missed it?

It seems that tryLock can fail without attempting a CAS, but what guarantees are really expected by the code users, and are they in line with this assumption.


Alex


On 22/08/2015 19:15, Vitaly Davidovich wrote:

I would hope there's no ordering difference between successful and unsuccessful CAS.  On x86/64 the instruction itself provides full fence irrespective of outcome; compiler doesn't know a priori whether it will succeed or not.  Are there platforms where the lowering of the CAS has different cpu ordering based on outcome? LL/SC can fail if cacheline is modified in between (even if value is still the expected one) but I'm not aware of that changing ordering semantics.  However, if there are cases where this would matter, I hope the JVM ensures the requested ordering irrespective of outcome.

Along this line, the more "interesting" and related question is what the ordering guarantees are for failed tryLock methods.

sent from my phone

On Aug 22, 2015 1:41 PM, "thurstonn" <[hidden email]> wrote:

Thanks for the prompt reply.  I guess I'll operate then from the yes
perspective.

What are the plans with respect to the "higher-order methods" on e.g.
AtomicReference, i.e.

T getAndAccumulate(T, BinaryOperator<T>)
T updateAndGet(UnaryOperator<T>)
. . .
etc.


Are you going to have:
T getAndAccumulateVolatilely(T, BinaryOperator<T>)
T getAndAccumulateAcquiredly(T, BinaryOperator<T>)
etc versions?


That seems like a pollution of the API, IMO (and just awful names).  And I'm
not really sure where it ends.

And then a small javadoc modification suggestion:
/**
      * Returns the value, and ensures that subsequent loads and stores
      * are not reordered before this access.
      *
      * @apiNote Ignoring the many semantic differences from C and
      * C++, this method has memory ordering effects compatible with
      * memory_order_acquire ordering.
      *
      * @return the value
      */
     T getAcquire(Object owner);

I find
/**
      * Returns the value, and ensures that subsequent loads and stores (*in
the program order*)
      * are not reordered before this access.
      *
      * @apiNote Ignoring the many semantic differences from C and
      * C++, this method has memory ordering effects compatible with
      * memory_order_acquire ordering.
      *
      * @return the value
      */
     T getAcquire(Object owner);

to be a little clearer as *subsequent* is an overloaded term when it comes
to JMM matters.

And one final question that I've always been confused about;  are there
different "memory ordering effects" between a successful CAS and an
unsuccessful one (presumably in the latter because no write actually
occurs)?
IIRC, when looking at the java 8 JVM code, I believe a fence was inserted in
the successful case, at least on x86/64.  If so, I can take a shot at
producing some javadoc language to reflect that, if it would be helpful.



--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/jdk9-VarHandle-and-Fence-methods-tp12666p12680.html
Sent from the JSR166 Concurrency mailing list archive at Nabble.com.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest





_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

 

 

 



_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

Doug Lea
In reply to this post by oleksandr otenko
On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:
> Has anyone come up with the answer about ordering for tryLock, or have I missed it?

You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

thurstonn
In reply to this post by Paul Sandoz
Thanks for the straightforward response.

On another matter, in the link you provided it looks like Unsafe#getAndSetObject (XCHG) is now going to be intrinsified?  Is that correct?
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

Paul Sandoz

On 8 Sep 2015, at 11:12, thurstonn <[hidden email]> wrote:

> Thanks for the straightforward response.
>
> On another matter, in the link you provided it looks like
> Unsafe#getAndSetObject (XCHG) is now going to be intrinsified?  Is that
> correct?

It is already intrinsified (you may notice in recent code such intrinsic methods are annotated with @HotSpotIntrinsicCandidate).

We have yet to make intrinsic support for compareAndExchange{Volatile, Acquire, Release}.

Paul.

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

signature.asc (858 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

thurstonn
The reason I asked is that in JK8, Unsafe#getAndSetObject isn't even declared native (looking at Unsafe.java) - I assume that means it isn't even eligible for intrinsics (I was surprised that it wasn't native).

I came across some code (akka?) that was using some workaround because (according to a comment) Unsafe#getAndSetObject wasn't intrinsified; obviously that wasn't referring to jdk 9
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

Vitaly Davidovich
The reason I asked is that in JK8, Unsafe#getAndSetObject isn't even declared
native (looking at Unsafe.java) - I assume that means it isn't even eligible
for intrinsics (I was surprised that it wasn't native).

Methods don't need to be native to be intrinsified.

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7023898 did the intrinsification, which is available in java 8; just tried this on 8u40, and getAndSetObject just emits XCHG (this is on x86_64).

On Mon, Sep 14, 2015 at 6:12 PM, thurstonn <[hidden email]> wrote:
The reason I asked is that in JK8, Unsafe#getAndSetObject isn't even declared
native (looking at Unsafe.java) - I assume that means it isn't even eligible
for intrinsics (I was surprised that it wasn't native).

I came across some code (akka?) that was using some workaround because
(according to a comment) Unsafe#getAndSetObject wasn't intrinsified;
obviously that wasn't referring to jdk 9



--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/jdk9-VarHandle-and-Fence-methods-tp12666p12754.html
Sent from the JSR166 Concurrency mailing list archive at Nabble.com.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

thurstonn
Thanks.

Uhh, nevermind.
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

Hans Boehm
In reply to this post by Doug Lea
FWIW, this general issues is discussed in section 3 of http://dl.acm.org/citation.cfm?id=1375581.1375591 .

Yet another argument against providing the stronger guarantees is that, on many architectures, it doesn't just slow down trylock(), it more importantly slows down lock().  In general, if your code cares about ordering for unsuccessful trylock(), then it's not robust against, say, a debugging thread unexpectedly acquiring the lock for a short period.  In my view, in such a case, you're no longer using it as a lock, and you should be using something else, e.g. an atomic object, with stronger guarantees.

On Fri, Sep 4, 2015 at 4:18 AM, Doug Lea <[hidden email]> wrote:
On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:
Has anyone come up with the answer about ordering for tryLock, or have I missed it?

You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

Vitaly Davidovich

How does it slow down lock()?

I don't necessarily disagree but I can certainly see people considering tryLock to have same ordering effect as (failed) CAS.  It's certainly true that a CAS is a lower level primitive than a lock, but I don't know if that resonates immediately when thinking about this.  It's also the case that on very popular platforms such as x86 a failing tryLock will have the same ordering as a successful one, and no difference is observed (and JIT doesn't do anything different).

I don't understand the debugger thread example - what's the issue there?

sent from my phone

On Sep 14, 2015 9:07 PM, "Hans Boehm" <[hidden email]> wrote:
FWIW, this general issues is discussed in section 3 of http://dl.acm.org/citation.cfm?id=1375581.1375591 .

Yet another argument against providing the stronger guarantees is that, on many architectures, it doesn't just slow down trylock(), it more importantly slows down lock().  In general, if your code cares about ordering for unsuccessful trylock(), then it's not robust against, say, a debugging thread unexpectedly acquiring the lock for a short period.  In my view, in such a case, you're no longer using it as a lock, and you should be using something else, e.g. an atomic object, with stronger guarantees.

On Fri, Sep 4, 2015 at 4:18 AM, Doug Lea <[hidden email]> wrote:
On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:
Has anyone come up with the answer about ordering for tryLock, or have I missed it?

You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

Hans Boehm
How does it slow down lock()?

It depends on the precise guarantee you provide, and I suspect this thread didn't quite agree on that.  The most natural one is that the succeeding lock acquisition happens before the failed trylock().  That implies that if we have

x = 1;
lock();

those can't be reordered by the hardware, since a failing trylock() would have to see the assignment to x.  That requires a fence between them on ARM or Power.

I think the right way to think of trylock(), at least informally, is as allowing spurious failures. I.e. trylock() is allowed to behave as though the lock was held when it isn't.  You thus can't conclude anything about other threads from the fact that it failed.  In this view you don't have to think about memory ordering issues when reasoning about correctness, you just reason about spurious failures instead.

If your code is robust against unknown, e.g. debugger, threads acquiring the lock now and then, then it must be robust against this sort of spurious failure.  If the lock is really used only to provide mutual exclusion, this should not affect correctness.

On Mon, Sep 14, 2015 at 6:41 PM, Vitaly Davidovich <[hidden email]> wrote:

How does it slow down lock()?

I don't necessarily disagree but I can certainly see people considering tryLock to have same ordering effect as (failed) CAS.  It's certainly true that a CAS is a lower level primitive than a lock, but I don't know if that resonates immediately when thinking about this.  It's also the case that on very popular platforms such as x86 a failing tryLock will have the same ordering as a successful one, and no difference is observed (and JIT doesn't do anything different).

I don't understand the debugger thread example - what's the issue there?

sent from my phone

On Sep 14, 2015 9:07 PM, "Hans Boehm" <[hidden email]> wrote:
FWIW, this general issues is discussed in section 3 of http://dl.acm.org/citation.cfm?id=1375581.1375591 .

Yet another argument against providing the stronger guarantees is that, on many architectures, it doesn't just slow down trylock(), it more importantly slows down lock().  In general, if your code cares about ordering for unsuccessful trylock(), then it's not robust against, say, a debugging thread unexpectedly acquiring the lock for a short period.  In my view, in such a case, you're no longer using it as a lock, and you should be using something else, e.g. an atomic object, with stronger guarantees.

On Fri, Sep 4, 2015 at 4:18 AM, Doug Lea <[hidden email]> wrote:
On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:
Has anyone come up with the answer about ordering for tryLock, or have I missed it?

You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

Vitaly Davidovich

Hmm, the ordering I had in mind was unlock() happens-before a failing tryLock.  So a thread failing on tryLock sees operations preceded by last unlock() as ordered.  This is no different than successful tryLock or lock() in that regard.

sent from my phone

On Sep 15, 2015 1:16 AM, "Hans Boehm" <[hidden email]> wrote:
How does it slow down lock()?

It depends on the precise guarantee you provide, and I suspect this thread didn't quite agree on that.  The most natural one is that the succeeding lock acquisition happens before the failed trylock().  That implies that if we have

x = 1;
lock();

those can't be reordered by the hardware, since a failing trylock() would have to see the assignment to x.  That requires a fence between them on ARM or Power.

I think the right way to think of trylock(), at least informally, is as allowing spurious failures. I.e. trylock() is allowed to behave as though the lock was held when it isn't.  You thus can't conclude anything about other threads from the fact that it failed.  In this view you don't have to think about memory ordering issues when reasoning about correctness, you just reason about spurious failures instead.

If your code is robust against unknown, e.g. debugger, threads acquiring the lock now and then, then it must be robust against this sort of spurious failure.  If the lock is really used only to provide mutual exclusion, this should not affect correctness.

On Mon, Sep 14, 2015 at 6:41 PM, Vitaly Davidovich <[hidden email]> wrote:

How does it slow down lock()?

I don't necessarily disagree but I can certainly see people considering tryLock to have same ordering effect as (failed) CAS.  It's certainly true that a CAS is a lower level primitive than a lock, but I don't know if that resonates immediately when thinking about this.  It's also the case that on very popular platforms such as x86 a failing tryLock will have the same ordering as a successful one, and no difference is observed (and JIT doesn't do anything different).

I don't understand the debugger thread example - what's the issue there?

sent from my phone

On Sep 14, 2015 9:07 PM, "Hans Boehm" <[hidden email]> wrote:
FWIW, this general issues is discussed in section 3 of http://dl.acm.org/citation.cfm?id=1375581.1375591 .

Yet another argument against providing the stronger guarantees is that, on many architectures, it doesn't just slow down trylock(), it more importantly slows down lock().  In general, if your code cares about ordering for unsuccessful trylock(), then it's not robust against, say, a debugging thread unexpectedly acquiring the lock for a short period.  In my view, in such a case, you're no longer using it as a lock, and you should be using something else, e.g. an atomic object, with stronger guarantees.

On Fri, Sep 4, 2015 at 4:18 AM, Doug Lea <[hidden email]> wrote:
On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:
Has anyone come up with the answer about ordering for tryLock, or have I missed it?

You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

Peter Levart


On 09/15/2015 02:56 PM, Vitaly Davidovich wrote:

Hmm, the ordering I had in mind was unlock() happens-before a failing tryLock.  So a thread failing on tryLock sees operations preceded by last unlock() as ordered.  This is no different than successful tryLock or lock() in that regard.


How can you differentiate between:
- unlock() in thread T1 followed by unsuccessful tryLock() in thread T2 and
- unsuccessfull tryLock() in T2 followed by unlock() in T1

You want T1 to already unlock before you observe tryLock() failing in T2 (pressumably because some T3 has already obtained the lock before T2 or because of spurious failure).
But T2 failing tryLock() might simply be because T1 hasn't unlocked yet. So regardless of memory effects, you can't reason of ordering in other threads by observing tryLock() failing.

Do you have an example that proves me wrong?

Regards, Peter

sent from my phone

On Sep 15, 2015 1:16 AM, "Hans Boehm" <[hidden email]> wrote:
How does it slow down lock()?

It depends on the precise guarantee you provide, and I suspect this thread didn't quite agree on that.  The most natural one is that the succeeding lock acquisition happens before the failed trylock().  That implies that if we have

x = 1;
lock();

those can't be reordered by the hardware, since a failing trylock() would have to see the assignment to x.  That requires a fence between them on ARM or Power.

I think the right way to think of trylock(), at least informally, is as allowing spurious failures. I.e. trylock() is allowed to behave as though the lock was held when it isn't.  You thus can't conclude anything about other threads from the fact that it failed.  In this view you don't have to think about memory ordering issues when reasoning about correctness, you just reason about spurious failures instead.

If your code is robust against unknown, e.g. debugger, threads acquiring the lock now and then, then it must be robust against this sort of spurious failure.  If the lock is really used only to provide mutual exclusion, this should not affect correctness.

On Mon, Sep 14, 2015 at 6:41 PM, Vitaly Davidovich <[hidden email]> wrote:

How does it slow down lock()?

I don't necessarily disagree but I can certainly see people considering tryLock to have same ordering effect as (failed) CAS.  It's certainly true that a CAS is a lower level primitive than a lock, but I don't know if that resonates immediately when thinking about this.  It's also the case that on very popular platforms such as x86 a failing tryLock will have the same ordering as a successful one, and no difference is observed (and JIT doesn't do anything different).

I don't understand the debugger thread example - what's the issue there?

sent from my phone

On Sep 14, 2015 9:07 PM, "Hans Boehm" <[hidden email]> wrote:
FWIW, this general issues is discussed in section 3 of http://dl.acm.org/citation.cfm?id=1375581.1375591 .

Yet another argument against providing the stronger guarantees is that, on many architectures, it doesn't just slow down trylock(), it more importantly slows down lock().  In general, if your code cares about ordering for unsuccessful trylock(), then it's not robust against, say, a debugging thread unexpectedly acquiring the lock for a short period.  In my view, in such a case, you're no longer using it as a lock, and you should be using something else, e.g. an atomic object, with stronger guarantees.

On Fri, Sep 4, 2015 at 4:18 AM, Doug Lea <[hidden email]> wrote:
On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:
Has anyone come up with the answer about ordering for tryLock, or have I missed it?

You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

Vitaly Davidovich

You'd differentiate via whatever protocol is chosen by implementation, i.e. some state that would be set to signal phase changes.  The closest analog is code using CAS, successful or not, to piggyback ordering for surrounding memory ops.  I gave an example upthread where threads compete to get exclusive right to close a socket using tryLock; one thread wins and closes the socket, and losers skip closing the socket but proceed to use memory set inside unlock().  It's a semi contrived example since normally you'd use a CAS for something like this, but it illustrates the gist of what I'm getting at.  Of course you can solve these situations using dedicated atomic (or otherwise threadsafe) state, but perhaps one wants to piggyback on existing critical section's fences and not add additional ones.

sent from my phone

On Sep 15, 2015 4:52 PM, "Peter Levart" <[hidden email]> wrote:


On 09/15/2015 02:56 PM, Vitaly Davidovich wrote:

Hmm, the ordering I had in mind was unlock() happens-before a failing tryLock.  So a thread failing on tryLock sees operations preceded by last unlock() as ordered.  This is no different than successful tryLock or lock() in that regard.


How can you differentiate between:
- unlock() in thread T1 followed by unsuccessful tryLock() in thread T2 and
- unsuccessfull tryLock() in T2 followed by unlock() in T1

You want T1 to already unlock before you observe tryLock() failing in T2 (pressumably because some T3 has already obtained the lock before T2 or because of spurious failure).
But T2 failing tryLock() might simply be because T1 hasn't unlocked yet. So regardless of memory effects, you can't reason of ordering in other threads by observing tryLock() failing.

Do you have an example that proves me wrong?

Regards, Peter

sent from my phone

On Sep 15, 2015 1:16 AM, "Hans Boehm" <[hidden email]> wrote:
How does it slow down lock()?

It depends on the precise guarantee you provide, and I suspect this thread didn't quite agree on that.  The most natural one is that the succeeding lock acquisition happens before the failed trylock().  That implies that if we have

x = 1;
lock();

those can't be reordered by the hardware, since a failing trylock() would have to see the assignment to x.  That requires a fence between them on ARM or Power.

I think the right way to think of trylock(), at least informally, is as allowing spurious failures. I.e. trylock() is allowed to behave as though the lock was held when it isn't.  You thus can't conclude anything about other threads from the fact that it failed.  In this view you don't have to think about memory ordering issues when reasoning about correctness, you just reason about spurious failures instead.

If your code is robust against unknown, e.g. debugger, threads acquiring the lock now and then, then it must be robust against this sort of spurious failure.  If the lock is really used only to provide mutual exclusion, this should not affect correctness.

On Mon, Sep 14, 2015 at 6:41 PM, Vitaly Davidovich <[hidden email]> wrote:

How does it slow down lock()?

I don't necessarily disagree but I can certainly see people considering tryLock to have same ordering effect as (failed) CAS.  It's certainly true that a CAS is a lower level primitive than a lock, but I don't know if that resonates immediately when thinking about this.  It's also the case that on very popular platforms such as x86 a failing tryLock will have the same ordering as a successful one, and no difference is observed (and JIT doesn't do anything different).

I don't understand the debugger thread example - what's the issue there?

sent from my phone

On Sep 14, 2015 9:07 PM, "Hans Boehm" <[hidden email]> wrote:
FWIW, this general issues is discussed in section 3 of http://dl.acm.org/citation.cfm?id=1375581.1375591 .

Yet another argument against providing the stronger guarantees is that, on many architectures, it doesn't just slow down trylock(), it more importantly slows down lock().  In general, if your code cares about ordering for unsuccessful trylock(), then it's not robust against, say, a debugging thread unexpectedly acquiring the lock for a short period.  In my view, in such a case, you're no longer using it as a lock, and you should be using something else, e.g. an atomic object, with stronger guarantees.

On Fri, Sep 4, 2015 at 4:18 AM, Doug Lea <[hidden email]> wrote:
On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:
Has anyone come up with the answer about ordering for tryLock, or have I missed it?

You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

Peter Levart


On 09/16/2015 03:26 AM, Vitaly Davidovich wrote:

You'd differentiate via whatever protocol is chosen by implementation, i.e. some state that would be set to signal phase changes.  The closest analog is code using CAS, successful or not, to piggyback ordering for surrounding memory ops.


You can piggyback ordering of surrounding memory ops if you can reason about the phase changes using the state manipulated by CAS even in case of a failed CAS if that failure is guaranteed to not be spurious. It's the same with lock, but only if the tryLock succeeds. I argue that you can not reason about the phase of other thread(s) on the failed tryLock even if that is not a spurious failure. A non-spurious tryLock failure simply means that some thread has a lock, but does not tell which one.

  I gave an example upthread where threads compete to get exclusive right to close a socket using tryLock; one thread wins and closes the socket, and losers skip closing the socket but proceed to use memory set inside unlock().  It's a semi contrived example since normally you'd use a CAS for something like this, but it illustrates the gist of what I'm getting at.  Of course you can solve these situations using dedicated atomic (or otherwise threadsafe) state, but perhaps one wants to piggyback on existing critical section's fences and not add additional ones.


Ah, I see. The following example, right?

On 09/04/2015 05:09 AM, Vitaly Davidovich wrote:

If thread A releases a lock and threads B and C tryLock it, with one succeeding, the failing thread may want to do something else but wants a happens-before edge with the lock release - that's the general use case.  As a simple example, consider two threads tryLock'ing to acquire the exclusive right to close a socket and then perform some additional actions that require ordering of actions done by the releasing thread.  The thread failing to acquire the lock will skip closing the socket but will proceed to do some work that requires happens-before edge.  This is typically done using CAS, with one thread successfully flipping the state, and the others just skip that action that's guarded by the CAS, but can proceed with doing subsequent work.  In other words, one may want to piggyback on the unlock/tryLock to provide the ordering rather than introducing additional dedicated state for this.


That's exactly what I'm talking about. B or C failing tryLock doesn't mean one of them succeeded (how would any of B or C know that the other succeeded if there's no other communication between the two?). It could simply mean that both failed because A hasn't released the lock yet. If the later is the case, then any relaxed memory operations performed by A before releasing the lock can be observed in B or C failing tryLock in arbitrary order regardless of whether failed tryLock "guarantees" ordering or not, because there hasn't been an unlock in A yet. If those operations are not relaxed but follow some release/acquire or SC semantics, then they operate by themselves and don't require a failed tryLock to guarantee ordering.

Regards, Peter

sent from my phone

On Sep 15, 2015 4:52 PM, "Peter Levart" <[hidden email]> wrote:


On 09/15/2015 02:56 PM, Vitaly Davidovich wrote:

Hmm, the ordering I had in mind was unlock() happens-before a failing tryLock.  So a thread failing on tryLock sees operations preceded by last unlock() as ordered.  This is no different than successful tryLock or lock() in that regard.


How can you differentiate between:
- unlock() in thread T1 followed by unsuccessful tryLock() in thread T2 and
- unsuccessfull tryLock() in T2 followed by unlock() in T1

You want T1 to already unlock before you observe tryLock() failing in T2 (pressumably because some T3 has already obtained the lock before T2 or because of spurious failure).
But T2 failing tryLock() might simply be because T1 hasn't unlocked yet. So regardless of memory effects, you can't reason of ordering in other threads by observing tryLock() failing.

Do you have an example that proves me wrong?

Regards, Peter

sent from my phone

On Sep 15, 2015 1:16 AM, "Hans Boehm" <[hidden email]> wrote:
How does it slow down lock()?

It depends on the precise guarantee you provide, and I suspect this thread didn't quite agree on that.  The most natural one is that the succeeding lock acquisition happens before the failed trylock().  That implies that if we have

x = 1;
lock();

those can't be reordered by the hardware, since a failing trylock() would have to see the assignment to x.  That requires a fence between them on ARM or Power.

I think the right way to think of trylock(), at least informally, is as allowing spurious failures. I.e. trylock() is allowed to behave as though the lock was held when it isn't.  You thus can't conclude anything about other threads from the fact that it failed.  In this view you don't have to think about memory ordering issues when reasoning about correctness, you just reason about spurious failures instead.

If your code is robust against unknown, e.g. debugger, threads acquiring the lock now and then, then it must be robust against this sort of spurious failure.  If the lock is really used only to provide mutual exclusion, this should not affect correctness.

On Mon, Sep 14, 2015 at 6:41 PM, Vitaly Davidovich <[hidden email]> wrote:

How does it slow down lock()?

I don't necessarily disagree but I can certainly see people considering tryLock to have same ordering effect as (failed) CAS.  It's certainly true that a CAS is a lower level primitive than a lock, but I don't know if that resonates immediately when thinking about this.  It's also the case that on very popular platforms such as x86 a failing tryLock will have the same ordering as a successful one, and no difference is observed (and JIT doesn't do anything different).

I don't understand the debugger thread example - what's the issue there?

sent from my phone

On Sep 14, 2015 9:07 PM, "Hans Boehm" <[hidden email]> wrote:
FWIW, this general issues is discussed in section 3 of http://dl.acm.org/citation.cfm?id=1375581.1375591 .

Yet another argument against providing the stronger guarantees is that, on many architectures, it doesn't just slow down trylock(), it more importantly slows down lock().  In general, if your code cares about ordering for unsuccessful trylock(), then it's not robust against, say, a debugging thread unexpectedly acquiring the lock for a short period.  In my view, in such a case, you're no longer using it as a lock, and you should be using something else, e.g. an atomic object, with stronger guarantees.

On Fri, Sep 4, 2015 at 4:18 AM, Doug Lea <[hidden email]> wrote:
On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:
Has anyone come up with the answer about ordering for tryLock, or have I missed it?

You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

Vitaly Davidovich

Yes, I agree with what you're saying.  My initial gripe was with difference between failing tryLock and CAS, but given that a CAS is done against communicated state, it makes sense to have different ordering semantics on failure.  Also, given more granular fencing will be available, it's easier and more straightforward to explicitly indicate more precise ordering when needed.

sent from my phone

On Sep 16, 2015 3:12 AM, "Peter Levart" <[hidden email]> wrote:


On 09/16/2015 03:26 AM, Vitaly Davidovich wrote:

You'd differentiate via whatever protocol is chosen by implementation, i.e. some state that would be set to signal phase changes.  The closest analog is code using CAS, successful or not, to piggyback ordering for surrounding memory ops.


You can piggyback ordering of surrounding memory ops if you can reason about the phase changes using the state manipulated by CAS even in case of a failed CAS if that failure is guaranteed to not be spurious. It's the same with lock, but only if the tryLock succeeds. I argue that you can not reason about the phase of other thread(s) on the failed tryLock even if that is not a spurious failure. A non-spurious tryLock failure simply means that some thread has a lock, but does not tell which one.

  I gave an example upthread where threads compete to get exclusive right to close a socket using tryLock; one thread wins and closes the socket, and losers skip closing the socket but proceed to use memory set inside unlock().  It's a semi contrived example since normally you'd use a CAS for something like this, but it illustrates the gist of what I'm getting at.  Of course you can solve these situations using dedicated atomic (or otherwise threadsafe) state, but perhaps one wants to piggyback on existing critical section's fences and not add additional ones.


Ah, I see. The following example, right?

On 09/04/2015 05:09 AM, Vitaly Davidovich wrote:

If thread A releases a lock and threads B and C tryLock it, with one succeeding, the failing thread may want to do something else but wants a happens-before edge with the lock release - that's the general use case.  As a simple example, consider two threads tryLock'ing to acquire the exclusive right to close a socket and then perform some additional actions that require ordering of actions done by the releasing thread.  The thread failing to acquire the lock will skip closing the socket but will proceed to do some work that requires happens-before edge.  This is typically done using CAS, with one thread successfully flipping the state, and the others just skip that action that's guarded by the CAS, but can proceed with doing subsequent work.  In other words, one may want to piggyback on the unlock/tryLock to provide the ordering rather than introducing additional dedicated state for this.


That's exactly what I'm talking about. B or C failing tryLock doesn't mean one of them succeeded (how would any of B or C know that the other succeeded if there's no other communication between the two?). It could simply mean that both failed because A hasn't released the lock yet. If the later is the case, then any relaxed memory operations performed by A before releasing the lock can be observed in B or C failing tryLock in arbitrary order regardless of whether failed tryLock "guarantees" ordering or not, because there hasn't been an unlock in A yet. If those operations are not relaxed but follow some release/acquire or SC semantics, then they operate by themselves and don't require a failed tryLock to guarantee ordering.

Regards, Peter

sent from my phone

On Sep 15, 2015 4:52 PM, "Peter Levart" <[hidden email]> wrote:


On 09/15/2015 02:56 PM, Vitaly Davidovich wrote:

Hmm, the ordering I had in mind was unlock() happens-before a failing tryLock.  So a thread failing on tryLock sees operations preceded by last unlock() as ordered.  This is no different than successful tryLock or lock() in that regard.


How can you differentiate between:
- unlock() in thread T1 followed by unsuccessful tryLock() in thread T2 and
- unsuccessfull tryLock() in T2 followed by unlock() in T1

You want T1 to already unlock before you observe tryLock() failing in T2 (pressumably because some T3 has already obtained the lock before T2 or because of spurious failure).
But T2 failing tryLock() might simply be because T1 hasn't unlocked yet. So regardless of memory effects, you can't reason of ordering in other threads by observing tryLock() failing.

Do you have an example that proves me wrong?

Regards, Peter

sent from my phone

On Sep 15, 2015 1:16 AM, "Hans Boehm" <[hidden email]> wrote:
How does it slow down lock()?

It depends on the precise guarantee you provide, and I suspect this thread didn't quite agree on that.  The most natural one is that the succeeding lock acquisition happens before the failed trylock().  That implies that if we have

x = 1;
lock();

those can't be reordered by the hardware, since a failing trylock() would have to see the assignment to x.  That requires a fence between them on ARM or Power.

I think the right way to think of trylock(), at least informally, is as allowing spurious failures. I.e. trylock() is allowed to behave as though the lock was held when it isn't.  You thus can't conclude anything about other threads from the fact that it failed.  In this view you don't have to think about memory ordering issues when reasoning about correctness, you just reason about spurious failures instead.

If your code is robust against unknown, e.g. debugger, threads acquiring the lock now and then, then it must be robust against this sort of spurious failure.  If the lock is really used only to provide mutual exclusion, this should not affect correctness.

On Mon, Sep 14, 2015 at 6:41 PM, Vitaly Davidovich <[hidden email]> wrote:

How does it slow down lock()?

I don't necessarily disagree but I can certainly see people considering tryLock to have same ordering effect as (failed) CAS.  It's certainly true that a CAS is a lower level primitive than a lock, but I don't know if that resonates immediately when thinking about this.  It's also the case that on very popular platforms such as x86 a failing tryLock will have the same ordering as a successful one, and no difference is observed (and JIT doesn't do anything different).

I don't understand the debugger thread example - what's the issue there?

sent from my phone

On Sep 14, 2015 9:07 PM, "Hans Boehm" <[hidden email]> wrote:
FWIW, this general issues is discussed in section 3 of http://dl.acm.org/citation.cfm?id=1375581.1375591 .

Yet another argument against providing the stronger guarantees is that, on many architectures, it doesn't just slow down trylock(), it more importantly slows down lock().  In general, if your code cares about ordering for unsuccessful trylock(), then it's not robust against, say, a debugging thread unexpectedly acquiring the lock for a short period.  In my view, in such a case, you're no longer using it as a lock, and you should be using something else, e.g. an atomic object, with stronger guarantees.

On Fri, Sep 4, 2015 at 4:18 AM, Doug Lea <[hidden email]> wrote:
On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:
Has anyone come up with the answer about ordering for tryLock, or have I missed it?

You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

oleksandr otenko
In reply to this post by Hans Boehm
Wow, that's some very flaky relaxation of the meaning of a lock!

I would expect the failing lock acquire to establish no sw edges, but I would certainly expect the order of acquires and releases (successful and no) to be total, and the total order of all acquires and releases (successful and no) to be in alignment with the total order of operations on volatiles. That way indeed x=1 should be visible, but only if it is a volatile store - no guarantees for normal stores.

Also, I would not expect the debugging thread to acquire the lock, if that breaks the protocol. You wouldn't encourage a debugging thread to write to arbitrary volatiles - so you wouldn't encourage the debugging thread to acquire arbitrary locks.

Alex

On 15/09/2015 06:16, Hans Boehm wrote:
How does it slow down lock()?

It depends on the precise guarantee you provide, and I suspect this thread didn't quite agree on that.  The most natural one is that the succeeding lock acquisition happens before the failed trylock().  That implies that if we have

x = 1;
lock();

those can't be reordered by the hardware, since a failing trylock() would have to see the assignment to x.  That requires a fence between them on ARM or Power.

I think the right way to think of trylock(), at least informally, is as allowing spurious failures. I.e. trylock() is allowed to behave as though the lock was held when it isn't.  You thus can't conclude anything about other threads from the fact that it failed.  In this view you don't have to think about memory ordering issues when reasoning about correctness, you just reason about spurious failures instead.

If your code is robust against unknown, e.g. debugger, threads acquiring the lock now and then, then it must be robust against this sort of spurious failure.  If the lock is really used only to provide mutual exclusion, this should not affect correctness.

On Mon, Sep 14, 2015 at 6:41 PM, Vitaly Davidovich <[hidden email]> wrote:

How does it slow down lock()?

I don't necessarily disagree but I can certainly see people considering tryLock to have same ordering effect as (failed) CAS.  It's certainly true that a CAS is a lower level primitive than a lock, but I don't know if that resonates immediately when thinking about this.  It's also the case that on very popular platforms such as x86 a failing tryLock will have the same ordering as a successful one, and no difference is observed (and JIT doesn't do anything different).

I don't understand the debugger thread example - what's the issue there?

sent from my phone

On Sep 14, 2015 9:07 PM, "Hans Boehm" <[hidden email]> wrote:
FWIW, this general issues is discussed in section 3 of http://dl.acm.org/citation.cfm?id=1375581.1375591 .

Yet another argument against providing the stronger guarantees is that, on many architectures, it doesn't just slow down trylock(), it more importantly slows down lock().  In general, if your code cares about ordering for unsuccessful trylock(), then it's not robust against, say, a debugging thread unexpectedly acquiring the lock for a short period.  In my view, in such a case, you're no longer using it as a lock, and you should be using something else, e.g. an atomic object, with stronger guarantees.

On Fri, Sep 4, 2015 at 4:18 AM, Doug Lea <[hidden email]> wrote:
On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:
Has anyone come up with the answer about ordering for tryLock, or have I missed it?

You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

oleksandr otenko
In reply to this post by Peter Levart
It is even ok to assume a failing CAS doesn't provide any fences. The code will have to use volatiles to communicate anything, if it never retries a failing CAS - and still the JVM should be able to eliminate the unnecessary store barriers on platforms where the failing CAS provides such a barrier anyway. So I would not demand the failing CAS to have fencing semantics.

Yet I do expect the CAS to only ever fail if the underlying value has been modified by another thread and no other circumstances (eg not because of a cache line invalidated because of a write to another location on the same line). Otherwise a boolean return value is too small for efficient cooperative concurrency.

In the same way a tryLock should only ever fail if some other thread held the lock at the time the acquire was attempted - in the meaning of system-wide progress: if I failed to acquire the lock, I should be able to conclude someone else is making progress; debug thread is not making progress, so it is changing the meaning of the lock.

Alex

On 16/09/2015 08:12, Peter Levart wrote:


On 09/16/2015 03:26 AM, Vitaly Davidovich wrote:

You'd differentiate via whatever protocol is chosen by implementation, i.e. some state that would be set to signal phase changes.  The closest analog is code using CAS, successful or not, to piggyback ordering for surrounding memory ops.


You can piggyback ordering of surrounding memory ops if you can reason about the phase changes using the state manipulated by CAS even in case of a failed CAS if that failure is guaranteed to not be spurious. It's the same with lock, but only if the tryLock succeeds. I argue that you can not reason about the phase of other thread(s) on the failed tryLock even if that is not a spurious failure. A non-spurious tryLock failure simply means that some thread has a lock, but does not tell which one.

  I gave an example upthread where threads compete to get exclusive right to close a socket using tryLock; one thread wins and closes the socket, and losers skip closing the socket but proceed to use memory set inside unlock().  It's a semi contrived example since normally you'd use a CAS for something like this, but it illustrates the gist of what I'm getting at.  Of course you can solve these situations using dedicated atomic (or otherwise threadsafe) state, but perhaps one wants to piggyback on existing critical section's fences and not add additional ones.


Ah, I see. The following example, right?

On 09/04/2015 05:09 AM, Vitaly Davidovich wrote:

If thread A releases a lock and threads B and C tryLock it, with one succeeding, the failing thread may want to do something else but wants a happens-before edge with the lock release - that's the general use case.  As a simple example, consider two threads tryLock'ing to acquire the exclusive right to close a socket and then perform some additional actions that require ordering of actions done by the releasing thread.  The thread failing to acquire the lock will skip closing the socket but will proceed to do some work that requires happens-before edge.  This is typically done using CAS, with one thread successfully flipping the state, and the others just skip that action that's guarded by the CAS, but can proceed with doing subsequent work.  In other words, one may want to piggyback on the unlock/tryLock to provide the ordering rather than introducing additional dedicated state for this.


That's exactly what I'm talking about. B or C failing tryLock doesn't mean one of them succeeded (how would any of B or C know that the other succeeded if there's no other communication between the two?). It could simply mean that both failed because A hasn't released the lock yet. If the later is the case, then any relaxed memory operations performed by A before releasing the lock can be observed in B or C failing tryLock in arbitrary order regardless of whether failed tryLock "guarantees" ordering or not, because there hasn't been an unlock in A yet. If those operations are not relaxed but follow some release/acquire or SC semantics, then they operate by themselves and don't require a failed tryLock to guarantee ordering.

Regards, Peter

sent from my phone

On Sep 15, 2015 4:52 PM, "Peter Levart" <[hidden email]> wrote:


On 09/15/2015 02:56 PM, Vitaly Davidovich wrote:

Hmm, the ordering I had in mind was unlock() happens-before a failing tryLock.  So a thread failing on tryLock sees operations preceded by last unlock() as ordered.  This is no different than successful tryLock or lock() in that regard.


How can you differentiate between:
- unlock() in thread T1 followed by unsuccessful tryLock() in thread T2 and
- unsuccessfull tryLock() in T2 followed by unlock() in T1

You want T1 to already unlock before you observe tryLock() failing in T2 (pressumably because some T3 has already obtained the lock before T2 or because of spurious failure).
But T2 failing tryLock() might simply be because T1 hasn't unlocked yet. So regardless of memory effects, you can't reason of ordering in other threads by observing tryLock() failing.

Do you have an example that proves me wrong?

Regards, Peter

sent from my phone

On Sep 15, 2015 1:16 AM, "Hans Boehm" <[hidden email]> wrote:
How does it slow down lock()?

It depends on the precise guarantee you provide, and I suspect this thread didn't quite agree on that.  The most natural one is that the succeeding lock acquisition happens before the failed trylock().  That implies that if we have

x = 1;
lock();

those can't be reordered by the hardware, since a failing trylock() would have to see the assignment to x.  That requires a fence between them on ARM or Power.

I think the right way to think of trylock(), at least informally, is as allowing spurious failures. I.e. trylock() is allowed to behave as though the lock was held when it isn't.  You thus can't conclude anything about other threads from the fact that it failed.  In this view you don't have to think about memory ordering issues when reasoning about correctness, you just reason about spurious failures instead.

If your code is robust against unknown, e.g. debugger, threads acquiring the lock now and then, then it must be robust against this sort of spurious failure.  If the lock is really used only to provide mutual exclusion, this should not affect correctness.

On Mon, Sep 14, 2015 at 6:41 PM, Vitaly Davidovich <[hidden email]> wrote:

How does it slow down lock()?

I don't necessarily disagree but I can certainly see people considering tryLock to have same ordering effect as (failed) CAS.  It's certainly true that a CAS is a lower level primitive than a lock, but I don't know if that resonates immediately when thinking about this.  It's also the case that on very popular platforms such as x86 a failing tryLock will have the same ordering as a successful one, and no difference is observed (and JIT doesn't do anything different).

I don't understand the debugger thread example - what's the issue there?

sent from my phone

On Sep 14, 2015 9:07 PM, "Hans Boehm" <[hidden email]> wrote:
FWIW, this general issues is discussed in section 3 of http://dl.acm.org/citation.cfm?id=1375581.1375591 .

Yet another argument against providing the stronger guarantees is that, on many architectures, it doesn't just slow down trylock(), it more importantly slows down lock().  In general, if your code cares about ordering for unsuccessful trylock(), then it's not robust against, say, a debugging thread unexpectedly acquiring the lock for a short period.  In my view, in such a case, you're no longer using it as a lock, and you should be using something else, e.g. an atomic object, with stronger guarantees.

On Fri, Sep 4, 2015 at 4:18 AM, Doug Lea <[hidden email]> wrote:
On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:
Has anyone come up with the answer about ordering for tryLock, or have I missed it?

You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

Hans Boehm
In reply to this post by oleksandr otenko
This all depends on your perspective.  In my view, if we assumed that trylock() always provides a correct response based on the current state of the lock (something you shouldn't be assuming), then I think we would definitely want the x = 1 to be visible after a failed trylock().  If you write (and yes you shouldn't):

Thread 1:
x = 1;
y.lock();

Thread 2:
while (y.trylock()) {y.unlock();}
load x;

That program does not intuitively have a data race.  The load and store to x cannot occur simultaneously. Thus it should have interleaving-based semantics.  Which means the load of x must return 1.

If there is no sw edge from the lock() to the failed trylock(), then it does have a data race.  But that's a contrivance of the model; data races no longer correspond to actual possible concurrent execution in a simple model.  In my view that's bad, since I can no longer rely on intuitions based on simultaneous executions for "data races".  I actually have to teach people about all the happens-before rules.  With the perspective of our PLDI 2008 paper, the two possible definitions of data race no longer agree.

People do very occasionally write code like this, vaguely along the lines of the close example in this thread.  ("Somebody's already working on it, so I don't have to.") Weaker ordering properties that guarantee correctness are really hard to explain and at best brittle.  (But that thread had the lock, of course it finished running that earlier initialization code.)  I think it's far easier to just say "trylock() may spuriously fail.  Your code doesn't work.  Use e.g. an atomic getAndSet() instead."


On Wed, Sep 16, 2015 at 8:28 AM, Oleksandr Otenko <[hidden email]> wrote:
Wow, that's some very flaky relaxation of the meaning of a lock!

I would expect the failing lock acquire to establish no sw edges, but I would certainly expect the order of acquires and releases (successful and no) to be total, and the total order of all acquires and releases (successful and no) to be in alignment with the total order of operations on volatiles. That way indeed x=1 should be visible, but only if it is a volatile store - no guarantees for normal stores.

Also, I would not expect the debugging thread to acquire the lock, if that breaks the protocol. You wouldn't encourage a debugging thread to write to arbitrary volatiles - so you wouldn't encourage the debugging thread to acquire arbitrary locks.

Alex


On 15/09/2015 06:16, Hans Boehm wrote:
How does it slow down lock()?

It depends on the precise guarantee you provide, and I suspect this thread didn't quite agree on that.  The most natural one is that the succeeding lock acquisition happens before the failed trylock().  That implies that if we have

x = 1;
lock();

those can't be reordered by the hardware, since a failing trylock() would have to see the assignment to x.  That requires a fence between them on ARM or Power.

I think the right way to think of trylock(), at least informally, is as allowing spurious failures. I.e. trylock() is allowed to behave as though the lock was held when it isn't.  You thus can't conclude anything about other threads from the fact that it failed.  In this view you don't have to think about memory ordering issues when reasoning about correctness, you just reason about spurious failures instead.

If your code is robust against unknown, e.g. debugger, threads acquiring the lock now and then, then it must be robust against this sort of spurious failure.  If the lock is really used only to provide mutual exclusion, this should not affect correctness.

On Mon, Sep 14, 2015 at 6:41 PM, Vitaly Davidovich <[hidden email]> wrote:

How does it slow down lock()?

I don't necessarily disagree but I can certainly see people considering tryLock to have same ordering effect as (failed) CAS.  It's certainly true that a CAS is a lower level primitive than a lock, but I don't know if that resonates immediately when thinking about this.  It's also the case that on very popular platforms such as x86 a failing tryLock will have the same ordering as a successful one, and no difference is observed (and JIT doesn't do anything different).

I don't understand the debugger thread example - what's the issue there?

sent from my phone

On Sep 14, 2015 9:07 PM, "Hans Boehm" <[hidden email]> wrote:
FWIW, this general issues is discussed in section 3 of http://dl.acm.org/citation.cfm?id=1375581.1375591 .

Yet another argument against providing the stronger guarantees is that, on many architectures, it doesn't just slow down trylock(), it more importantly slows down lock().  In general, if your code cares about ordering for unsuccessful trylock(), then it's not robust against, say, a debugging thread unexpectedly acquiring the lock for a short period.  In my view, in such a case, you're no longer using it as a lock, and you should be using something else, e.g. an atomic object, with stronger guarantees.

On Fri, Sep 4, 2015 at 4:18 AM, Doug Lea <[hidden email][hidden email]> wrote:
On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:
Has anyone come up with the answer about ordering for tryLock, or have I missed it?

You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest




_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

David Holmes-6

Late to the party here but I’ve been on a nice long vacation … J

 

The argument below is to me conflating a temporal notion of “happens before” and the memory-model notion. Just because I can reason that thread A must have already performed a certain action (ie x=1) because thread B can’t get the lock, it does not mean that action is visible to thread B, unless a suitable happens-before edge exists. This should not be at all surprising because the store and lock in Thread A can already be freely reordered, if no HB edge exists.

 

David

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Hans Boehm
Sent: Thursday, September 17, 2015 3:28 AM
To: Oleksandr Otenko
Cc: Doug Lea; [hidden email]
Subject: Re: [concurrency-interest] jdk9 VarHandle and Fence methods

 

This all depends on your perspective.  In my view, if we assumed that trylock() always provides a correct response based on the current state of the lock (something you shouldn't be assuming), then I think we would definitely want the x = 1 to be visible after a failed trylock().  If you write (and yes you shouldn't):

 

Thread 1:

x = 1;

y.lock();

 

Thread 2:

while (y.trylock()) {y.unlock();}

load x;

 

That program does not intuitively have a data race.  The load and store to x cannot occur simultaneously. Thus it should have interleaving-based semantics.  Which means the load of x must return 1.

 

If there is no sw edge from the lock() to the failed trylock(), then it does have a data race.  But that's a contrivance of the model; data races no longer correspond to actual possible concurrent execution in a simple model.  In my view that's bad, since I can no longer rely on intuitions based on simultaneous executions for "data races".  I actually have to teach people about all the happens-before rules.  With the perspective of our PLDI 2008 paper, the two possible definitions of data race no longer agree.

 

People do very occasionally write code like this, vaguely along the lines of the close example in this thread.  ("Somebody's already working on it, so I don't have to.") Weaker ordering properties that guarantee correctness are really hard to explain and at best brittle.  (But that thread had the lock, of course it finished running that earlier initialization code.)  I think it's far easier to just say "trylock() may spuriously fail.  Your code doesn't work.  Use e.g. an atomic getAndSet() instead."

 

 

On Wed, Sep 16, 2015 at 8:28 AM, Oleksandr Otenko <[hidden email]> wrote:

Wow, that's some very flaky relaxation of the meaning of a lock!

I would expect the failing lock acquire to establish no sw edges, but I would certainly expect the order of acquires and releases (successful and no) to be total, and the total order of all acquires and releases (successful and no) to be in alignment with the total order of operations on volatiles. That way indeed x=1 should be visible, but only if it is a volatile store - no guarantees for normal stores.

Also, I would not expect the debugging thread to acquire the lock, if that breaks the protocol. You wouldn't encourage a debugging thread to write to arbitrary volatiles - so you wouldn't encourage the debugging thread to acquire arbitrary locks.

Alex

 

On 15/09/2015 06:16, Hans Boehm wrote:

How does it slow down lock()?

 

It depends on the precise guarantee you provide, and I suspect this thread didn't quite agree on that.  The most natural one is that the succeeding lock acquisition happens before the failed trylock().  That implies that if we have

 

x = 1;

lock();

 

those can't be reordered by the hardware, since a failing trylock() would have to see the assignment to x.  That requires a fence between them on ARM or Power.

 

I think the right way to think of trylock(), at least informally, is as allowing spurious failures. I.e. trylock() is allowed to behave as though the lock was held when it isn't.  You thus can't conclude anything about other threads from the fact that it failed.  In this view you don't have to think about memory ordering issues when reasoning about correctness, you just reason about spurious failures instead.

 

If your code is robust against unknown, e.g. debugger, threads acquiring the lock now and then, then it must be robust against this sort of spurious failure.  If the lock is really used only to provide mutual exclusion, this should not affect correctness.

 

On Mon, Sep 14, 2015 at 6:41 PM, Vitaly Davidovich <[hidden email]> wrote:

How does it slow down lock()?

I don't necessarily disagree but I can certainly see people considering tryLock to have same ordering effect as (failed) CAS.  It's certainly true that a CAS is a lower level primitive than a lock, but I don't know if that resonates immediately when thinking about this.  It's also the case that on very popular platforms such as x86 a failing tryLock will have the same ordering as a successful one, and no difference is observed (and JIT doesn't do anything different).

I don't understand the debugger thread example - what's the issue there?

sent from my phone

On Sep 14, 2015 9:07 PM, "Hans Boehm" <[hidden email]> wrote:

FWIW, this general issues is discussed in section 3 of http://dl.acm.org/citation.cfm?id=1375581.1375591 .

 

Yet another argument against providing the stronger guarantees is that, on many architectures, it doesn't just slow down trylock(), it more importantly slows down lock().  In general, if your code cares about ordering for unsuccessful trylock(), then it's not robust against, say, a debugging thread unexpectedly acquiring the lock for a short period.  In my view, in such a case, you're no longer using it as a lock, and you should be using something else, e.g. an atomic object, with stronger guarantees.

 

On Fri, Sep 4, 2015 at 4:18 AM, Doug Lea <[hidden email]> wrote:

On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:

Has anyone come up with the answer about ordering for tryLock, or have I missed it?


You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug



_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

 


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

 

 

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

 

 


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: jdk9 VarHandle and Fence methods

Alex Otenko
Well, that’s true for “normal” store. But I am considering a volatile store to x, so it doesn’t get reordered with y.lock().

Alex

On 14 Oct 2015, at 05:35, David Holmes <[hidden email]> wrote:

Late to the party here but I’ve been on a nice long vacation … J
 
The argument below is to me conflating a temporal notion of “happens before” and the memory-model notion. Just because I can reason that thread A must have already performed a certain action (ie x=1) because thread B can’t get the lock, it does not mean that action is visible to thread B, unless a suitable happens-before edge exists. This should not be at all surprising because the store and lock in Thread A can already be freely reordered, if no HB edge exists.
 
David
 
From: [hidden email] [[hidden email]] On Behalf Of Hans Boehm
Sent: Thursday, September 17, 2015 3:28 AM
To: Oleksandr Otenko
Cc: Doug Lea; [hidden email]
Subject: Re: [concurrency-interest] jdk9 VarHandle and Fence methods
 
This all depends on your perspective.  In my view, if we assumed that trylock() always provides a correct response based on the current state of the lock (something you shouldn't be assuming), then I think we would definitely want the x = 1 to be visible after a failed trylock().  If you write (and yes you shouldn't):
 
Thread 1:
x = 1;
y.lock();
 
Thread 2:
while (y.trylock()) {y.unlock();}
load x;
 
That program does not intuitively have a data race.  The load and store to x cannot occur simultaneously. Thus it should have interleaving-based semantics.  Which means the load of x must return 1.
 
If there is no sw edge from the lock() to the failed trylock(), then it does have a data race.  But that's a contrivance of the model; data races no longer correspond to actual possible concurrent execution in a simple model.  In my view that's bad, since I can no longer rely on intuitions based on simultaneous executions for "data races".  I actually have to teach people about all the happens-before rules.  With the perspective of our PLDI 2008 paper, the two possible definitions of data race no longer agree.
 
People do very occasionally write code like this, vaguely along the lines of the close example in this thread.  ("Somebody's already working on it, so I don't have to.") Weaker ordering properties that guarantee correctness are really hard to explain and at best brittle.  (But that thread had the lock, of course it finished running that earlier initialization code.)  I think it's far easier to just say "trylock() may spuriously fail.  Your code doesn't work.  Use e.g. an atomic getAndSet() instead."
 
 
On Wed, Sep 16, 2015 at 8:28 AM, Oleksandr Otenko <[hidden email]> wrote:
Wow, that's some very flaky relaxation of the meaning of a lock!

I would expect the failing lock acquire to establish no sw edges, but I would certainly expect the order of acquires and releases (successful and no) to be total, and the total order of all acquires and releases (successful and no) to be in alignment with the total order of operations on volatiles. That way indeed x=1 should be visible, but only if it is a volatile store - no guarantees for normal stores.

Also, I would not expect the debugging thread to acquire the lock, if that breaks the protocol. You wouldn't encourage a debugging thread to write to arbitrary volatiles - so you wouldn't encourage the debugging thread to acquire arbitrary locks.

Alex

 

On 15/09/2015 06:16, Hans Boehm wrote:
How does it slow down lock()?
 
It depends on the precise guarantee you provide, and I suspect this thread didn't quite agree on that.  The most natural one is that the succeeding lock acquisition happens before the failed trylock().  That implies that if we have
 
x = 1;
lock();
 
those can't be reordered by the hardware, since a failing trylock() would have to see the assignment to x.  That requires a fence between them on ARM or Power.
 
I think the right way to think of trylock(), at least informally, is as allowing spurious failures. I.e. trylock() is allowed to behave as though the lock was held when it isn't.  You thus can't conclude anything about other threads from the fact that it failed.  In this view you don't have to think about memory ordering issues when reasoning about correctness, you just reason about spurious failures instead. 
 
If your code is robust against unknown, e.g. debugger, threads acquiring the lock now and then, then it must be robust against this sort of spurious failure.  If the lock is really used only to provide mutual exclusion, this should not affect correctness.
 
On Mon, Sep 14, 2015 at 6:41 PM, Vitaly Davidovich <[hidden email]> wrote:

How does it slow down lock()?

I don't necessarily disagree but I can certainly see people considering tryLock to have same ordering effect as (failed) CAS.  It's certainly true that a CAS is a lower level primitive than a lock, but I don't know if that resonates immediately when thinking about this.  It's also the case that on very popular platforms such as x86 a failing tryLock will have the same ordering as a successful one, and no difference is observed (and JIT doesn't do anything different).

I don't understand the debugger thread example - what's the issue there?

sent from my phone

On Sep 14, 2015 9:07 PM, "Hans Boehm" <[hidden email]> wrote:
FWIW, this general issues is discussed in section 3 of http://dl.acm.org/citation.cfm?id=1375581.1375591 . 
 
Yet another argument against providing the stronger guarantees is that, on many architectures, it doesn't just slow down trylock(), it more importantly slows down lock().  In general, if your code cares about ordering for unsuccessful trylock(), then it's not robust against, say, a debugging thread unexpectedly acquiring the lock for a short period.  In my view, in such a case, you're no longer using it as a lock, and you should be using something else, e.g. an atomic object, with stronger guarantees.
 
On Fri, Sep 4, 2015 at 4:18 AM, Doug Lea <[hidden email]> wrote:
On 09/03/2015 02:19 PM, Oleksandr Otenko wrote:
Has anyone come up with the answer about ordering for tryLock, or have I missed it?

You missed the dog not barking :-)

The Lock specs don't require any specific HB effects here on failed
tryLock. Even if we wanted to, we cannot retroactively impose any
considering that anyone can implement the Lock interface (not just j.u.c)
and some of these might become in violation.

As you and Vitaly pointed out, there are a few fringe cases where
users might want to impose ordering on failure. In jdk9, you'll
me able to do this with moded VarHandle accesses and/or fences. The
resulting extra fencing might be redundant here and there, but if you
cared enough, you could create and rely on custom locks with stronger
guarantees.

-Doug


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
 


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

 

 

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
 
 
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
1234