/** * Performs a selective receive. This method blocks (but for no longer than the given timeout) * until a message that is {@link MessageProcessor#process(java.lang.Object) selected} by the * given {@link MessageProcessor} is available in the mailbox, and returns the value returned by * {@link MessageProcessor#process(java.lang.Object) MessageProcessor.process}. If the given * timeout expires, this method returns {@code null}. * * <p>Messages that are not selected, are temporarily skipped. They will remain in the mailbox * until another call to receive (selective or non-selective) retrieves them. * * @param <T> The type of the returned value * @param timeout the duration to wait for a matching message to arrive. * @param unit timeout's time unit. * @param proc performs the selection. * @return The non-null value returned by {@link MessageProcessor#process(java.lang.Object) * MessageProcessor.process}, or {@code null} if the timeout expired. * @throws InterruptedException */ public final <T> T receive(long timeout, TimeUnit unit, MessageProcessor<? super Message, T> proc) throws TimeoutException, SuspendExecution, InterruptedException { assert Actor.currentActor() == null || Actor.currentActor() == actor; final Mailbox<Object> mailbox = actor.mailbox(); actor.checkThrownIn0(); mailbox.maybeSetCurrentStrandAsOwner(); final long start = timeout > 0 ? System.nanoTime() : 0; long now; long left = unit != null ? unit.toNanos(timeout) : 0; final long deadline = start + left; actor.monitorResetSkippedMessages(); QueueIterator<Object> it = mailbox.queue().iterator(); for (int i = 0; ; i++) { if (actor.flightRecorder != null) actor.record( 1, "SelctiveReceiveHelper", "receive", "%s waiting for a message. %s", this, timeout > 0 ? "millis left: " + TimeUnit.MILLISECONDS.convert(left, TimeUnit.NANOSECONDS) : ""); mailbox.lock(); if (it.hasNext()) { final Object m = it.next(); mailbox.unlock(); if (m == currentMessage) { it.remove(); continue; } actor.record(1, "SelctiveReceiveHelper", "receive", "Received %s <- %s", this, m); actor.monitorAddMessage(); if (m instanceof LifecycleMessage) { it.remove(); handleLifecycleMessage((LifecycleMessage) m); } else { final Message msg = (Message) m; currentMessage = msg; try { T res = proc.process(msg); if (res != null) { if (it.value() == msg) // another call to receive from within the processor may have deleted msg it.remove(); return res; } } catch (Exception e) { if (it.value() == msg) // another call to receive from within the processor may have deleted msg it.remove(); throw e; } finally { currentMessage = null; } actor.record(1, "SelctiveReceiveHelper", "receive", "%s skipped %s", this, m); actor.monitorSkippedMessage(); } } else { try { if (unit == null) mailbox.await(i); else if (timeout > 0) { mailbox.await(i, left, TimeUnit.NANOSECONDS); now = System.nanoTime(); left = deadline - now; if (left <= 0) { actor.record(1, "Actor", "receive", "%s timed out.", this); throw new TimeoutException(); } } else { return null; } } finally { mailbox.unlock(); } } } }