/* * Quasar: lightweight threads and actors for the JVM. * Copyright (c) 2013-2016, 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 static co.paralleluniverse.common.test.Matchers.*; import co.paralleluniverse.common.test.TestUtil; import co.paralleluniverse.common.util.Function2; import co.paralleluniverse.fibers.Fiber; import co.paralleluniverse.fibers.FiberForkJoinScheduler; import co.paralleluniverse.fibers.FiberScheduler; import co.paralleluniverse.fibers.SuspendExecution; import co.paralleluniverse.strands.Strand; import co.paralleluniverse.strands.SuspendableAction1; import co.paralleluniverse.strands.SuspendableAction2; import co.paralleluniverse.strands.SuspendableCallable; import co.paralleluniverse.strands.SuspendableRunnable; import co.paralleluniverse.strands.Timeout; import co.paralleluniverse.strands.channels.Channels.OverflowPolicy; import com.google.common.base.Function; import com.google.common.base.Predicate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.TimeUnit; import static org.hamcrest.CoreMatchers.*; import org.junit.After; import static org.junit.Assert.*; import static org.junit.Assume.*; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; /** * * @author pron */ @RunWith(Parameterized.class) public class TransformingChannelTest { private static final Object GO = new Object(); @Rule public TestName name = new TestName(); @Rule public TestRule watchman = TestUtil.WATCHMAN; final int mailboxSize; final OverflowPolicy policy; final boolean singleConsumer; final boolean singleProducer; final FiberScheduler scheduler; // public ChannelTest() { // fjPool = new ForkJoinPool(4, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); // this.mailboxSize = 0; // this.policy = OverflowPolicy.BLOCK; // this.singleConsumer = false; // this.singleProducer = false; // // Debug.dumpAfter(20000, "channels.log"); // } public TransformingChannelTest(int mailboxSize, OverflowPolicy policy, boolean singleConsumer, boolean singleProducer) { scheduler = new FiberForkJoinScheduler("test", 4, null, false); this.mailboxSize = mailboxSize; this.policy = policy; this.singleConsumer = singleConsumer; this.singleProducer = singleProducer; } @Parameterized.Parameters public static Collection<Object[]> data() { return Arrays.asList(new Object[][]{ {5, OverflowPolicy.THROW, true, false}, {5, OverflowPolicy.THROW, false, false}, {5, OverflowPolicy.BLOCK, true, false}, {5, OverflowPolicy.BLOCK, false, false}, {1, OverflowPolicy.BLOCK, false, false}, {-1, OverflowPolicy.THROW, true, false}, {5, OverflowPolicy.DISPLACE, true, false}, {0, OverflowPolicy.BLOCK, false, false},}); } private <Message> Channel<Message> newChannel() { return Channels.newChannel(mailboxSize, policy, singleProducer, singleConsumer); } @Before public void setUp() { } @After public void tearDown() { } @Test public void transformingReceiveChannelIsEqualToChannel() throws Exception { final Channel<Integer> ch = newChannel(); ReceivePort<Integer> ch1 = Channels.filter((ReceivePort<Integer>) ch, new Predicate<Integer>() { @Override public boolean apply(Integer input) { return input % 2 == 0; } }); ReceivePort<Integer> ch2 = Channels.map((ReceivePort<Integer>) ch, new Function<Integer, Integer>() { @Override public Integer apply(Integer input) { return input + 10; } }); ReceivePort<Integer> ch3 = Channels.flatMap((ReceivePort<Integer>) ch, new Function<Integer, ReceivePort<Integer>>() { @Override public ReceivePort<Integer> apply(Integer input) { return Channels.toReceivePort(Arrays.asList(new Integer[]{input * 10, input * 100, input * 1000})); } }); ReceivePort<Integer> ch4 = Channels.reduce((ReceivePort<Integer>) ch, new Function2<Integer, Integer, Integer>() { @Override public Integer apply(Integer accum, Integer input) { return (accum += input); } }, 0); ReceivePort<Integer> ch5 = Channels.take((ReceivePort<Integer>) ch, 1); assertTrue(ch1.equals(ch)); assertTrue(ch.equals(ch1)); assertTrue(ch2.equals(ch)); assertTrue(ch.equals(ch2)); assertTrue(ch3.equals(ch)); assertTrue(ch.equals(ch3)); assertTrue(ch4.equals(ch)); assertTrue(ch.equals(ch4)); assertTrue(ch5.equals(ch)); assertTrue(ch.equals(ch5)); assertTrue(ch1.equals(ch1)); assertTrue(ch1.equals(ch2)); assertTrue(ch1.equals(ch3)); assertTrue(ch1.equals(ch4)); assertTrue(ch1.equals(ch5)); assertTrue(ch2.equals(ch1)); assertTrue(ch2.equals(ch2)); assertTrue(ch2.equals(ch3)); assertTrue(ch2.equals(ch4)); assertTrue(ch2.equals(ch5)); assertTrue(ch3.equals(ch1)); assertTrue(ch3.equals(ch2)); assertTrue(ch3.equals(ch3)); assertTrue(ch3.equals(ch4)); assertTrue(ch3.equals(ch5)); assertTrue(ch4.equals(ch1)); assertTrue(ch4.equals(ch2)); assertTrue(ch4.equals(ch3)); assertTrue(ch4.equals(ch4)); assertTrue(ch4.equals(ch5)); assertTrue(ch5.equals(ch1)); assertTrue(ch5.equals(ch2)); assertTrue(ch5.equals(ch3)); assertTrue(ch5.equals(ch4)); assertTrue(ch5.equals(ch5)); } @Test public void transformingSendChannelIsEqualToChannel() throws Exception { final Channel<Integer> ch = newChannel(); SendPort<Integer> ch1 = Channels.filterSend((SendPort<Integer>) ch, new Predicate<Integer>() { @Override public boolean apply(Integer input) { return input % 2 == 0; } }); SendPort<Integer> ch2 = Channels.mapSend((SendPort<Integer>) ch, new Function<Integer, Integer>() { @Override public Integer apply(Integer input) { return input + 10; } }); SendPort<Integer> ch3 = Channels.flatMapSend(Channels.<Integer>newChannel(1), (SendPort<Integer>) ch, new Function<Integer, ReceivePort<Integer>>() { @Override public ReceivePort<Integer> apply(Integer input) { return Channels.toReceivePort(Arrays.asList(new Integer[]{input * 10, input * 100, input * 1000})); } }); SendPort<Integer> ch4 = Channels.reduceSend((SendPort<Integer>) ch, new Function2<Integer, Integer, Integer>() { @Override public Integer apply(Integer accum, Integer input) { return (accum += input); } }, 0); assertTrue(ch1.equals(ch)); assertTrue(ch.equals(ch1)); assertTrue(ch2.equals(ch)); assertTrue(ch.equals(ch2)); assertTrue(ch3.equals(ch)); assertTrue(ch.equals(ch3)); assertTrue(ch4.equals(ch)); assertTrue(ch.equals(ch4)); assertTrue(ch1.equals(ch1)); assertTrue(ch1.equals(ch2)); assertTrue(ch1.equals(ch3)); assertTrue(ch1.equals(ch4)); assertTrue(ch2.equals(ch1)); assertTrue(ch2.equals(ch2)); assertTrue(ch2.equals(ch3)); assertTrue(ch2.equals(ch4)); assertTrue(ch3.equals(ch1)); assertTrue(ch3.equals(ch2)); assertTrue(ch3.equals(ch3)); assertTrue(ch3.equals(ch4)); assertTrue(ch4.equals(ch1)); assertTrue(ch4.equals(ch2)); assertTrue(ch4.equals(ch3)); assertTrue(ch4.equals(ch4)); } @Test public void testFilterFiberToFiber() throws Exception { final Channel<Integer> ch = newChannel(); Fiber fib1 = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { ReceivePort<Integer> ch1 = Channels.filter((ReceivePort<Integer>) ch, new Predicate<Integer>() { @Override public boolean apply(Integer input) { return input % 2 == 0; } }); Integer m1 = ch1.receive(); Integer m2 = ch1.receive(); Integer m3 = ch1.receive(); assertThat(m1, equalTo(2)); assertThat(m2, equalTo(4)); assertThat(m3, is(nullValue())); } }).start(); Fiber fib2 = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { Strand.sleep(50); ch.send(1); ch.send(2); Strand.sleep(50); ch.send(3); ch.send(4); ch.send(5); ch.close(); } }).start(); fib1.join(); fib2.join(); } @Test public void testFilterThreadToFiber() throws Exception { final Channel<Integer> ch = newChannel(); Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { ReceivePort<Integer> ch1 = Channels.filter((ReceivePort<Integer>) ch, new Predicate<Integer>() { @Override public boolean apply(Integer input) { return input % 2 == 0; } }); Integer m1 = ch1.receive(); Integer m2 = ch1.receive(); Integer m3 = ch1.receive(); assertThat(m1, equalTo(2)); assertThat(m2, equalTo(4)); assertThat(m3, is(nullValue())); } }).start(); Strand.sleep(50); ch.send(1); ch.send(2); Strand.sleep(50); ch.send(3); ch.send(4); ch.send(5); ch.close(); fib.join(); } @Test public void testFilterFiberToThread() throws Exception { final Channel<Integer> ch = newChannel(); Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { Fiber.sleep(100); Strand.sleep(50); ch.send(1); ch.send(2); Strand.sleep(50); ch.send(3); ch.send(4); ch.send(5); ch.close(); } }).start(); ReceivePort<Integer> ch1 = Channels.filter((ReceivePort<Integer>) ch, new Predicate<Integer>() { @Override public boolean apply(Integer input) { return input % 2 == 0; } }); Integer m1 = ch1.receive(); Integer m2 = ch1.receive(); Integer m3 = ch1.receive(); assertThat(m1, equalTo(2)); assertThat(m2, equalTo(4)); assertThat(m3, is(nullValue())); fib.join(); } @Test public void testFilterWithTimeouts() throws Exception { final Channel<Integer> ch = newChannel(); final Channel<Object> sync = Channels.newChannel(0); final Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { final ReceivePort<Integer> ch1 = Channels.filter((ReceivePort<Integer>) ch, new Predicate<Integer>() { @Override public boolean apply(Integer input) { return input % 2 == 0; } }); sync.receive(); // 0 final Integer m1 = ch1.receive(200, TimeUnit.MILLISECONDS); sync.receive(); // 1 final Integer m0 = ch1.receive(10, TimeUnit.MILLISECONDS); final Integer m2 = ch1.receive(190, TimeUnit.MILLISECONDS); final Integer m3 = ch1.receive(30, TimeUnit.MILLISECONDS); assertThat(m1, equalTo(2)); assertThat(m0, is(nullValue())); assertThat(m2, equalTo(4)); assertThat(m3, is(nullValue())); } }).start(); sync.send(GO); // 0 Strand.sleep(50); ch.send(1, 10, TimeUnit.SECONDS); // Discarded (at receive side) ch.send(2, 10, TimeUnit.SECONDS); sync.send(GO); // 1 Strand.sleep(100); ch.send(3, 10, TimeUnit.SECONDS); // Discarded (at receive side) ch.send(4, 10, TimeUnit.SECONDS); ch.send(5, 10, TimeUnit.SECONDS); // Discarded (at receive side) ch.close(); fib.join(); } @Test public void testSendFilterFiberToFiber() throws Exception { final Channel<Integer> ch = newChannel(); Fiber fib1 = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { Integer m1 = ch.receive(); Integer m2 = ch.receive(); Integer m3 = ch.receive(); assertThat(m1, equalTo(2)); assertThat(m2, equalTo(4)); assertThat(m3, is(nullValue())); } }).start(); Fiber fib2 = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { SendPort<Integer> ch1 = Channels.filterSend((SendPort<Integer>) ch, new Predicate<Integer>() { @Override public boolean apply(Integer input) { return input % 2 == 0; } }); Strand.sleep(50); ch1.send(1); ch1.send(2); Strand.sleep(50); ch1.send(3); ch1.send(4); ch1.send(5); ch1.close(); } }).start(); fib1.join(); fib2.join(); } @Test public void testSendFilterThreadToFiber() throws Exception { final Channel<Integer> ch = newChannel(); Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { Integer m1 = ch.receive(); Integer m2 = ch.receive(); Integer m3 = ch.receive(); assertThat(m1, equalTo(2)); assertThat(m2, equalTo(4)); assertThat(m3, is(nullValue())); } }).start(); SendPort<Integer> ch1 = Channels.filterSend((SendPort<Integer>) ch, new Predicate<Integer>() { @Override public boolean apply(Integer input) { return input % 2 == 0; } }); Strand.sleep(50); ch1.send(1); ch1.send(2); Strand.sleep(50); ch1.send(3); ch1.send(4); ch1.send(5); ch1.close(); fib.join(); } @Test public void testSendFilterFiberToThread() throws Exception { final Channel<Integer> ch = newChannel(); Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { Fiber.sleep(100); SendPort<Integer> ch1 = Channels.filterSend((SendPort<Integer>) ch, new Predicate<Integer>() { @Override public boolean apply(Integer input) { return input % 2 == 0; } }); Strand.sleep(50); ch1.send(1); ch1.send(2); Strand.sleep(50); ch1.send(3); ch1.send(4); ch1.send(5); ch1.close(); } }).start(); Integer m1 = ch.receive(); Integer m2 = ch.receive(); Integer m3 = ch.receive(); assertThat(m1, equalTo(2)); assertThat(m2, equalTo(4)); assertThat(m3, is(nullValue())); fib.join(); } @Test public void testSendFilterWithTimeouts() throws Exception { final Channel<Integer> ch = newChannel(); final Channel<Object> sync = Channels.newChannel(0); final Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { sync.receive(); // 0 final Integer m1 = ch.receive(200, TimeUnit.MILLISECONDS); sync.receive(); // 1 final Integer m0 = ch.receive(10, TimeUnit.MILLISECONDS); final Integer m2 = ch.receive(190, TimeUnit.MILLISECONDS); final Integer m3 = ch.receive(30, TimeUnit.MILLISECONDS); assertThat(m1, equalTo(2)); assertThat(m0, is(nullValue())); assertThat(m2, equalTo(4)); assertThat(m3, is(nullValue())); } }).start(); SendPort<Integer> ch1 = Channels.filterSend((SendPort<Integer>) ch, new Predicate<Integer>() { @Override public boolean apply(Integer input) { return input % 2 == 0; } }); sync.send(GO); // 0 Strand.sleep(50); ch1.send(1, 10, TimeUnit.SECONDS); // Discarded (at send side) ch1.send(2, 10, TimeUnit.SECONDS); sync.send(GO); // 1 Strand.sleep(50); ch1.send(3, 10, TimeUnit.SECONDS); // Discarded (at send side) ch1.send(4, 10, TimeUnit.SECONDS); ch1.send(5, 10, TimeUnit.SECONDS); // Discarded (at send side) ch1.close(); fib.join(); } @Test public void testMapThreadToFiber() throws Exception { final Channel<Integer> ch = newChannel(); Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { ReceivePort<Integer> ch1 = Channels.map((ReceivePort<Integer>) ch, new Function<Integer, Integer>() { @Override public Integer apply(Integer input) { return input + 10; } }); Integer m1 = ch1.receive(); Integer m2 = ch1.receive(); Integer m3 = ch1.receive(); Integer m4 = ch1.receive(); Integer m5 = ch1.receive(); Integer m6 = ch1.receive(); assertThat(m1, equalTo(11)); assertThat(m2, equalTo(12)); assertThat(m3, equalTo(13)); assertThat(m4, equalTo(14)); assertThat(m5, equalTo(15)); assertThat(m6, is(nullValue())); } }).start(); Strand.sleep(50); ch.send(1); ch.send(2); Strand.sleep(50); ch.send(3); ch.send(4); ch.send(5); ch.close(); fib.join(); } @Test public void testSendMapThreadToFiber() throws Exception { final Channel<Integer> ch = newChannel(); Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { Integer m1 = ch.receive(); Integer m2 = ch.receive(); Integer m3 = ch.receive(); Integer m4 = ch.receive(); Integer m5 = ch.receive(); Integer m6 = ch.receive(); assertThat(m1, equalTo(11)); assertThat(m2, equalTo(12)); assertThat(m3, equalTo(13)); assertThat(m4, equalTo(14)); assertThat(m5, equalTo(15)); assertThat(m6, is(nullValue())); } }).start(); SendPort<Integer> ch1 = Channels.mapSend((SendPort<Integer>) ch, new Function<Integer, Integer>() { @Override public Integer apply(Integer input) { return input + 10; } }); Strand.sleep(50); ch1.send(1); ch1.send(2); Strand.sleep(50); ch1.send(3); ch1.send(4); ch1.send(5); ch1.close(); fib.join(); } @Test public void testReduceThreadToFiber() throws Exception { final Channel<Integer> ch = newChannel(); Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { final ReceivePort<Integer> ch1 = Channels.reduce((ReceivePort<Integer>) ch, new Function2<Integer, Integer, Integer>() { @Override public Integer apply(Integer accum, Integer input) { return accum + input; } }, 0); final Integer m1 = ch1.receive(); final Integer m2 = ch1.receive(); final Integer m3 = ch1.receive(); final Integer m4 = ch1.receive(); final Integer m5 = ch1.receive(); final Integer m6 = ch1.receive(); assertThat(m1, equalTo(1)); assertThat(m2, equalTo(3)); assertThat(m3, equalTo(6)); assertThat(m4, equalTo(10)); assertThat(m5, equalTo(15)); assertThat(m6, is(nullValue())); } }).start(); Strand.sleep(50); ch.send(1); ch.send(2); Strand.sleep(50); ch.send(3); ch.send(4); ch.send(5); ch.close(); fib.join(); } @Test public void testReduceInitThreadToFiber() throws Exception { final Channel<Integer> ch = newChannel(); Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { final ReceivePort<Integer> ch1 = Channels.reduce((ReceivePort<Integer>) ch, new Function2<Integer, Integer, Integer>() { @Override public Integer apply(Integer accum, Integer input) { return accum + input; } }, 0); final Integer m1 = ch1.receive(); final Integer m2 = ch1.receive(); assertThat(m1, equalTo(0)); assertNull(m2); } }).start(); Strand.sleep(50); ch.close(); fib.join(); } @Test @SuppressWarnings("null") public void testTakeThreadToFibers() throws Exception { assumeThat(mailboxSize, greaterThan(0)); // TODO Reorg to try with blocking channel at least meaningful parts final Channel<Object> takeSourceCh = newChannel(); // Test 2 fibers failing immediately on take 0 of 1 final ReceivePort<Object> take0RP = Channels.take((ReceivePort<Object>) takeSourceCh, 0); final SuspendableRunnable take0SR = new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { assertThat(take0RP.receive(), is(nullValue())); assertThat(take0RP.tryReceive(), is(nullValue())); long start = System.nanoTime(); assertThat(take0RP.receive(10, TimeUnit.SECONDS), is(nullValue())); long end = System.nanoTime(); assertThat(end - start, lessThan(new Long(5 * 1000 * 1000 * 1000))); // Should be immediate start = System.nanoTime(); assertThat(take0RP.receive(new Timeout(10, TimeUnit.SECONDS)), is(nullValue())); end = System.nanoTime(); assertThat(end - start, lessThan(new Long(5 * 1000 * 1000 * 1000))); // Should be immediate } }; final Fiber take0Of1Fiber1 = new Fiber("take-0-of-1_fiber1", scheduler, take0SR).start(); final Fiber take0Of1Fiber2 = new Fiber("take-0-of-1_fiber2", scheduler, take0SR).start(); takeSourceCh.send(new Object()); take0Of1Fiber1.join(); take0Of1Fiber2.join(); assertThat(takeSourceCh.receive(), is(notNullValue())); // 1 left in source, check and cleanup // Test tryReceive failing immediately when fiber blocked in receive on take 1 of 2 final ReceivePort<Object> take1Of2RP = Channels.take((ReceivePort<Object>) takeSourceCh, 1); final Fiber timeoutSucceedingTake1Of2 = new Fiber("take-1-of-2_timeout_success", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { final long start = System.nanoTime(); assertThat(take1Of2RP.receive(1, TimeUnit.SECONDS), is(notNullValue())); final long end = System.nanoTime(); assertThat(end - start, lessThan(new Long(500 * 1000 * 1000))); } }).start(); Thread.sleep(100); // Let the fiber blocks in receive before starting the try final Fiber tryFailingTake1Of2 = new Fiber("take-1-of-2_try_fail", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { final long start = System.nanoTime(); assertThat(take1Of2RP.tryReceive(), is(nullValue())); final long end = System.nanoTime(); assertThat(end - start, lessThan(new Long(500 * 1000 * 1000))); // Should be immediate } }).start(); Thread.sleep(100); // Make messages available takeSourceCh.send(new Object()); takeSourceCh.send(new Object()); timeoutSucceedingTake1Of2.join(); tryFailingTake1Of2.join(); assertThat(takeSourceCh.receive(), is(notNullValue())); // 1 left in source, check and cleanup // Comprehensive take + contention test: // // - 1 message available immediately, 2 messages available in a burst on the source after 1s // - take 2 // - 5 fibers competing on the take source (1 in front) // // - one front fiber receiving with 200ms timeout => immediate success // - one more front fiber receiving with 200ms timeout => fail // - 3rd fiber taking over, receiving with 200ms timeout => fail // - 4th fiber taking over, receiving with 1s timeout => success // - 5th fiber asking untimed receive, waiting in monitor, will bail out because of take threshold final ReceivePort<Object> take2Of3RPComprehensive = Channels.take((ReceivePort<Object>) takeSourceCh, 2); final Function2<Long, Integer, Fiber> take1SRFun = new Function2<Long, Integer, Fiber>() { @Override public Fiber apply(final Long timeoutMS, final Integer position) { return new Fiber("take-1-of-2_comprehensive_receiver_" + (timeoutMS >= 0 ? timeoutMS : "unlimited") + "ms-" + position, scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { final long start = System.nanoTime(); final Object res = (timeoutMS >= 0 ? take2Of3RPComprehensive.receive(timeoutMS, TimeUnit.MILLISECONDS) : take2Of3RPComprehensive.receive()); final long end = System.nanoTime(); switch (position) { case 1: assertThat(res, is(notNullValue())); assertThat(end - start, lessThan(new Long(300 * 1000 * 1000))); break; case 2: assertThat(res, is(nullValue())); assertThat(end - start, greaterThan(new Long(300 * 1000 * 1000))); break; case 3: assertThat(res, is(nullValue())); assertThat(end - start, greaterThan(new Long(200 * 1000 * 1000))); break; case 4: assertThat(res, is(notNullValue())); assertThat(end - start, lessThan(new Long(1000 * 1000 * 1000))); break; case 5: assertThat(res, is(nullValue())); assertThat(end - start, lessThan(new Long(1000 * 1000 * 1000))); // Should be almost instantaneous break; default: fail(); break; } } }); } }; final Fiber[] competing = new Fiber[5]; // First front fiber winning first message competing[0] = take1SRFun.apply(300l, 1).start(); // Make 1 message available immediately for the first front fiber to consume takeSourceCh.send(new Object()); Thread.sleep(100); // Second front fiber losing (waiting too little for second message) competing[1] = take1SRFun.apply(300l, 2).start(); Thread.sleep(100); // First waiter, will fail (not waiting enough) competing[2] = take1SRFun.apply(200l, 3).start(); Thread.sleep(300); // First waiter takeover // Second waiter, will win second message (waiting enough) competing[3] = take1SRFun.apply(1000l, 4).start(); Thread.sleep(300); // Second waiter takeover // Third waiter, will try after take threshold and will bail out competing[4] = take1SRFun.apply(-1l, 5).start(); // Make 2 more messages available takeSourceCh.send(new Object()); takeSourceCh.send(new Object()); // Wait fibers to finsh for (final Fiber f : competing) f.join(); assertThat(takeSourceCh.receive(), is(notNullValue())); // 1 left in source, check and cleanup // Explicit (and uncoupled from source) closing of TakeSP final ReceivePort<Object> take1Of0ExplicitClose = Channels.take((ReceivePort<Object>) takeSourceCh, 1); final SuspendableRunnable explicitCloseSR = new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { final long start = System.nanoTime(); final Object ret = take1Of0ExplicitClose.receive(); final long end = System.nanoTime(); assertThat(ret, is(nullValue())); assertTrue(take1Of0ExplicitClose.isClosed()); assertFalse(takeSourceCh.isClosed()); assertThat(end - start, lessThan(new Long(500 * 1000 * 1000))); } }; final Fiber explicitCloseF1 = new Fiber("take-explicit-close-1", scheduler, explicitCloseSR); final Fiber explicitCloseF2 = new Fiber("take-explicit-close-2", scheduler, explicitCloseSR); Thread.sleep(100); take1Of0ExplicitClose.close(); } @Test public void testSendReduceThreadToFiber() throws Exception { final Channel<Integer> ch = newChannel(); final Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { Integer m1 = ch.receive(); Integer m2 = ch.receive(); Integer m3 = ch.receive(); Integer m4 = ch.receive(); Integer m5 = ch.receive(); Integer m6 = ch.receive(); assertThat(m1, equalTo(1)); assertThat(m2, equalTo(3)); assertThat(m3, equalTo(6)); assertThat(m4, equalTo(10)); assertThat(m5, equalTo(15)); assertThat(m6, is(nullValue())); } }).start(); final SendPort<Integer> ch1 = Channels.reduceSend((SendPort<Integer>) ch, new Function2<Integer, Integer, Integer>() { @Override public Integer apply(Integer accum, Integer input) { return accum + input; } }, 0); Strand.sleep(50); ch1.send(1); ch1.send(2); Strand.sleep(50); ch1.send(3); ch1.send(4); ch1.send(5); ch1.close(); fib.join(); } @Test public void testSendReduceInitThreadToFiber() throws Exception { final Channel<Integer> ch = newChannel(); final Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { Integer m1 = ch.receive(); Integer m2 = ch.receive(); assertThat(m1, equalTo(0)); assertNull(m2); } }).start(); final SendPort<Integer> ch1 = Channels.reduceSend((SendPort<Integer>) ch, new Function2<Integer, Integer, Integer>() { @Override public Integer apply(Integer accum, Integer input) { return accum + input; } }, 0); Strand.sleep(50); ch1.close(); fib.join(); } @Test public void testZipThreadToFiber() throws Exception { final Channel<String> ch1 = newChannel(); final Channel<Integer> ch2 = newChannel(); Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { ReceivePort<String> ch = Channels.zip(ch1, ch2, new Function2<String, Integer, String>() { @Override public String apply(String x1, Integer x2) { return x1 + x2; } }); String m1 = ch.receive(); String m2 = ch.receive(); String m3 = ch.receive(); String m4 = ch.receive(); assertThat(m1, equalTo("a1")); assertThat(m2, equalTo("b2")); assertThat(m3, equalTo("c3")); assertThat(m4, is(nullValue())); } }).start(); Strand.sleep(50); ch1.send("a"); ch2.send(1); ch1.send("b"); ch2.send(2); ch1.send("c"); ch2.send(3); ch1.send("foo"); ch2.close(); fib.join(); } @Test public void testZipWithTimeoutsThreadToFiber() throws Exception { final Channel<String> ch1 = newChannel(); final Channel<Integer> ch2 = newChannel(); final Channel<Object> sync = Channels.newChannel(0); final Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { final ReceivePort<String> ch = Channels.zip(ch1, ch2, new Function2<String, Integer, String>() { @Override public String apply(String x1, Integer x2) { return x1 + x2; } }); sync.receive(); // 0 final String m1 = ch.receive(200, TimeUnit.MILLISECONDS); sync.receive(); // 1 final String m0 = ch.receive(10, TimeUnit.MILLISECONDS); final String m2 = ch.receive(190, TimeUnit.MILLISECONDS); sync.receive(); // 2 final String m3 = ch.receive(30, TimeUnit.MILLISECONDS); sync.receive(); // 3 final String m4 = ch.receive(30, TimeUnit.MILLISECONDS); assertThat(m1, equalTo("a1")); assertThat(m0, is(nullValue())); assertThat(m2, equalTo("b2")); assertThat(m3, equalTo("c3")); assertThat(m4, is(nullValue())); } }).start(); sync.send(GO); // 0 Strand.sleep(50); ch1.send("a", 10, TimeUnit.SECONDS); ch2.send(1, 10, TimeUnit.SECONDS); sync.send(GO); // 1 ch1.send("b", 10, TimeUnit.SECONDS); Strand.sleep(100); ch2.send(2, 10, TimeUnit.SECONDS); sync.send(GO); // 2 ch1.send("c", 10, TimeUnit.SECONDS); ch2.send(3, 10, TimeUnit.SECONDS); sync.send(GO); // 3 ch1.send("foo", 10, TimeUnit.SECONDS); ch2.close(); // Discards previous fib.join(); } @Test public void testFlatmapThreadToFiber() throws Exception { final Channel<Integer> ch1 = newChannel(); Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { ReceivePort<Integer> ch = Channels.flatMap(ch1, new Function<Integer, ReceivePort<Integer>>() { @Override public ReceivePort<Integer> apply(Integer x) { if (x == 3) return null; if (x % 2 == 0) return Channels.toReceivePort(Arrays.asList(new Integer[]{x * 10, x * 100, x * 1000})); else return Channels.singletonReceivePort(x); } }); assertThat(ch.receive(), is(1)); assertThat(ch.receive(), is(20)); assertThat(ch.receive(), is(200)); assertThat(ch.receive(), is(2000)); assertThat(ch.receive(), is(40)); assertThat(ch.receive(), is(400)); assertThat(ch.receive(), is(4000)); assertThat(ch.receive(), is(5)); assertThat(ch.receive(), is(nullValue())); assertThat(ch.isClosed(), is(true)); } }).start(); Strand.sleep(50); ch1.send(1); ch1.send(2); ch1.send(3); ch1.send(4); ch1.send(5); ch1.close(); fib.join(); } @Test public void testFlatmapWithTimeoutsThreadToFiber() throws Exception { final Channel<Integer> ch1 = newChannel(); final Channel<Object> sync = Channels.newChannel(0); final Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { final ReceivePort<Integer> ch = Channels.flatMap(ch1, new Function<Integer, ReceivePort<Integer>>() { @Override public ReceivePort<Integer> apply(Integer x) { if (x == 3) return null; // Discard if (x % 2 == 0) return Channels.toReceivePort(Arrays.asList(new Integer[]{x * 10, x * 100, x * 1000})); else return Channels.singletonReceivePort(x); } }); sync.receive(); // 0 assertThat(ch.receive(200, TimeUnit.MILLISECONDS), is(1)); sync.receive(); // 1 assertThat(ch.receive(10, TimeUnit.MILLISECONDS), is(nullValue())); assertThat(ch.receive(190, TimeUnit.MILLISECONDS), is(20)); assertThat(ch.receive(10, TimeUnit.MILLISECONDS), is(200)); assertThat(ch.receive(10, TimeUnit.MILLISECONDS), is(2000)); sync.receive(); // 2 assertThat(ch.receive(200, TimeUnit.MILLISECONDS), is(40)); assertThat(ch.receive(10, TimeUnit.MILLISECONDS), is(400)); assertThat(ch.receive(10, TimeUnit.MILLISECONDS), is(4000)); sync.receive(); // 3 assertThat(ch.receive(10, TimeUnit.MILLISECONDS), is(nullValue())); assertThat(ch.receive(190, TimeUnit.MILLISECONDS), is(5)); sync.receive(); // 4 assertThat(ch.receive(30, TimeUnit.MILLISECONDS), is(nullValue())); assertThat(ch.isClosed(), is(true)); } }).start(); sync.send(GO); // 0 Strand.sleep(50); ch1.send(1, 10, TimeUnit.SECONDS); sync.send(GO); // 1 Strand.sleep(100); ch1.send(2, 10, TimeUnit.SECONDS); sync.send(GO); // 2 ch1.send(3, 10, TimeUnit.SECONDS); // Discarded ch1.send(4, 10, TimeUnit.SECONDS); sync.send(GO); // 3 Strand.sleep(50); ch1.send(5, 10, TimeUnit.SECONDS); sync.send(GO); // 4 ch1.close(); fib.join(); } @Test public void testFiberTransform1() throws Exception { final Channel<Integer> in = newChannel(); final Channel<Integer> out = newChannel(); Channels.fiberTransform(in, out, new SuspendableAction2<ReceivePort<Integer>, SendPort<Integer>>() { @Override public void call(ReceivePort<Integer> in, SendPort<Integer> out) throws SuspendExecution, InterruptedException { Integer x; while ((x = in.receive()) != null) { if (x % 2 == 0) out.send(x * 10); } out.send(1234); out.close(); } }); Fiber fib1 = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { assertThat(out.receive(), equalTo(20)); assertThat(out.receive(), equalTo(40)); assertThat(out.receive(), equalTo(1234)); assertThat(out.receive(), is(nullValue())); } }).start(); Fiber fib2 = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { Strand.sleep(50); in.send(1); in.send(2); Strand.sleep(50); in.send(3); in.send(4); in.send(5); in.close(); } }).start(); fib1.join(); fib2.join(); } @Test public void testFlatmapSendThreadToFiber() throws Exception { final Channel<Integer> ch = newChannel(); Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { assertThat(ch.receive(), is(1)); assertThat(ch.receive(), is(20)); assertThat(ch.receive(), is(200)); assertThat(ch.receive(), is(2000)); assertThat(ch.receive(), is(40)); assertThat(ch.receive(), is(400)); assertThat(ch.receive(), is(4000)); assertThat(ch.receive(), is(5)); assertThat(ch.receive(), is(nullValue())); assertThat(ch.isClosed(), is(true)); } }).start(); SendPort<Integer> ch1 = Channels.flatMapSend(Channels.<Integer>newChannel(1), ch, new Function<Integer, ReceivePort<Integer>>() { @Override public ReceivePort<Integer> apply(Integer x) { if (x == 3) return null; if (x % 2 == 0) return Channels.toReceivePort(Arrays.asList(new Integer[]{x * 10, x * 100, x * 1000})); else return Channels.singletonReceivePort(x); } }); Strand.sleep(50); ch1.send(1); ch1.send(2); ch1.send(3); ch1.send(4); ch1.send(5); ch1.close(); fib.join(); } //@Test public void testFlatmapSendWithTimeoutsThreadToFiber() throws Exception { final Channel<Integer> ch = newChannel(); Fiber fib = new Fiber("fiber", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { assertThat(ch.receive(), is(1)); assertThat(ch.receive(30, TimeUnit.MILLISECONDS), is(nullValue())); assertThat(ch.receive(40, TimeUnit.MILLISECONDS), is(20)); assertThat(ch.receive(), is(200)); assertThat(ch.receive(), is(2000)); assertThat(ch.receive(), is(40)); assertThat(ch.receive(), is(400)); assertThat(ch.receive(), is(4000)); assertThat(ch.receive(30, TimeUnit.MILLISECONDS), is(nullValue())); assertThat(ch.receive(40, TimeUnit.MILLISECONDS), is(5)); assertThat(ch.receive(), is(nullValue())); assertThat(ch.isClosed(), is(true)); } }).start(); SendPort<Integer> ch1 = Channels.flatMapSend(Channels.<Integer>newChannel(1), ch, new Function<Integer, ReceivePort<Integer>>() { @Override public ReceivePort<Integer> apply(Integer x) { if (x == 3) return null; if (x % 2 == 0) return Channels.toReceivePort(Arrays.asList(new Integer[]{x * 10, x * 100, x * 1000})); else return Channels.singletonReceivePort(x); } }); Strand.sleep(50); ch1.send(1); Strand.sleep(50); ch1.send(2); ch1.send(3); ch1.send(4); Strand.sleep(50); ch1.send(5); ch1.close(); fib.join(); } @Test public void testSendSplitThreadToFiber() throws Exception { final Channel<String> chF1 = newChannel(); final Channel<String> chF2 = newChannel(); final SendPort<String> splitSP = new SplitSendPort<String>() { @Override protected SendPort select(final String m) { if (m.equals("f1")) return chF1; else return chF2; } }; final Fiber f1 = new Fiber("split-send-1", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { assertThat(chF1.receive(), is("f1")); assertThat(chF1.receive(100, TimeUnit.NANOSECONDS), is(nullValue())); assertThat(chF1.receive(new Timeout(100, TimeUnit.NANOSECONDS)), is(nullValue())); assertThat(chF1.receive(), is(nullValue())); assertTrue(chF1.isClosed()); } }).start(); final Fiber f2 = new Fiber("split-send-2", scheduler, new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { assertThat(chF2.receive(), is(not("f1"))); assertThat(chF2.receive(100, TimeUnit.NANOSECONDS), is(nullValue())); assertThat(chF2.receive(new Timeout(100, TimeUnit.NANOSECONDS)), is(nullValue())); assertThat(chF2.receive(), is(nullValue())); assertTrue(chF2.isClosed()); } }).start(); splitSP.send("f1"); splitSP.send("f2"); splitSP.close(); assertFalse(chF1.isClosed()); assertFalse(chF2.isClosed()); Thread.sleep(100); chF1.close(); chF2.close(); f1.join(); f2.join(); } @Test public void testForEach() throws Exception { final Channel<Integer> ch = newChannel(); Fiber<List<Integer>> fib = new Fiber<List<Integer>>("fiber", scheduler, new SuspendableCallable() { @Override public List<Integer> run() throws SuspendExecution, InterruptedException { final List<Integer> list = new ArrayList<>(); Channels.transform(ch).forEach(new SuspendableAction1<Integer>() { @Override public void call(Integer x) throws SuspendExecution, InterruptedException { list.add(x); } }); return list; } }).start(); Strand.sleep(50); ch.send(1); ch.send(2); Strand.sleep(50); ch.send(3); ch.send(4); ch.send(5); ch.close(); List<Integer> list = fib.get(); assertThat(list, equalTo(Arrays.asList(new Integer[]{1, 2, 3, 4, 5}))); } }