AtomicReference CAS signatures

classic Classic list List threaded Threaded
89 messages Options
12345
Reply | Threaded
Open this post in threaded view
|

AtomicReference CAS signatures

thurstonn
Hello,

I was curious as to why there isn't an AtomicReference CAS:

T cas(T expectedValue, T newValue)

where the returned T represents the oldValue (if it == expectedValue, then CAS succeeded).

(LOCK) CMPXCHG on x86* platforms provides this behavior directly.

It's more flexible than the present
boolean compareAndSet(T expected, T new), which is fine as it goes, but is not equivalent

Is it because other JVM platforms don't natively provide the equivalent instruction?

Reply | Threaded
Open this post in threaded view
|

Re: AtomicReference CAS signatures

Vitaly Davidovich

So the hardware is going to return the unexpected value - that's going to be an oop address/pointer.  Are you suggesting the JVM should go and load that object as well?

Sent from my phone

On Jan 9, 2015 5:22 PM, "thurstonn" <[hidden email]> wrote:
Hello,

I was curious as to why there isn't an AtomicReference CAS:

T cas(T expectedValue, T newValue)

where the returned T represents the oldValue (if it == expectedValue, then
CAS succeeded).

(LOCK) CMPXCHG on x86* platforms provides this behavior directly.

It's more flexible than the present
boolean compareAndSet(T expected, T new), which is fine as it goes, but is
not equivalent

Is it because other JVM platforms don't natively provide the equivalent
instruction?





--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/AtomicReference-CAS-signatures-tp11820.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: AtomicReference CAS signatures

Justin Sampson
Vitaly Davidovich wrote:

> So the hardware is going to return the unexpected value - that's
> going to be an oop address/pointer. Are you suggesting the JVM
> should go and load that object as well?

Are you saying that the value sitting in the accumulator register
isn't just the reference value to be returned? The referenced object
doesn't have to be loaded before returning it. Or is there some kind
of additional pointer indirection going on? Thurston's question is
still valid looking at AtomicInteger instead of AtomicReference, if
that helps clarify the issue. I'm very curious about the answer
myself. :)

Thurston: There's also getAndSet and (in Java 8) getAndUpdate, which
do return the old value. They have loops calling compareAndSet
internally. Presumably if they could be implemented better with a
proper compare-and-swap the implementers could make that change and
preserve the API, though they would still require loops; they would
just eliminate an extra volatile load per retry in the presence of
contention.

Cheers,
Justin

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

Re: AtomicReference CAS signatures

Vitaly Davidovich

Sorry, I didn't really phrase my reply/question properly.  If you were to return the current value, isn't it the case that by the time the caller sees it that it may no longer be the actual value/object there? For AtomicInteger I think that's fine because it's just a value type, but I'm not sure that's very useful for references.  Also, the caller presumably would have to do their own reference identity check to figure out whether the cas succeeded or not, making the code longer for an (I think?) uncommon use case.

To answer your indirection question, no, I don't think there's any extra indirection; the register will contain the address (raw ptr or encoded object ptr if compressed oops are used) of the object, but that's all that's needed to return T.  Again, the confusion is my fault as I see my original reply insinuated that.  My real question is what is one going to do with that T considering it's possibly stale as soon as you get it back.

Sent from my phone

Vitaly Davidovich wrote:

> So the hardware is going to return the unexpected value - that's
> going to be an oop address/pointer. Are you suggesting the JVM
> should go and load that object as well?

Are you saying that the value sitting in the accumulator register
isn't just the reference value to be returned? The referenced object
doesn't have to be loaded before returning it. Or is there some kind
of additional pointer indirection going on? Thurston's question is
still valid looking at AtomicInteger instead of AtomicReference, if
that helps clarify the issue. I'm very curious about the answer
myself. :)

Thurston: There's also getAndSet and (in Java 8) getAndUpdate, which
do return the old value. They have loops calling compareAndSet
internally. Presumably if they could be implemented better with a
proper compare-and-swap the implementers could make that change and
preserve the API, though they would still require loops; they would
just eliminate an extra volatile load per retry in the presence of
contention.

Cheers,
Justin

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

Re: AtomicReference CAS signatures

Doug Lea
In reply to this post by thurstonn
On 01/09/2015 04:57 PM, thurstonn wrote:
> Hello,
>
> I was curious as to why there isn't an AtomicReference CAS:
>
> T cas(T expectedValue, T newValue)
>
> where the returned T represents the oldValue (if it == expectedValue, then
> CAS succeeded).

