package org.multiverse.stms.gamma.integration.blocking; import org.junit.Before; import org.multiverse.TestThread; import org.multiverse.api.Txn; import org.multiverse.api.TxnExecutor; import org.multiverse.api.callables.TxnCallable; import org.multiverse.api.callables.TxnVoidCallable; import org.multiverse.stms.gamma.GammaConstants; import org.multiverse.stms.gamma.GammaStm; import org.multiverse.stms.gamma.transactionalobjects.GammaTxnRef; import org.multiverse.stms.gamma.transactions.GammaTxn; import java.util.LinkedList; import static org.junit.Assert.assertEquals; import static org.multiverse.TestUtils.*; import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance; import static org.multiverse.api.StmUtils.retry; import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn; public abstract class QueueWithoutCapacity_AbstractTest implements GammaConstants { protected GammaStm stm; private Queue<Integer> queue; private int itemCount = 10 * 1000 * 1000; protected abstract TxnExecutor newPopBlock(); protected abstract TxnExecutor newPushBlock(); @Before public void setUp() { clearThreadLocalTxn(); stm = (GammaStm) getGlobalStmInstance(); } public void run() { queue = new Queue<Integer>(); ProduceThread produceThread = new ProduceThread(); ConsumeThread consumeThread = new ConsumeThread(); startAll(produceThread, consumeThread); joinAll(produceThread, consumeThread); assertEquals(itemCount, produceThread.producedItems.size()); assertEquals(produceThread.producedItems, consumeThread.consumedItems); } class ConsumeThread extends TestThread { private final LinkedList<Integer> consumedItems = new LinkedList<Integer>(); public ConsumeThread() { super("ConsumeThread"); } @Override public void doRun() throws Exception { for (int k = 0; k < itemCount; k++) { int item = queue.pop(); consumedItems.add(item); if (k % 100000 == 0) { System.out.printf("%s is at %s\n", getName(), k); } } } } class ProduceThread extends TestThread { private final LinkedList<Integer> producedItems = new LinkedList<Integer>(); public ProduceThread() { super("ProduceThread"); } @Override public void doRun() throws Exception { for (int k = 0; k < itemCount; k++) { queue.push(k); producedItems.add(k); if (k % 100000 == 0) { sleepMs(100); System.out.printf("%s is at %s\n", getName(), k); } } } } class Queue<E> { final Stack<E> pushedStack = new Stack<E>(); final Stack<E> readyToPopStack = new Stack<E>(); final TxnExecutor pushBlock = newPushBlock(); final TxnExecutor popBlock = newPopBlock(); public void push(final E item) { pushBlock.execute(new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { GammaTxn btx = (GammaTxn) tx; pushedStack.push(btx, item); } }); } public E pop() { return popBlock.execute(new TxnCallable<E>() { @Override public E call(Txn tx) throws Exception { GammaTxn btx = (GammaTxn) tx; if (!readyToPopStack.isEmpty(btx)) { return readyToPopStack.pop(btx); } while (!pushedStack.isEmpty(btx)) { E item = pushedStack.pop(btx); readyToPopStack.push(btx, item); } return readyToPopStack.pop(btx); } }); } } class Stack<E> { final GammaTxnRef<Node<E>> head = new GammaTxnRef<Node<E>>(stm); void push(GammaTxn tx, E item) { Node<E> newHead = new Node<E>(item, head.get()); head.set(newHead); } boolean isEmpty(GammaTxn tx) { return head.isNull(); } E pop(GammaTxn tx) { Node<E> node = head.get(); if (node == null) { retry(); } head.set(node.next); return node.item; } } class Node<E> { final E item; Node<E> next; Node(E item, Node<E> next) { this.item = item; this.next = next; } } }