Atomic non-null then null

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

Atomic non-null then null

JSR166 Concurrency mailing list
Hello.

Could someone confirm (or invalidate :) that the following class works?

The "tricky part" is that get() is supposed to be callable concurrently.
If it always gets called by a same thread, it should obviously work
since only this thread would deal with the non-volatile reference.



/**
 * When wanting to retrieve an object from an AtomicReference
 * that is initially non-null and then is only set once and with null,
 * this class allows to get rid of the volatile read overhead
 * after the nullification has been detected.
 */
public class AtomicNonNullThenNull<T> {

    private final AtomicReference<T> aRef;

    private Object ref = this;

    /**
     * @param aRef The initially non-null reference must be set into it
     *        early enough for first call to get() method to see it.
     */
    public AtomicNonNullThenNull(AtomicReference<T> aRef) {
        this.aRef = aRef;
    }

    /**
     * Must only be called from threads to which the initial state
     * of this instance has properly been made visible.
     *
     * @return The reference retrieved from the AtomicReference specified
     *         to the constructor, until a call to this method detects that
     *         it became null, after which null is always returned,
     *         from a non-volatile read.
     */
    public T get() {
        final Object ref = this.ref;
        if (ref == null) {
            return null;
        } else {
            final T vRef = this.aRef.get();
            if (vRef == null) {
                // Possibly done by multiple threads.
                this.ref = null;
                return null;
            } else {
                return vRef;
            }
        }
    }
}



NB1: I use it in a method that gets called a lot,
to do something once if it ever gets called,
as follows:
    final Runnable runnable = anntn.get();
    if ((runnable != null) && aRef.compareAndSet(runnable, null)) {
        runnable.run();
    }
I just read the discussion about "Unsynchronized lazy condition" from June,
and the "Effective Java" class load trick, which seems to solve a similar
problem.
The cons that I see for the class load trick are that you need one class per use case,
that it is always "slow" when it triggers (class load),
and that it can only "fire" once (with ANNTN you could do aRef.set(null) only after a few runs),
and the pro that in the long run the overhead is smaller due to the method being empty
(unless the JIT can see that once the value is null it can never get back to non-null,
and optimize the code away).



NB2: There is a dual "AtomicNullThenNonNull" using a similar idea,
but it only works with objects with final fields or such,
since it can make them visible without proper memory barriers.



-Jeff


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

Re: Atomic non-null then null

JSR166 Concurrency mailing list
Hi Jeff,

Why the complication? Just return the value of `aRef` all the time. All those extra code there with the additional read of `ref` and the conditional checks look redundant as you'd probably have to null check the result of your `get()` method anyway. Are you targeting a platform with an expensive volatile read operation?

Jeff Hain via Concurrency-interest <[hidden email]> ezt írta (időpont: 2018. aug. 8., Sze, 23:23):
Hello.

Could someone confirm (or invalidate :) that the following class works?

The "tricky part" is that get() is supposed to be callable concurrently.
If it always gets called by a same thread, it should obviously work
since only this thread would deal with the non-volatile reference.



/**
 * When wanting to retrieve an object from an AtomicReference
 * that is initially non-null and then is only set once and with null,
 * this class allows to get rid of the volatile read overhead
 * after the nullification has been detected.
 */
public class AtomicNonNullThenNull<T> {

    private final AtomicReference<T> aRef;

    private Object ref = this;

    /**
     * @param aRef The initially non-null reference must be set into it
     *        early enough for first call to get() method to see it.
     */
    public AtomicNonNullThenNull(AtomicReference<T> aRef) {
        this.aRef = aRef;
    }

    /**
     * Must only be called from threads to which the initial state
     * of this instance has properly been made visible.
     *
     * @return The reference retrieved from the AtomicReference specified
     *         to the constructor, until a call to this method detects that
     *         it became null, after which null is always returned,
     *         from a non-volatile read.
     */
    public T get() {
        final Object ref = this.ref;
        if (ref == null) {
            return null;
        } else {
            final T vRef = this.aRef.get();
            if (vRef == null) {
                // Possibly done by multiple threads.
                this.ref = null;
                return null;
            } else {
                return vRef;
            }
        }
    }
}



NB1: I use it in a method that gets called a lot,
to do something once if it ever gets called,
as follows:
    final Runnable runnable = anntn.get();
    if ((runnable != null) && aRef.compareAndSet(runnable, null)) {
        runnable.run();
    }