Because there is no equivalent on processors (including ARM, POWER)
implementing compareAndSet using load-linked/store-conditional.
(C++11/C11 make the same choice for the same reason.)

-Doug



>
> (LOCK) CMPXCHG on x86* platforms provides this behavior directly.
>
> It's more flexible than the present
> boolean compareAndSet(T expected, T new), which is fine as it goes, but is
> not equivalent
>
> Is it because other JVM platforms don't natively provide the equivalent
> instruction?
>
>
>
>
>
> --
> View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/AtomicReference-CAS-signatures-tp11820.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: AtomicReference CAS signatures

Vitaly Davidovich
In reply to this post by Vitaly Davidovich

Actually, thinking about it a bit more, if you write (pseudocode):

Foo old = AtomicReference<Foo>.cas (expected, new);

Wouldn't the JVM have to do a type check to verify it's a Foo? Perhaps it won't if you don't dereference it though (I.e. ref check only).

Sent from my phone

On Jan 9, 2015 7:11 PM, "Vitaly Davidovich" <[hidden email]> wrote:

Sorry, I didn't really phrase my reply/question properly.  If you were to return the current value, isn't it the case that by the time the caller sees it that it may no longer be the actual value/object there? For AtomicInteger I think that's fine because it's just a value type, but I'm not sure that's very useful for references.  Also, the caller presumably would have to do their own reference identity check to figure out whether the cas succeeded or not, making the code longer for an (I think?) uncommon use case.

To answer your indirection question, no, I don't think there's any extra indirection; the register will contain the address (raw ptr or encoded object ptr if compressed oops are used) of the object, but that's all that's needed to return T.  Again, the confusion is my fault as I see my original reply insinuated that.  My real question is what is one going to do with that T considering it's possibly stale as soon as you get it back.

Sent from my phone

Vitaly Davidovich wrote:

> So the hardware is going to return the unexpected value - that's
> going to be an oop address/pointer. Are you suggesting the JVM
> should go and load that object as well?

Are you saying that the value sitting in the accumulator register
isn't just the reference value to be returned? The referenced object
doesn't have to be loaded before returning it. Or is there some kind
of additional pointer indirection going on? Thurston's question is
still valid looking at AtomicInteger instead of AtomicReference, if
that helps clarify the issue. I'm very curious about the answer
myself. :)

Thurston: There's also getAndSet and (in Java 8) getAndUpdate, which
do return the old value. They have loops calling compareAndSet
internally. Presumably if they could be implemented better with a
proper compare-and-swap the implementers could make that change and
preserve the API, though they would still require loops; they would
just eliminate an extra volatile load per retry in the presence of
contention.

Cheers,
Justin

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

Re: AtomicReference CAS signatures

Justin Sampson
Vitaly Davidovich wrote:

> Actually, thinking about it a bit more, if you write (pseudocode):
>
> Foo old = AtomicReference<Foo>.cas (expected, new);
>
> Wouldn't the JVM have to do a type check to verify it's a Foo?
> Perhaps it won't if you don't dereference it though (I.e. ref
> check only).

Yep! Generic type erasure means there's an implicit cast inserted
there, which I think is required at runtime even if it's never
dereferenced. So the object does get loaded! Surprise, surprise.

That doesn't apply to, say, an AtomicInteger, or even to an
AtomicReference<Object>. But, yeah, good catch. :)

Cheers,
Justin

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

Re: AtomicReference CAS signatures

Justin Sampson
In reply to this post by Doug Lea
Doug Lea wrote:

> On 01/09/2015 04:57 PM, thurstonn wrote:
> > Hello,
> >
> > I was curious as to why there isn't an AtomicReference CAS:
> >
> > T cas(T expectedValue, T newValue)
> >
> > where the returned T represents the oldValue (if it ==
> > expectedValue, then CAS succeeded).
>
> Because there is no equivalent on processors (including ARM, POWER)
> implementing compareAndSet using load-linked/store-conditional.
> (C++11/C11 make the same choice for the same reason.)
>
> > (LOCK) CMPXCHG on x86* platforms provides this behavior directly.

Also, the x86 CMPXCHG instruction sets the ZF flag on success, so it can
serve equally well as a compareAndSet or a compareAndSwap without any
extra instructions.

I'm just mentioning that fact because I personally found it interesting
when I read about it while pondering similar questions just a week or
two ago. :)

