Top Description Fields Constructors Methods
org.HdrHistogram

public Class WriterReaderPhaser

extends Object
Class Inheritance
Imports
java.util.concurrent.TimeUnit, java.util.concurrent.atomic.AtomicLongFieldUpdater, java.util.concurrent.locks.ReentrantLock

WriterReaderPhaser provides an asymmetric means for synchronizing the execution of wait-free "writer" critical sections against a "reader phase flip" that needs to make sure no writer critical sections that were active at the beginning of the flip are still active after the flip is done. Multiple writers and multiple readers are supported.

Using a WriterReaderPhaser for coordination, writers can continously perform wait-free/lock-free updates to common data structures, while readers can get hold of atomic and inactive snapshots without stalling writers.

While a WriterReaderPhaser can be useful in multiple scenarios, a specific and common use case is that of safely managing "double buffered" data stream access in which writers can proceed without being blocked, while readers gain access to stable and unchanging buffer samples. WriterReaderPhaser "writers" are wait free (on architectures that support wait free atomic increment operations), "readers" block for other "readers", and "readers" are only blocked by "writers" whose critical section was entered before the reader's WriterReaderPhaser#flipPhase() attempt.

Assumptions and Guarantees

When used to protect an actively recording data structure, the assumptions on how readers and writers act are:

  1. There are two sets of data structures ("active" and "inactive")
  2. Writing is done to the perceived active version (as perceived by the writer), and only within critical sections delineated by WriterReaderPhaser#writerCriticalSectionEnter and writerCriticalSectionExit().
  3. Only readers switch the perceived roles of the active and inactive data structures. They do so only while under WriterReaderPhaser#readerLock() protection and only before calling WriterReaderPhaser#flipPhase().
  4. Writers do not remain in their critical sections indefinitely.
  5. Only writers perform WriterReaderPhaser#writerCriticalSectionEnter and writerCriticalSectionExit().
  6. Readers do not hold onto readerLock indefinitely.
  7. Only readers perform WriterReaderPhaser#readerLock() and WriterReaderPhaser#readerUnlock().
  8. Only readers perform WriterReaderPhaser#flipPhase() operations, and only while holding the readerLock.

When the above assumptions are met, WriterReaderPhaser guarantees that the inactive data structures are not being modified by any writers while being read while under readerLock() protection after a WriterReaderPhaser#flipPhase()() operation.

The following progress guarantees are provided to writers and readers that adhere to the above stated assumptions:

  1. Writers operations (writerCriticalSectionEnter and writerCriticalSectionExit) are wait free on architectures that support wait-free atomic increment operations (they remain lock-free [but not wait-free] on architectures that do not support wait-free atomic increment operations)
  2. WriterReaderPhaser#flipPhase() operations are guaranteed to make forward progress, and will only be blocked by writers whose critical sections were entered prior to the start of the reader's flipPhase operation, and have not yet exited their critical sections.
  3. WriterReaderPhaser#readerLock() only blocks for other readers that are holding the readerLock.

Example use

Imagine a simple use case where a histogram (which is basically a large set of rapidly updated counters) is being modified by writers, and a reader needs to gain access to stable interval samples of the histogram for reporting or other analysis purposes.

        final WriterReaderPhaser recordingPhaser = new WriterReaderPhaser();

        volatile Histogram activeHistogram;
        Histogram inactiveHistogram;
        ...
A writer may record values the histogram:

        // Wait-free recording:
        long criticalValueAtEnter = recordingPhaser.writerCriticalSectionEnter();
        try {
            activeHistogram.recordValue(value);
        } finally {
            recordingPhaser.writerCriticalSectionExit(criticalValueAtEnter);
        }
A reader gains access to a stable histogram of values recorded during an interval, and reports on it:

        try {
            recordingPhaser.readerLock();

            inactiveHistogram.reset();

            // Swap active and inactive histograms:
            final Histogram tempHistogram = inactiveHistogram;
            inactiveHistogram = activeHistogram;
            activeHistogram = tempHistogram;

            recordingPhaser.flipPhase();
            // At this point, inactiveHistogram content is guaranteed to be stable

            logHistogram(inactiveHistogram);

        } finally {
            recordingPhaser.readerUnlock();
        }

Field Summary

Modifier and TypeField and Description
private volatile long
private static final AtomicLongFieldUpdater<WriterReaderPhaser>
private volatile long
private static final AtomicLongFieldUpdater<WriterReaderPhaser>
private final ReentrantLock
private volatile long
private static final AtomicLongFieldUpdater<WriterReaderPhaser>

Constructor Summary

AccessConstructor and Description
public

Method Summary

Modifier and TypeMethod and Description
public void
flipPhase(long
The amount of time (in nanoseconds) to sleep in each yield if yield loop is needed.
yieldTimeNsec
)

Flip a phase in the WriterReaderPhaser instance, WriterReaderPhaser#flipPhase() can only be called while holding the readerLock.

