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);
}
}
}
}