In the case of LL/SC, I was briefly puzzled when I first read about it
because it would seem at first glance that we have the old value right
there in the result of the LL. But of course the SC can still fail even
if the LL returns the value we're looking for, in which case we don't
know what the now-current value actually is.

So all processors support compareAndSet directly, but only some support
compareAndSwap. Add to that the fact that compareAndSet is easier to
program against anyway, and it's clearly the only viable choice for an
intrinsic operation in the JVM.

That said, a compareAndSwap implemented on top of the existing
compareAndSet would be easy enough, and very similar to the other
high-level methods available on the AtomicXYZ classes. It's just a
matter of justifying some use cases.

Cheers,
Justin

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

Re: AtomicReference CAS signatures

David Holmes-6
Justin Sampson wrote:

> Doug Lea wrote:
>
> > On 01/09/2015 04:57 PM, thurstonn wrote:
> > > Hello,
> > >
> > > I was curious as to why there isn't an AtomicReference CAS:
> > >
> > > T cas(T expectedValue, T newValue)
> > >
> > > where the returned T represents the oldValue (if it ==
> > > expectedValue, then CAS succeeded).
> >
> > Because there is no equivalent on processors (including ARM, POWER)
> > implementing compareAndSet using load-linked/store-conditional.
> > (C++11/C11 make the same choice for the same reason.)
> >
> > > (LOCK) CMPXCHG on x86* platforms provides this behavior directly.
>
> Also, the x86 CMPXCHG instruction sets the ZF flag on success, so it can
> serve equally well as a compareAndSet or a compareAndSwap without any
> extra instructions.
>
> I'm just mentioning that fact because I personally found it interesting
> when I read about it while pondering similar questions just a week or
> two ago. :)
>
> In the case of LL/SC, I was briefly puzzled when I first read about it
> because it would seem at first glance that we have the old value right
> there in the result of the LL. But of course the SC can still fail even
> if the LL returns the value we're looking for, in which case we don't
> know what the now-current value actually is.

That would be true for a weak CAS, where we can fail spuriously, but for a
strong CAS we only fail if the compare fails, not because the SC fails.

> So all processors support compareAndSet directly, but only some support
> compareAndSwap. Add to that the fact that compareAndSet is easier to
> program against anyway, and it's clearly the only viable choice for an
> intrinsic operation in the JVM.

There's not much value in returning the value that was found in general,
because as already stated it can be stale a moment after you've read it.

> That said, a compareAndSwap implemented on top of the existing
> compareAndSet would be easy enough, and very similar to the other
> high-level methods available on the AtomicXYZ classes. It's just a
> matter of justifying some use cases.

I don't think that is true - if the value is not returned by the failing CAS
then any attempt to return whatever the value is now, may not return the
value that caused the CAS to fail - it could even return the expected value.

David
----

>
> Cheers,
> Justin
>
> _______________________________________________
> 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: AtomicReference CAS signatures

Andrew Haley
In reply to this post by Doug Lea
On 10/01/15 00:20, Doug Lea wrote:

> On 01/09/2015 04:57 PM, thurstonn wrote:
>>
>> I was curious as to why there isn't an AtomicReference CAS:
>>
>> T cas(T expectedValue, T newValue)
>>
>> where the returned T represents the oldValue (if it == expectedValue, then
>> CAS succeeded).
>
> Because there is no equivalent on processors (including ARM, POWER)
> implementing compareAndSet using load-linked/store-conditional.
> (C++11/C11 make the same choice for the same reason.)

I don't understand what you mean.  Writing a CAS with this signature
would seem to me to be pretty straightforward on ARM.  I'm wondering
if you're referring to some specific atomicity or ordering guarantees,
but I'm not aware of any problem with that either.

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

Re: AtomicReference CAS signatures

Hans Boehm
On Saturday, January 10, 2015, Andrew Haley <[hidden email]> wrote:
On 10/01/15 00:20, Doug Lea wrote:
> On 01/09/2015 04:57 PM, thurstonn wrote:
>>
>> I was curious as to why there isn't an AtomicReference CAS:
>>
>> T cas(T expectedValue, T newValue)
>>
>> where the returned T represents the oldValue (if it == expectedValue, then
>> CAS succeeded).
>
> Because there is no equivalent on processors (including ARM, POWER)
> implementing compareAndSet using load-linked/store-conditional.
> (C++11/C11 make the same choice for the same reason.)

