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<WriterReaderPhaser> startEpochUpdater =
            AtomicLongFieldUpdater.newUpdater(WriterReaderPhaser.class, "startEpoch");
    private static final AtomicLongFieldUpdater<WriterReaderPhaser> evenEndEpochUpdater =
            AtomicLongFieldUpdater.newUpdater(WriterReaderPhaser.class, "evenEndEpoch");
    private static final AtomicLongFieldUpdater<WriterReaderPhaser> oddEndEpochUpdater =
            AtomicLongFieldUpdater.newUpdater(WriterReaderPhaser.class, "oddEndEpoch");

    /**
     * Indicate entry to a critical section containing a write operation.
     * <p>
     * This call is wait-free on architectures that support wait free atomic increment operations,
     * and is lock-free on architectures that do not.
     * <p>
     * {@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.
     * <p>
     * This call is wait-free on architectures that support wait free atomic increment operations,
     * and is lock-free on architectures that do not.
     * <p>
     * {@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).
     * <p>
     * {@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.
     * <p>
     * No actual writer critical section activity is required for {@link WriterReaderPhaser#flipPhase()} to
     * succeed.
     * <p>
     * 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) {<FILL_FUNCTION_BODY>}

    /**
     * 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.
     * <p>
     * No actual writer critical section activity is required for {@code flipPhase()} to
     * succeed.
     * <p>
     * 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);
    }
}

        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.
                }
            }
        }
    
<no_super_class>