co.paralleluniverse.strands.channels.TakeReceivePort Maven / Gradle / Ivy
/*
* Quasar: lightweight threads and actors for the JVM.
* Copyright (c) 2013-2015, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.strands.channels;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.strands.Condition;
import co.paralleluniverse.strands.SimpleConditionSynchronizer;
import co.paralleluniverse.strands.Timeout;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import co.paralleluniverse.concurrent.util.EnhancedAtomicLong;
import static co.paralleluniverse.concurrent.util.EnhancedAtomicLong.*;
import co.paralleluniverse.fibers.Suspendable;
/**
*
* @author pron
* @author circlespainter
*/
class TakeReceivePort extends TransformingReceivePort {
private final EnhancedAtomicLong lease = new EnhancedAtomicLong();
private final AtomicLong countDown = new AtomicLong();
private final Condition monitor = new SimpleConditionSynchronizer(null);
public TakeReceivePort(final ReceivePort target, final long count) {
super(target);
final long start = count <= 0 ? 0 : count;
this.lease.set(start);
this.countDown.set(start);
}
@Override
@Suspendable
public M tryReceive() {
try {
return timedReceive(0, TimeUnit.NANOSECONDS);
} catch (Throwable t) {
// It should never happen
throw new AssertionError(t);
}
}
@Override
public M receive() throws SuspendExecution, InterruptedException {
return timedReceive(-1, null);
}
@Override
public M receive(final Timeout timeout) throws SuspendExecution, InterruptedException {
return timedReceive(timeout.nanosLeft(), TimeUnit.NANOSECONDS);
}
@Override
public M receive(final long timeout, final TimeUnit unit) throws SuspendExecution, InterruptedException {
return timedReceive(timeout, unit);
}
private M timedReceive(final long timeout, final TimeUnit unit) throws SuspendExecution, InterruptedException {
// Register in order to receive wakeup signals when waiting in the monitor
final Object ticket = monitor.register();
// Init time bookkeeping in case we have to wait when performing a timed receive
final long start = timeout > 0 ? System.nanoTime() : 0;
long left = unit != null ? unit.toNanos(timeout) : 0;
final long deadline = start + left;
long now;
try {
// Wait loop
for (int i = 0;; i++) {
if (isClosed())
// Fail
return null;
if (!lease.evalAndUpdate(gt(0), DEC)) { // Front line is busy, wait
if (unit == null) // Untimed receive
// => Untimed wait
monitor.await(i);
else if (timeout > 0) { // Timed receive
// => Timed wait with time bookkeeping
monitor.await(i, left, TimeUnit.NANOSECONDS);
now = System.nanoTime();
left = deadline - now;
if (left <= 0)
// Timed receive expired without making it to the front line
return null;
} else // tryReceive
// Front line busy => channel is surely blocking (or closed) => fail try
return null;
} else
// Front line has available seats, try receive
break;
}
// Try receive
final M ret;
if (unit == null)
// Untimed receive
ret = target.receive();
else if (timeout > 0)
// Timed receive
ret = target.receive(timeout, unit);
else
// tryReceive
ret = target.tryReceive();
if (ret != null) {
// Successful receive
if (countDown.decrementAndGet() <= 0)
// Last message consumed, wake up all waiters and return
monitor.signalAll();
return ret;
} else {
// Failed receive, let a waiter in and return null
lease.incrementAndGet();
monitor.signal();
return null;
}
} finally {
// No matter what happens, release our ticket in the monitor
monitor.unregister(ticket);
}
}
@Override
public boolean isClosed() {
return countDown.get() <= 0 || super.isClosed();
}
}