[concurrency-interest] Fwd: Should I avoid compareAndSet with value-based classes?

Gil Tene gil at azul.com
Fri Jul 7 13:42:26 EDT 2017

> On Jul 7, 2017, at 10:29 AM, Andrew Haley <aph at redhat.com> wrote:
> On 07/07/17 18:02, Gil Tene wrote:
>> And in some cases locking (on something other than the reference to
>> the value-based class) will be perfectly sufficient. But in cases
>> where people seek lock-free or wait-free operations, locking may not
>> be an acceptable thing. E.g. Clocks and Instants are starting to
>> show up in the low latency paths of FinServ software due to MiFID 2
>> requirements, and that sort of code often tries to avoid locking
>> like the plague…
> Mmm, but then the question is whether something like this is more
> performant than locking:
>>    public void add(final Duration amountToAdd) {
>>        boolean success;
>>        do {
>>            BoxedInstant currentBoxedInstance = instantHolder.get();
>>            BoxedInstant newBoxedInstant =
>>                    new BoxedInstant(currentBoxedInstance.getInstant().plus(amountToAdd));
>>            success = instantHolder.compareAndSet(currentBoxedInstance, newBoxedInstant);
>>        } while (!success);
>>    }
> I'd argue that it's may well not be, because in the mostly-uncontended
> case acquiring a lock will sail straight through with no significant
> delay -- certainly no more than the compareAndSet we see here.  In the
> highly-contended case, locking may be preferable to the inter-thread
> thrashing this CAS loop might cause.  At least one of the threads
> would acquire the lock, and at least one of them would make some
> forward progress.  And the readers would be simpler and faster.  The
> lock used could be a fast spinlock: it doesn't have to block at the OS
> level.  But to know for sure you'd have to measure some real
> application behaviour.
> And the meta-question is whether people who avoid locking like the
> plague, even when it really is the simplest and cleanest thing to do,
> are doing it for good reasons or following a cargo cult.

The cult thing certainly does exist, but lock-freedom and wait-freedom as design criteria have real and good reasons too.

One typical and very real case is when a critical path thread cannot afford to block waiting for a non-critical path thread, even in a very rare case. Imagine that the two threads share a clock for some reason, and both participate in updating it (again, for some reason). The critical path thread will prefer taking the [potentially] extra hit of the code above in order to never be blocked behind the background thread owning a lock it is waiting on (which may have been context switched out for 10s of msec for one of many possible reasons).

This is a very common pattern in low latency systems where the critical path thread does operations that take low 10s of usec, but a background thread is doing some other thing [like updating a journal]. Such systems will often do much to protect the critical path thread from interference (e.g. pin it to a dedicated core, etc.).

> -- 
> Andrew Haley
> Java Platform Lead Engineer
> Red Hat UK Ltd. <https://www.redhat.com>
> EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671

More information about the Concurrency-interest mailing list