package com.jivesoftware.os.amza.service; import com.google.common.base.Optional; import com.jivesoftware.os.mlogger.core.MetricLogger; import com.jivesoftware.os.mlogger.core.MetricLoggerFactory; import java.util.concurrent.Callable; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; /** * */ public class AwaitNotify<K> { private static final MetricLogger LOG = MetricLoggerFactory.getLogger(); private final AtomicLong[] changedLocks; public AwaitNotify(int stripingLevel) { this.changedLocks = new AtomicLong[stripingLevel]; for (int i = 0; i < stripingLevel; i++) { this.changedLocks[i] = new AtomicLong(); } } private AtomicLong getChangedLock(K key) { return changedLocks[Math.abs(key.hashCode() % changedLocks.length)]; } public <R> R awaitChange(K key, Callable<Optional<R>> awaiter, long timeoutMillis) throws Exception { long startTime = System.currentTimeMillis(); do { AtomicLong changedLock = getChangedLock(key); long version = changedLock.get(); Optional<R> result = awaiter.call(); if (result != null) { return result.orNull(); } synchronized (changedLock) { if (version == changedLock.get()) { long elapse = System.currentTimeMillis() - startTime; if (elapse < timeoutMillis) { changedLock.wait(timeoutMillis - elapse); LOG.debug("Woke from waiting after {}", System.currentTimeMillis() - startTime); } } } } while (timeoutMillis < 0 || System.currentTimeMillis() - startTime < timeoutMillis); throw new TimeoutException("Timed out awaiting changes after ms: " + timeoutMillis); } public void notifyChange(K key, Callable<Boolean> change) throws Exception { AtomicLong changedLock = getChangedLock(key); changedLock.incrementAndGet(); if (change.call()) { synchronized (changedLock) { changedLock.notifyAll(); } } } }