package org.jgroups.tests.byteman; import org.jboss.byteman.contrib.bmunit.BMNGRunner; import org.jboss.byteman.contrib.bmunit.BMScript; import org.jgroups.Global; import org.jgroups.util.RingBufferLockless; import org.testng.annotations.Test; import java.util.List; /** * Tests the correctness of RingBuffer * @author Bela Ban * @since 3.1 */ @Test(groups=Global.BYTEMAN,description="Correctness tests for RingBuffer",enabled=false) public class RingBufferTest extends BMNGRunner { /*@BMRules(rules= { @BMRule(name="DelayRemove", targetClass="org.jgroups.util.RingBuffer", targetMethod="remove(boolean)", targetLocation="WRITE hd", action="System.out.println(\"---> Remover: waiting on \\\"remove\\\"\");\n" + " waitFor(\"remove\");\n" + " System.out.println(\"---> remover: was woken up\");"), @BMRule(name="SignalAdd", targetClass="org.jgroups.util.RingBuffer", targetMethod="remove(boolean)", targetLocation="EXIT", action="System.out.println(\"---> Remover: signalling \\\"add\\\"\");\n" + " signalWake(\"add\", true);"), @BMRule(name="DelayAdd", targetClass="org.jgroups.util.RingBuffer", targetMethod="add(long,java.lang.Object,boolean)", targetLocation="READ low", condition="!createCountDown(\"tmp\", 4) && countDown(\"tmp\")", action="System.out.println(\"---> Adder: signaling \\\"remove\\\"\");\n" + " signalWake(\"remove\", true);\n" + " System.out.println(\"--> Adder: waiting for \\\"add\\\"\");\n" + " waitFor(\"add\");\n" + " System.out.println(\"--> Adder: awoken\");") })*/ /** * Tests the following scenario: * HD=4, HR=5 * T1 calls add(5). 5 already exists * T2 calls remove() with nullify=true * T1 reads HD, value is 4, continues (would terminate if HD was 5) * T2 checks that element at index HD+1 (5) is not null * T2 gets element at index HD+1 (5) * T2 increments HD to 5 and nulls the element at index 5 * T1 does a CAS(null, 5) and succeeds because T2 just nulled the element ==> We now deliver the message at index 5 TWICE (or multiple times) ! */ @BMScript(dir="scripts/RingBufferTest", value="testRemoveAndConcurrentAdd") public void testRemoveAndConcurrentAdd() throws InterruptedException { final RingBufferLockless<Integer> buf=new RingBufferLockless<Integer>(10, 0); for(int i=1; i <= 5; i++) buf.add(i, i); buf.removeMany(true,4); System.out.println("buf = " + buf); Remover remover=new Remover(buf, 1); remover.start(); Adder adder=new Adder(5, buf, false); adder.start(); remover.join(); adder.join(); System.out.println("buf = " + buf); for(int i=1; i <=5; i++) System.out.println(i + ": " + buf._get(i)); for(int i=1; i <=5; i++) assert buf._get(i) == null : "element " + i + " should be null, but isn't"; } /** * Same as above, but using removeMany() rather than remove() * @throws InterruptedException */ @BMScript(dir="scripts/RingBufferTest", value="testRemoveAndConcurrentAdd2") public void testRemoveAndConcurrentAdd2() throws InterruptedException { final RingBufferLockless<Integer> buf=new RingBufferLockless<Integer>(10, 0); for(int i=1; i <= 10; i++) buf.add(i, i); buf.removeMany(true, 4); System.out.println("buf = " + buf); Remover remover=new Remover(buf, 0); remover.start(); Adder[] adders=new Adder[6]; for(int i=0; i < adders.length; i++) { adders[i]=new Adder(i+5, buf, true); adders[i].start(); } remover.join(); for(Adder adder: adders) adder.join(); System.out.println("buf = " + buf); for(int i=1; i <=10; i++) System.out.println(i + ": " + buf._get(i)); for(int i=1; i <=10; i++) assert buf._get(i) == null : "element " + i + " should be null, but isn't"; } protected static <T> void assertIndices(RingBufferLockless<T> buf, long low, long hd, long hr) { assert buf.getLow() == low : "expected low=" + low + " but was " + buf.getLow(); assert buf.getHighestDelivered() == hd : "expected hd=" + hd + " but was " + buf.getHighestDelivered(); assert buf.getHighestReceived() == hr : "expected hr=" + hr + " but was " + buf.getHighestReceived(); } protected static class Adder extends Thread { protected final int seqno; protected final RingBufferLockless<Integer> buf; protected final boolean block; public Adder(int seqno, RingBufferLockless<Integer> buf, boolean block) { this.seqno=seqno; this.buf=buf; this.block=block; } public void run() { boolean success=buf.add(seqno, seqno, block); // System.out.println("Adder: added " + seqno + (success? " successfully" : " unsuccessfully")); } } protected static class Remover extends Thread { protected final RingBufferLockless<Integer> buf; protected final int remove_num_elements; public Remover(RingBufferLockless<Integer> buf, int num) { this.buf=buf; remove_num_elements=num; } public void run() { if(remove_num_elements == 0) { List<Integer> list=buf.removeMany(true, remove_num_elements); System.out.println("Remover: removed " + list); } else { Integer element=buf.remove(true); System.out.println("Remover: removed " + element); } } } }