I just read the discussion about "Unsynchronized lazy condition" from June,
and the "Effective Java" class load trick, which seems to solve a similar
problem.
The cons that I see for the class load trick are that you need one class per use case,
that it is always "slow" when it triggers (class load),
and that it can only "fire" once (with ANNTN you could do aRef.set(null) only after a few runs),
and the pro that in the long run the overhead is smaller due to the method being empty
(unless the JIT can see that once the value is null it can never get back to non-null,
and optimize the code away).



NB2: There is a dual "AtomicNullThenNonNull" using a similar idea,
but it only works with objects with final fields or such,
since it can make them visible without proper memory barriers.



-Jeff

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


--
Best regards,
David Karnok

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

Re: Atomic non-null then null

JSR166 Concurrency mailing list
>Why the complication? Just return the value of `aRef` all the time.
>All those extra code there with the additional read of `ref` and the conditional checks
>look redundant as you'd probably have to null check the result of your `get()` method anyway.
>Are you targeting a platform with an expensive volatile read operation?

Hi Dávid,

I initially made ANNTN for this logging class (where it's called NullElseAtomicReference):
https://github.com/jeffhain/jodk/blob/master/src/net/jodk/threading/HeisenLogger.java

The idea of HeisenLogger is to be able to do logs with minimal memory barriers,
typically just a lazySet, to help debug concurrency issues (heisenbugs)
without making them go away with heavy memory barriers (and slow synchronous
logging code).
It served me well while developing variations around the Disruptor pattern.

I know that volatile reads can be quite fast (when uncontended), but I wanted to make sure
the code is always fast, in particular by avoiding the risk of false sharing without using
heavy and eventually unsufficient padding, and did not cause an avoidable memory barrier.

The complexity being mostly factored in a single class, it shouldn't hurt.
Could also increase the encapsulation by making the AtomicReference internal to it,
and have public methods to deal with it.

-Jeff


Jeff Hain via Concurrency-interest <[hidden email]> ezt írta (időpont: 2018. aug. 8., Sze, 23:23):
Hello.

Could someone confirm (or invalidate :) that the following class works?

The "tricky part" is that get() is supposed to be callable concurrently.
If it always gets called by a same thread, it should obviously work
since only this thread would deal with the non-volatile reference.



/**
 * When wanting to retrieve an object from an AtomicReference
 * that is initially non-null and then is only set once and with null,
 * this class allows to get rid of the volatile read overhead
 * after the nullification has been detected.
 */
public class AtomicNonNullThenNull<T> {

    private final AtomicReference<T> aRef;

    private Object ref = this;

    /**
     * @param aRef The initially non-null reference must be set into it
     *        early enough for first call to get() method to see it.
     */
    public AtomicNonNullThenNull(AtomicReference<T> aRef) {
        this.aRef = aRef;
    }

    /**
     * Must only be called from threads to which the initial state
     * of this instance has properly been made visible.
     *
     * @return The reference retrieved from the AtomicReference specified
     *         to the constructor, until a call to this method detects that
     *         it became null, after which null is always returned,
     *         from a non-volatile read.
     */
    public T get() {
        final Object ref = this.ref;
        if (ref == null) {
            return null;
        } else {
            final T vRef = this.aRef.get();
            if (vRef == null) {
                // Possibly done by multiple threads.
                this.ref = null;
                return null;
            } else {
                return vRef;
            }
        }
    }
}



NB1: I use it in a method that gets called a lot,
to do something once if it ever gets called,
as follows:
    final Runnable runnable = anntn.get();
    if ((runnable != null) && aRef.compareAndSet(runnable, null)) {
        runnable.run();
    }
I just read the discussion about "Unsynchronized lazy condition" from June,
and the "Effective Java" class load trick, which seems to solve a similar
problem.
The cons that I see for the class load trick are that you need one class per use case,
that it is always "slow" when it triggers (class load),
and that it can only "fire" once (with ANNTN you could do aRef.set(null) only after a few runs),
and the pro that in the long run the overhead is smaller due to the method being empty
(unless the JIT can see that once the value is null it can never get back to non-null,
and optimize the code away).



NB2: There is a dual "AtomicNullThenNonNull" using a similar idea,
but it only works with objects with final fields or such,
since it can make them visible without proper memory barriers.



-Jeff

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


--
Best regards,
David Karnok

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