I don't understand what you mean.  Writing a CAS with this signature
would seem to me to be pretty straightforward on ARM.  I'm wondering
if you're referring to some specific atomicity or ordering guarantees,
but I'm not aware of any problem with that either.

Andrew.
 
I second Andrew's puzzlement.

The C++ versions do give you back the old value.  expectedValue in the C++ version is a reference, which is updated on failure.  That API doesn't work in Java, and I have mixed feelings about it anyway.  But it can be very convenient, and it's exactly what you want if you just want to replace x by f(x) atomically.  It becomes essentially

expected = x;
while (!x.CAS(expected, f(expected));

Unfortunately, it seems to be a bit of a bug magnet, and may have been too clever a design.

Hans

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

Re: AtomicReference CAS signatures

thurstonn
In reply to this post by Doug Lea
Thanks, as my original post feared.

Why couldn't the JVMs for LL/SC platforms do a compare-and-swap implementation:

load-locked

. . .
store-unlocked


instead?
Reply | Threaded
Open this post in threaded view
|

Re: AtomicReference CAS signatures

Doug Lea
In reply to this post by Hans Boehm
On 01/10/2015 10:08 AM, Hans Boehm wrote:
>     I don't understand what you mean.  Writing a CAS with this signature
>     would seem to me to be pretty straightforward on ARM.  I'm wondering
>     if you're referring to some specific atomicity or ordering guarantees,
>     but I'm not aware of any problem with that either.
>
> I second Andrew's puzzlement.

As Justin pointed out, the semantics are not quite identical ...

> In the case of LL/SC, I was briefly puzzled when I first read about it
> because it would seem at first glance that we have the old value right
> there in the result of the LL. But of course the SC can still fail even
> if the LL returns the value we're looking for, in which case we don't
> know what the now-current value actually is.


... although as it turned out in the JSR133 (and C11) memory models
(even JSR133 finalized after intrinisics introduction), it would have
been have been OK spec-wise. So I suppose it is worth re-contemplating.

> The C++ versions do give you back the old value.

Oops. I forgot that this was done while I wasn't paying attention.

-Doug

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

Re: AtomicReference CAS signatures

thurstonn
In reply to this post by Justin Sampson
So this conversation digressed in unexpected ways . . . .

Perhaps, because I wasn't clear.  
I'm not suggesting that the extant
boolean compareAndSet(T expected, T new) and
T getAndSet(T new)

be changed/removed anything.
If you don't want to use the new proposed:
T cas(T expected, T new)
then you can just ignore it.


But the proposed cas would be faster, simpler, and **has no equivalent** in the currrent AtomicReference (because it would be **atomic**).

And I really don't understand the "wouldn't be useful" comments - there are many circumstances (state machines, code/runtime visibility) in which it comes in handy (I was looking for it and was surprised it wasn't there while writing YACQ)

Take the simplest case:
you can use it as a sort of surrogate runtime assert;
you're at a place in your code where you expect some shared state to be X, but you're lost and want to make sure:

T problem;
if ((problem = ar.cas(X, Y)) != X)
    log.error("BUG - expected X but was $problem")

Is any other justification needed (and there are many more potential justifications)?

Now, if it's truly not possible/practical to implement cross-platform, that's one thing . . .


Reply | Threaded
Open this post in threaded view
|

Re: AtomicReference CAS signatures

Justin Sampson
In reply to this post by David Holmes-6
David Holmes wrote:

> Justin Sampson wrote:
>
> > In the case of LL/SC, I was briefly puzzled when I first read
> > about it because it would seem at first glance that we have the
> > old value right there in the result of the LL. But of course the
> > SC can still fail even if the LL returns the value we're looking
> > for, in which case we don't know what the now-current value
> > actually is.
>
> That would be true for a weak CAS, where we can fail spuriously,
> but for a strong CAS we only fail if the compare fails, not
> because the SC fails.

Hmm, does that mean our LL/SC implementation has to loop anyway?

boolean compareAndSet(T expected, T newval) {
  while (true) {
    T oldval = loadLinked(); // pseudo-code :)
    if (oldval != expected) return false;
    if (storeConditional(newval)) return true;
  }
}

In that case we _do_ have the actual old value, which can be
returned by either of those returns instead of the boolean!

> There's not much value in returning the value that was found in
> general, because as already stated it can be stale a moment after
> you've read it.

That's true for any volatile read, but volatile reads are still
pretty useful. :)

> > That said, a compareAndSwap implemented on top of the existing
> > compareAndSet would be easy enough, and very similar to the
> > other high-level methods available on the AtomicXYZ classes.
> > It's just a matter of justifying some use cases.
>
> I don't think that is true - if the value is not returned by the
> failing CAS then any attempt to return whatever the value is now,
> may not return the value that caused the CAS to fail - it could
> even return the expected value.

What about this?

T compareAndSwap(T expected, T newval) {
  while (true) {
    T oldval = get();
    if (oldval != expected || compareAndSet(oldval, newval)) {
      return oldval;
    }
  }
}

Cheers,
Justin

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

Re: AtomicReference CAS signatures

thurstonn
Justin Sampson wrote
Hmm, does that mean our LL/SC implementation has to loop anyway?

boolean compareAndSet(T expected, T newval) {
  while (true) {
    T oldval = loadLinked(); // pseudo-code :)
    if (oldval != expected) return false;
    if (storeConditional(newval)) return true;
  }
}

In that case we _do_ have the actual old value, which can be
returned by either of those returns instead of the boolean!
Yes, I would suspect this is how getAndSet(T) is implemented on LL/SC platforms:
while (i++ < SOME_CONSTANT)
   T tmp:= loadLinked()
   if (storeConditional(newVal)
      return tmp;

//promote to load-lock after SOME_CONSTANT failures:
T tmp = loadLocked()
storeUnlock(newVal)
return tmp

But it's important to emphasize this is **assembler** pseudocode



Reply | Threaded
Open this post in threaded view
|

Re: AtomicReference CAS signatures

thurstonn
In reply to this post by Andrew Haley
Right.
The C++11 version provides the same behavior as CMPXCHG, essentially providing 2 return values
Reply | Threaded
Open this post in threaded view
|

Re: AtomicReference CAS signatures

Vitaly Davidovich
In reply to this post by thurstonn

Using your assertion example, you're basically saying "at this point in the program I'm expecting the current value to be X and nobody else should he modifying it concurrently".  Well, in that case your error case is a slow/unlikely path, meaning can't you effectively write it as:

if (!ar.cas (x, y))
      log.error ("bug: " + ar.get ());

Yes, this isn't atomic.  But neither is your version because the actual current value may be different by the time you log it.  You *got* the current value atomically, but it doesn't mean that's what's in there by the time you do something with it.

But sure, if you want to have that as *another* version of cas and it's portably implementable and those semantics are adequate, I don't see an issue.  I just wouldn't want that to be the only variant, especially if it causes extra instructions to run or memory load to occur (e.g. checkcast that needs to load the object header to verify type) even if only reference comparison follows.

Sent from my phone

On Jan 10, 2015 11:11 AM, "thurstonn" <[hidden email]> wrote:
So this conversation digressed in unexpected ways . . . .

Perhaps, because I wasn't clear.
I'm not suggesting that the extant
boolean compareAndSet(T expected, T new) and
T getAndSet(T new)

be changed/removed anything.
If you don't want to use the new proposed:
T cas(T expected, T new)
then you can just ignore it.


But the proposed cas would be faster, simpler, and **has no equivalent** in
the currrent AtomicReference (because it would be **atomic**).

And I really don't understand the "wouldn't be useful" comments - there are
many circumstances (state machines, code/runtime visibility) in which it
comes in handy (I was looking for it and was surprised it wasn't there while
writing YACQ)

Take the simplest case:
you can use it as a sort of surrogate runtime assert;
you're at a place in your code where you expect some shared state to be X,
but you're lost and want to make sure:

T problem;
if ((problem = ar.cas(X, Y)) != X)
    log.error("BUG - expected X but was $problem")

Is any other justification needed (and there are many more potential
justifications)?

Now, if it's truly not possible/practical to implement cross-platform,
that's one thing . . .






--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/AtomicReference-CAS-signatures-tp11820p11835.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: AtomicReference CAS signatures

Vitaly Davidovich

I just double checked what .NET does (just to see another comparison besides c++) and they return the old value(reference).  They do, however, have sane (I.e. reified) generics so I don't think a checkcast is a concern there.

Sent from my phone

On Jan 10, 2015 3:01 PM, "Vitaly Davidovich" <[hidden email]> wrote:

Using your assertion example, you're basically saying "at this point in the program I'm expecting the current value to be X and nobody else should he modifying it concurrently".  Well, in that case your error case is a slow/unlikely path, meaning can't you effectively write it as:

if (!ar.cas (x, y))
      log.error ("bug: " + ar.get ());

Yes, this isn't atomic.  But neither is your version because the actual current value may be different by the time you log it.  You *got* the current value atomically, but it doesn't mean that's what's in there by the time you do something with it.

But sure, if you want to have that as *another* version of cas and it's portably implementable and those semantics are adequate, I don't see an issue.  I just wouldn't want that to be the only variant, especially if it causes extra instructions to run or memory load to occur (e.g. checkcast that needs to load the object header to verify type) even if only reference comparison follows.

Sent from my phone

On Jan 10, 2015 11:11 AM, "thurstonn" <[hidden email]> wrote:
So this conversation digressed in unexpected ways . . . .

Perhaps, because I wasn't clear.
I'm not suggesting that the extant
boolean compareAndSet(T expected, T new) and
T getAndSet(T new)

be changed/removed anything.
If you don't want to use the new proposed:
T cas(T expected, T new)
then you can just ignore it.


But the proposed cas would be faster, simpler, and **has no equivalent** in
the currrent AtomicReference (because it would be **atomic**).

And I really don't understand the "wouldn't be useful" comments - there are
many circumstances (state machines, code/runtime visibility) in which it
comes in handy (I was looking for it and was surprised it wasn't there while
writing YACQ)

Take the simplest case:
you can use it as a sort of surrogate runtime assert;
you're at a place in your code where you expect some shared state to be X,
but you're lost and want to make sure:

T problem;
if ((problem = ar.cas(X, Y)) != X)
    log.error("BUG - expected X but was $problem")

Is any other justification needed (and there are many more potential
justifications)?

Now, if it's truly not possible/practical to implement cross-platform,
that's one thing . . .






--
View this message in context: http://jsr166-concurrency.10961.n7.nabble.com/AtomicReference-CAS-signatures-tp11820p11835.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: AtomicReference CAS signatures

David Holmes-6
In reply to this post by Justin Sampson
Justin Sampson writes:

> David Holmes wrote:
>
> > Justin Sampson wrote:
> >
> > > In the case of LL/SC, I was briefly puzzled when I first read
> > > about it because it would seem at first glance that we have the
> > > old value right there in the result of the LL. But of course the
> > > SC can still fail even if the LL returns the value we're looking
> > > for, in which case we don't know what the now-current value
> > > actually is.
> >
> > That would be true for a weak CAS, where we can fail spuriously,
> > but for a strong CAS we only fail if the compare fails, not
> > because the SC fails.
>
> Hmm, does that mean our LL/SC implementation has to loop anyway?
>
> boolean compareAndSet(T expected, T newval) {
>   while (true) {
>     T oldval = loadLinked(); // pseudo-code :)
>     if (oldval != expected) return false;
>     if (storeConditional(newval)) return true;
>   }
> }
>
> In that case we _do_ have the actual old value, which can be
> returned by either of those returns instead of the boolean!

Yes it has to loop; and yes it has access to the value that caused failure.

> > There's not much value in returning the value that was found in
> > general, because as already stated it can be stale a moment after
> > you've read it.
>
> That's true for any volatile read, but volatile reads are still
> pretty useful. :)