public void
flipPhase()

Flip a phase in the WriterReaderPhaser instance, flipPhase() can only be called while holding the readerLock.

public void
readerLock()

Enter to a critical section containing a read operation (reentrant, mutually excludes against WriterReaderPhaser#readerLock calls by other threads).

public void
readerUnlock()

Exit from a critical section containing a read operation (relinquishes mutual exclusion against other WriterReaderPhaser#readerLock calls).

public long

Returns:

an (opaque) value associated with the critical section entry, which MUST be provided to the matching WriterReaderPhaser#writerCriticalSectionExit call.
writerCriticalSectionEnter
()

Indicate entry to a critical section containing a write operation.

public void
writerCriticalSectionExit(long
the (opaque) value returned from the matching WriterReaderPhaser#writerCriticalSectionEnter() call.
criticalValueAtEnter
)

Indicate exit from a critical section containing a write operation.

Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait

Field Detail

evenEndEpochback to summary
private volatile long evenEndEpoch
evenEndEpochUpdaterback to summary
private static final AtomicLongFieldUpdater<WriterReaderPhaser> evenEndEpochUpdater
oddEndEpochback to summary
private volatile long oddEndEpoch
oddEndEpochUpdaterback to summary
private static final AtomicLongFieldUpdater<WriterReaderPhaser> oddEndEpochUpdater
readerLockback to summary
private final ReentrantLock readerLock
startEpochback to summary
private volatile long startEpoch
startEpochUpdaterback to summary
private static final AtomicLongFieldUpdater<WriterReaderPhaser> startEpochUpdater

Constructor Detail

WriterReaderPhaserback to summary
public WriterReaderPhaser()

Method Detail

flipPhaseback to summary
public void flipPhase(long yieldTimeNsec)

Flip a phase in the WriterReaderPhaser instance, WriterReaderPhaser#flipPhase() can only be called while holding the readerLock. WriterReaderPhaser#flipPhase() will return only after all writer critical sections (protected by writerCriticalSectionEnter and writerCriticalSectionEnter) that may have been in flight when the WriterReaderPhaser#flipPhase() call were made had completed.

No actual writer critical section activity is required for WriterReaderPhaser#flipPhase() to succeed.

However, WriterReaderPhaser#flipPhase() is lock-free with respect to calls to WriterReaderPhaser#writerCriticalSectionEnter() and writerCriticalSectionExit(). It may spin-wait or for active writer critical section code to complete.

Parameters
yieldTimeNsec:long

The amount of time (in nanoseconds) to sleep in each yield if yield loop is needed.

flipPhaseback to summary
public void flipPhase()

Flip a phase in the WriterReaderPhaser instance, flipPhase() can only be called while holding the readerLock. flipPhase() will return only after all writer critical sections (protected by writerCriticalSectionEnter and writerCriticalSectionEnter) that may have been in flight when the flipPhase() call were made had completed.

No actual writer critical section activity is required for flipPhase() to succeed.

However, flipPhase() is lock-free with respect to calls to WriterReaderPhaser#writerCriticalSectionEnter() and writerCriticalSectionExit(). It may spin-wait or for active writer critical section code to complete.

readerLockback to summary
public void readerLock()

Enter to a critical section containing a read operation (reentrant, mutually excludes against WriterReaderPhaser#readerLock calls by other threads).

WriterReaderPhaser#readerLock DOES NOT provide synchronization against WriterReaderPhaser#writerCriticalSectionEnter() calls. Use WriterReaderPhaser#flipPhase() to synchronize reads against writers.

readerUnlockback to summary
public void readerUnlock()

Exit from a critical section containing a read operation (relinquishes mutual exclusion against other WriterReaderPhaser#readerLock calls).

writerCriticalSectionEnterback to summary
public long writerCriticalSectionEnter()

Indicate entry to a critical section containing a write operation.

This call is wait-free on architectures that support wait free atomic increment operations, and is lock-free on architectures that do not.

writerCriticalSectionEnter() must be matched with a subsequent WriterReaderPhaser#writerCriticalSectionExit(long) in order for CriticalSectionPhaser synchronization to function properly.

Returns:long

an (opaque) value associated with the critical section entry, which MUST be provided to the matching WriterReaderPhaser#writerCriticalSectionExit call.

writerCriticalSectionExitback to summary
public void writerCriticalSectionExit(long criticalValueAtEnter)

Indicate exit from a critical section containing a write operation.

This call is wait-free on architectures that support wait free atomic increment operations, and is lock-free on architectures that do not.

writerCriticalSectionExit(long) must be matched with a preceding WriterReaderPhaser#writerCriticalSectionEnter() call, and must be provided with the matching WriterReaderPhaser#writerCriticalSectionEnter() call's return value, in order for CriticalSectionPhaser synchronization to function properly.

Parameters
criticalValueAtEnter:long

the (opaque) value returned from the matching WriterReaderPhaser#writerCriticalSectionEnter() call.