[concurrency-interest] jdk9 VarHandle and Fence methods

Vitaly Davidovich vitalyd at gmail.com
Wed Sep 16 09:53:38 EDT 2015

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

sent from my phone
On Sep 16, 2015 3:12 AM, "Peter Levart" <peter.levart at gmail.com> 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" <peter.levart at gmail.com> 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" <boehm at acm.org> 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 <vitalyd at gmail.com>
>>> 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" <boehm at acm.org> 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 <dl at cs.oswego.edu> 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
>>>>>> Concurrency-interest at cs.oswego.edu
>>>>>> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>>>>> _______________________________________________
>>>>> Concurrency-interest mailing list
>>>>> Concurrency-interest at cs.oswego.edu
>>>>> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>> _______________________________________________
>> Concurrency-interest mailing listConcurrency-interest at cs.oswego.eduhttp://cs.oswego.edu/mailman/listinfo/concurrency-interest
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20150916/3256b733/attachment-0001.html>

More information about the Concurrency-interest mailing list