Different usage contexts.

> > > That said, a compareAndSwap implemented on top of the existing
> > > compareAndSet would be easy enough, and very similar to the
> > > other high-level methods available on the AtomicXYZ classes.
> > > It's just a matter of justifying some use cases.
> >
> > I don't think that is true - if the value is not returned by the
> > failing CAS then any attempt to return whatever the value is now,
> > may not return the value that caused the CAS to fail - it could
> > even return the expected value.
>
> What about this?
>
> T compareAndSwap(T expected, T newval) {
>   while (true) {
>     T oldval = get();
>     if (oldval != expected || compareAndSet(oldval, newval)) {
>       return oldval;
>     }
>   }
> }

Ok but all this says is that it either returns the expected value on success
or some past value that was once stored, on failure. Doesn't seem to give me
much over a boolean. Typically compareAndSwap code just looks like:

if (cas(expected, newval) == expected) ...

which is more typing than needed if cas just returns boolean. :)

Though it wasn't the original reason for using boolean, weakCompareAndSet
requires it. It would have looked odd to have:

T compareAndSet(T expected, T newVal)
boolean weakCompareAndSet(T expected, T newVal)

I don't see a strong enough justification for adding a T returning CAS
method but won't fight it either.

Cheers,
David

> Cheers,
> Justin
>
> _______________________________________________
> 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
12345