All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.couchbase.client.core.deps.org.HdrHistogram.WriterReaderPhaser Maven / Gradle / Ivy

There is a newer version: 3.7.2
Show newest version
/**
 * Written by Gil Tene of Azul Systems, and released to the public domain,
 * as explained at http://creativecommons.org/publicdomain/zero/1.0/
 */

package com.couchbase.client.core.deps.org.HdrHistogram;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import java.util.concurrent.locks.ReentrantLock;

/**
 * {@link 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 {@link 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 {@link 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. * {@link 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 * {@link 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. *
  3. Writing is done to the perceived active version (as perceived by the * writer), and only within critical sections delineated by * {@link WriterReaderPhaser#writerCriticalSectionEnter} and * {@link WriterReaderPhaser#writerCriticalSectionExit writerCriticalSectionExit()}. *
  4. *
  5. Only readers switch the perceived roles of the active and inactive data * structures. They do so only while under {@link WriterReaderPhaser#readerLock()} * protection and only before calling {@link WriterReaderPhaser#flipPhase()}.
  6. *
  7. Writers do not remain in their critical sections indefinitely.
  8. *
  9. Only writers perform {@link WriterReaderPhaser#writerCriticalSectionEnter} * and * {@link WriterReaderPhaser#writerCriticalSectionExit writerCriticalSectionExit()}. *
  10. *
  11. Readers do not hold onto readerLock indefinitely.
  12. *
  13. Only readers perform {@link WriterReaderPhaser#readerLock()} and * {@link WriterReaderPhaser#readerUnlock()}.
  14. *
  15. Only readers perform {@link WriterReaderPhaser#flipPhase()} operations, * and only while holding the readerLock.
  16. *
*

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

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

    *
  1. Writers operations * ({@link WriterReaderPhaser#writerCriticalSectionEnter writerCriticalSectionEnter} * and {@link WriterReaderPhaser#writerCriticalSectionExit 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. *
  3. {@link 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.
  4. *
  5. {@link WriterReaderPhaser#readerLock()} only blocks for other * readers that are holding the readerLock.
  6. *
* *

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();
 *         }
 * 
*/ /* * High level design: There are even and odd epochs; the epoch flips for each * reader. Any number of writers can be in the same epoch (odd or even), but * after a completed phase flip no writers will be still in the old epoch * (and therefor are known to not be updating or observing the old, inactive * data structure). Writers can always proceed at full speed in what they * percieve to be the current (odd or even) epoch. The epoch flip is fast (a * single atomic op). */ public class WriterReaderPhaser { private volatile long startEpoch = 0; private volatile long evenEndEpoch = 0; private volatile long oddEndEpoch = Long.MIN_VALUE; private final ReentrantLock readerLock = new ReentrantLock(); private static final AtomicLongFieldUpdater startEpochUpdater = AtomicLongFieldUpdater.newUpdater(WriterReaderPhaser.class, "startEpoch"); private static final AtomicLongFieldUpdater evenEndEpochUpdater = AtomicLongFieldUpdater.newUpdater(WriterReaderPhaser.class, "evenEndEpoch"); private static final AtomicLongFieldUpdater oddEndEpochUpdater = AtomicLongFieldUpdater.newUpdater(WriterReaderPhaser.class, "oddEndEpoch"); /** * 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. *

* {@code writerCriticalSectionEnter()} must be matched with a subsequent * {@link WriterReaderPhaser#writerCriticalSectionExit(long)} in order for CriticalSectionPhaser * synchronization to function properly. * * @return an (opaque) value associated with the critical section entry, which MUST be provided * to the matching {@link WriterReaderPhaser#writerCriticalSectionExit} call. */ public long writerCriticalSectionEnter() { return startEpochUpdater.getAndIncrement(this); } /** * 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. *

* {@code writerCriticalSectionExit(long)} must be matched with a preceding * {@link WriterReaderPhaser#writerCriticalSectionEnter()} call, and must be provided with the * matching {@link WriterReaderPhaser#writerCriticalSectionEnter()} call's return value, in * order for CriticalSectionPhaser synchronization to function properly. * * @param criticalValueAtEnter the (opaque) value returned from the matching * {@link WriterReaderPhaser#writerCriticalSectionEnter()} call. */ public void writerCriticalSectionExit(long criticalValueAtEnter) { (criticalValueAtEnter < 0 ? oddEndEpochUpdater : evenEndEpochUpdater).getAndIncrement(this); } /** * Enter to a critical section containing a read operation (reentrant, mutually excludes against * {@link WriterReaderPhaser#readerLock} calls by other threads). *

* {@link WriterReaderPhaser#readerLock} DOES NOT provide synchronization * against {@link WriterReaderPhaser#writerCriticalSectionEnter()} calls. Use {@link WriterReaderPhaser#flipPhase()} * to synchronize reads against writers. */ public void readerLock() { readerLock.lock(); } /** * Exit from a critical section containing a read operation (relinquishes mutual exclusion against other * {@link WriterReaderPhaser#readerLock} calls). */ public void readerUnlock() { readerLock.unlock(); } /** * Flip a phase in the {@link WriterReaderPhaser} instance, {@link WriterReaderPhaser#flipPhase()} * can only be called while holding the {@link WriterReaderPhaser#readerLock() readerLock}. * {@link WriterReaderPhaser#flipPhase()} will return only after all writer critical sections (protected by * {@link WriterReaderPhaser#writerCriticalSectionEnter() writerCriticalSectionEnter} and * {@link WriterReaderPhaser#writerCriticalSectionExit writerCriticalSectionEnter}) that may have been * in flight when the {@link WriterReaderPhaser#flipPhase()} call were made had completed. *

* No actual writer critical section activity is required for {@link WriterReaderPhaser#flipPhase()} to * succeed. *

* However, {@link WriterReaderPhaser#flipPhase()} is lock-free with respect to calls to * {@link WriterReaderPhaser#writerCriticalSectionEnter()} and * {@link WriterReaderPhaser#writerCriticalSectionExit writerCriticalSectionExit()}. It may spin-wait * or for active writer critical section code to complete. * * @param yieldTimeNsec The amount of time (in nanoseconds) to sleep in each yield if yield loop is needed. */ public void flipPhase(long yieldTimeNsec) { if (!readerLock.isHeldByCurrentThread()) { throw new IllegalStateException("flipPhase() can only be called while holding the readerLock()"); } // Read the volatile 'startEpoch' exactly once boolean nextPhaseIsEven = (startEpoch < 0); // Current phase is odd... // First, clear currently unused [next] phase end epoch (to proper initial value for phase): long initialStartValue = nextPhaseIsEven ? 0 : Long.MIN_VALUE; (nextPhaseIsEven ? evenEndEpochUpdater : oddEndEpochUpdater).lazySet(this, initialStartValue); // Next, reset start value, indicating new phase, and retain value at flip: long startValueAtFlip = startEpochUpdater.getAndSet(this, initialStartValue); // Now, spin until previous phase end value catches up with start value at flip: while((nextPhaseIsEven ? oddEndEpoch : evenEndEpoch) != startValueAtFlip) { if (yieldTimeNsec == 0) { Thread.yield(); } else { try { TimeUnit.NANOSECONDS.sleep(yieldTimeNsec); } catch (InterruptedException ex) { // nothing to do here, we just woke up earlier that expected. } } } } /** * Flip a phase in the {@link WriterReaderPhaser} instance, {@code flipPhase()} * can only be called while holding the {@link WriterReaderPhaser#readerLock() readerLock}. * {@code flipPhase()} will return only after all writer critical sections (protected by * {@link WriterReaderPhaser#writerCriticalSectionEnter() writerCriticalSectionEnter} and * {@link WriterReaderPhaser#writerCriticalSectionExit writerCriticalSectionEnter}) that may have been * in flight when the {@code flipPhase()} call were made had completed. *

* No actual writer critical section activity is required for {@code flipPhase()} to * succeed. *

* However, {@code flipPhase()} is lock-free with respect to calls to * {@link WriterReaderPhaser#writerCriticalSectionEnter()} and * {@link WriterReaderPhaser#writerCriticalSectionExit writerCriticalSectionExit()}. It may spin-wait * or for active writer critical section code to complete. */ public void flipPhase() { flipPhase(0); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy