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

com.oracle.coherence.common.base.ConcurrentNotifier Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */

package com.oracle.coherence.common.base;


import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.locks.LockSupport;


/**
 * A Condition-like object, usable by multiple threads to both wait and signal.
 * 

* Note that no synchronization is needed to use this class; i.e. clients * must not synchronize on this class prior to calling await() or * signal(), nor should the use any of the primitive wait() * or notify() methods. *

* Unlike the {@link SingleWaiterMultiNotifier}, this notifier implementation requires * the notion of a {@link #isReady ready} check. When the notifier is ready then a call * to await because a no-op. An example ready check for a notifier based queue removal * would be !queue.isEmpty(); * * @author mf 2018.04.13 */ public abstract class ConcurrentNotifier implements Notifier { @Override public void await(long cMillis) throws InterruptedException { Thread threadThis = Thread.currentThread(); Link linkThis = null; long lBitThis = -1L; // will be reset when we make our link while (!isReady()) { Object oHead = m_oWaitHead; Object oNew; if (oHead == null) { // try to be the initial waiting thread; avoid creating a Link oNew = threadThis; } else if (linkThis == null) { // other threads are waiting; create a Link for myself lBitThis = 1L << (threadThis.hashCode() % 61); oNew = linkThis = makeSelfLink(threadThis, lBitThis, oHead); // if linkThis is non-null linkThis.next has also been set to oHead // if null we are already in the stack and can avoid cas'ing and just park (see below) } else { // other threads are waiting; we've already created a Link for ourselves; just assign .next/lFilterThreads // no-need to recheck if we're in the stack Link linkNext = linkThis.next = oHead instanceof Link ? (Link) oHead : new Link((Thread) oHead); linkThis.lFilterThreads = lBitThis | linkNext.lFilterThreads; oNew = linkThis; } if (oNew == null || s_fuHead.compareAndSet(this, oHead, oNew)) { park(cMillis); return; } // else; retry } } @Override public void signal() { Object oWaitHead = m_oWaitHead; if (oWaitHead == null) { // nobody waiting; nothing to do } else if (oWaitHead instanceof Thread && s_fuHead.compareAndSet(this, oWaitHead, null)) { // common case, just one thread waiting, and we win the CAS on first attempt LockSupport.unpark((Thread) oWaitHead); } else { // take the slow path signalInternal(); } } // ----- helpers -------------------------------------------------------- /** * Full version of signal. */ protected void signalInternal() { for (Object oWaitHead = m_oWaitHead; oWaitHead != null; oWaitHead = m_oWaitHead) { if (s_fuHead.compareAndSet(this, oWaitHead, null)) { // we've signaled with waiting thread(s) if (oWaitHead instanceof Link) { for (Link link = (Link) oWaitHead; link != null; ) { LockSupport.unpark(link.thread); Link linkLast = link; link = link.next; linkLast.next = null; // helps avoid pulling new stuff into old-gen } } else // single waiting thread { LockSupport.unpark((Thread) oWaitHead); } // we've unparked all threads we are responsible for; if another cas'd in after our cas // they will rely on {@link #isReady} or appropriately wait for the next call to signal return; } } } /** * Block the calling thread if the notifier is not ready. * * @param cMillis the time to block for * * @throws InterruptedException if the calling thread is interrupted */ protected void park(long cMillis) throws InterruptedException { if (!isReady()) { if (cMillis == 0) { Blocking.park(/*blocker*/ this); } else { Blocking.parkNanos(/*blocker*/ this, cMillis * 1000000); } } if (m_oWaitHead != null && Blocking.interrupted()) // only pay the cost of interrupt check if we may not have been signaled { throw new InterruptedException(); } } /** * Make a link for the calling thread, checking if one already exists for this notifier. * * @param threadThis the calling thread * @param lBitThis this thread's contribution to the bloom filter * @param oHead the current head * * @return this thread's link, or null if we should not block */ protected Link makeSelfLink(Thread threadThis, long lBitThis, Object oHead) { // It's possible this thread is already in the chain, but this can only happen if we had a induced // wakeup (ready-check, timeout, spurious) while waiting on this notifier previously. // We could try to move this check to after we wakeup, but this will be more harder // to do, and we're about to block anyway so we might as well do it now. We make use of a bloom // filter to avoid scanning any deeper into the chain then is absolutely necessary, which in many // cases may eliminate the scan entirely. Link linkHead; if (oHead == threadThis) { return null; // we're already there } else if (oHead instanceof Link) { linkHead = (Link) oHead; for (Link link = linkHead; link != null && (link.lFilterThreads & lBitThis) != 0L; link = link.next) { if (link.thread == threadThis) { return null; // we're already there } } } else { linkHead = new Link((Thread) oHead); linkHead.lFilterThreads = 1L << (oHead.hashCode() % 61); } Link linkThis = new Link(threadThis); linkThis.next = linkHead; linkThis.lFilterThreads = lBitThis | linkHead.lFilterThreads; return linkThis; } /** * Return true if the notifier is ready, i.e. threads entering await cant return without blocking. * * @return true if the notifier is ready */ abstract protected boolean isReady(); // ----- inner class: Link ---------------------------------------------- /** * A link in a stack of waiting threads. */ protected static final class Link { /** * Construct a new Link for a given thread. * * @param thread the thread */ Link(Thread thread) { this.thread = thread; } /** * This waiting thread. */ final Thread thread; /** * A bloom filter of the waiting threads. */ long lFilterThreads; /** * The next waiting thread. */ Link next; } // ----- data members --------------------------------------------------- /** * The head of a stack of waiting threads. The head can be either a Thread, or a Link. */ protected volatile Object m_oWaitHead; /** * The atomic field updater for {@link #m_oWaitHead}. */ private static final AtomicReferenceFieldUpdater s_fuHead = AtomicReferenceFieldUpdater.newUpdater(ConcurrentNotifier.class, Object.class, "m_oWaitHead"); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy