package org.jgroups.tests; import org.jgroups.Address; import org.jgroups.Message; import org.jgroups.PhysicalAddress; import org.jgroups.protocols.RingBufferBundler; import org.jgroups.protocols.TP; import org.jgroups.util.AsciiString; import org.jgroups.util.DefaultThreadFactory; import org.jgroups.util.RingBuffer; import org.jgroups.util.Util; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.locks.LockSupport; import java.util.stream.Stream; /** * @author Bela Ban * @since 4.0 */ @Test(singleThreaded=true) public class RingBundlerTest { protected static final Address a=Util.createRandomAddress("A"), b=Util.createRandomAddress("B"), c=Util.createRandomAddress("C"), d=Util.createRandomAddress("D"); public void testReceiveAndSend() throws Exception { RingBufferBundler bundler=new RingBufferBundler(16); RingBuffer<Message> rb=bundler.buf(); MockTransport transport=new MockTransport(); bundler.init(transport); for(int i =0; i < 6; i++) bundler.send(new Message(null)); System.out.println("rb = " + rb); int cnt=rb.countLockLockless(); assert cnt == 6; bundler.sendBundledMessages(rb.buf(), rb.readIndexLockless(), cnt); rb.publishReadIndex(cnt); System.out.println("rb = " + rb); assert rb.readIndex() == 6; assert rb.writeIndex() == 6; assert rb.count() == 0; assert transport.map.get(null) == 1; transport.map.clear(); for(Message msg: create(10000, null, a,a,a,b,c,d,d,a, null, null, a)) bundler.send(msg); System.out.println("rb = " + rb); cnt=rb.countLockLockless(); assert cnt == 12; assert rb.readIndex() == 6; assert rb.writeIndex() == 2; bundler.sendBundledMessages(rb.buf(), rb.readIndexLockless(), rb.countLockLockless()); rb.publishReadIndex(cnt); assert rb.readIndex() == 2; assert rb.writeIndex() == 2; assert rb.count() == 0; Stream.of(null, a,b,c,d).forEach(a -> {assert transport.map.get(a) == 1;}); } public void testFullBufferAndRead() throws Exception { RingBufferBundler bundler=new RingBufferBundler(16); RingBuffer<Message> rb=bundler.buf(); MockTransport transport=new MockTransport(); bundler.init(transport); bundler.stop(); // stops the reader thread for(int i=0; i < 16; i++) bundler.send(new Message(a)); // buffer is full now; reader is not running System.out.println("rb = " + rb); Thread[] adders=new Thread[16]; for(int i=0; i < 16; i++) { adders[i]=new Thread(() -> { try { bundler.send(new Message(b)); } catch(Exception e) { e.printStackTrace(); } }, "Adder-" + i); adders[i].start(); } int available=rb.waitForMessages(5, (it,spins) -> LockSupport.park()); System.out.println("available = " + available); assert available == 16; // we skip the sending rb.publishReadIndex(available); while(!rb.isEmpty() || !allDone(adders)) { available=rb.waitForMessages(5, (it,spins) -> LockSupport.parkNanos(1)); System.out.println("available = " + available); rb.publishReadIndex(available); } assert rb.isEmpty(); assert allDone(adders); } protected static boolean allDone(Thread[] threads) { for(Thread thread: threads) if(thread.isAlive()) return false; return true; } protected List<Message> create(int msg_size, Address ... destinations) { List<Message> list=new ArrayList<>(destinations.length); for(Address dest: destinations) list.add(new Message(dest, new byte[msg_size])); return list; } protected static class MockTransport extends TP { protected final Map<Address,Integer> map=new HashMap<>(); public MockTransport() { this.cluster_name=new AsciiString("mock"); thread_factory=new DefaultThreadFactory("", false); } public boolean supportsMulticasting() { return false; } public void sendMulticast(byte[] data, int offset, int length) throws Exception { incrCount(null); } protected void sendToSingleMember(Address dest, byte[] buf, int offset, int length) throws Exception { incrCount(dest); } public void sendUnicast(PhysicalAddress dest, byte[] data, int offset, int length) throws Exception { } public String getInfo() { return null; } protected PhysicalAddress getPhysicalAddress() { return null; } protected void incrCount(Address dest) { Integer count=map.get(dest); if(count == null) map.put(dest, 1); else map.put(dest, count+1); } } }