package org.jgroups.tests;
import org.jgroups.Address;
import org.jgroups.Global;
import org.jgroups.util.CreditMap;
import org.jgroups.util.Util;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.List;
import java.util.concurrent.CyclicBarrier;
/**
* Tests CreditMap
* @author Bela Ban
*/
@Test(groups=Global.FUNCTIONAL,sequential=true)
public class CreditMapTest {
static Address a=Util.createRandomAddress("A");
static Address b=Util.createRandomAddress("B");
static Address c=Util.createRandomAddress("C");
static Address d=Util.createRandomAddress("D");
static long MAX_CREDITS=1000;
protected CreditMap map;
@BeforeMethod
void create() {
map=new CreditMap(MAX_CREDITS);
}
@AfterMethod
void destroy() {
map.clear();
}
private void addAll() {
map.putIfAbsent(a); map.putIfAbsent(b); map.putIfAbsent(c); map.putIfAbsent(d);
}
private void replenishAll(long credits) {
map.replenish(a, credits);
map.replenish(b, credits);
map.replenish(c, credits);
map.replenish(d, credits);
}
public void testSimpleDecrement() {
addAll();
System.out.println("map:\n" + map);
boolean rc=map.decrement(200, 4000);
System.out.println("rc=" + rc + ", map:\n" + map);
assert rc;
assert map.getMinCredits() == MAX_CREDITS - 200;
assert map.getAccumulatedCredits() == 200;
rc=map.decrement(150, 100);
System.out.println("\nrc=" + rc + ", map:\n" + map);
assert rc;
assert map.getMinCredits() == MAX_CREDITS - 200 - 150;
assert map.getAccumulatedCredits() == 200 + 150;
rc=map.decrement(300, 100);
System.out.println("\nrc=" + rc + ", map:\n" + map);
assert rc;
assert map.getMinCredits() == MAX_CREDITS - 200 - 150 - 300;
assert map.getAccumulatedCredits() == 200 + 150 + 300;
rc=map.decrement(500, 100);
System.out.println("\nrc=" + rc + ", map:\n" + map);
assert !rc;
assert map.getMinCredits() == MAX_CREDITS - 200 - 150 - 300;
assert map.getAccumulatedCredits() == 200 + 150 + 300;
}
public void testDecrementAndReplenish() {
testSimpleDecrement();
map.replenish(a, MAX_CREDITS);
System.out.println("\nmap:\n" + map);
assert map.getMinCredits() == MAX_CREDITS - 200 - 150 - 300;
assert map.getAccumulatedCredits() == 0;
map.replenish(b, MAX_CREDITS);
map.replenish(c, MAX_CREDITS);
System.out.println("\nmap:\n" + map);
assert map.getMinCredits() == MAX_CREDITS - 200 - 150 - 300;
assert map.getAccumulatedCredits() == 0;
map.replenish(d, MAX_CREDITS);
System.out.println("\nmap:\n" + map);
assert map.getMinCredits() == MAX_CREDITS;
assert map.getAccumulatedCredits() == 0;
}
public void testDecrementAndReplenish2() {
map.putIfAbsent(a);
map.decrement(200, 100);
map.putIfAbsent(b);
map.decrement(200, 100);
map.putIfAbsent(c);
map.decrement(200, 100);
map.putIfAbsent(d);
map.decrement(200, 100);
// A: 200, B: 400, C: 600, D: 800
System.out.println("map = " + map);
assert map.getAccumulatedCredits() == 200;
assert map.getMinCredits() == 200;
map.replenish(d, 100);
map.replenish(c, 100);
map.replenish(a, 100);
map.replenish(b, 100);
assert map.getMinCredits() == 300;
}
public void testBlockingDecrementAndReplenishment() throws Exception {
final CyclicBarrier barrier=new CyclicBarrier(2);
Thread thread=new Thread() {
public void run() {
try {
barrier.await();
Util.sleep(1000);
replenishAll(100);
}
catch(Exception e) {
e.printStackTrace();
}
}
};
thread.start();
addAll();
boolean rc=map.decrement(800, 100);
assert rc;
System.out.println("map:\n" + map);
barrier.await();
rc=map.decrement(250, 5000);
assert rc;
System.out.println("map:\n" + map);
assert map.getMinCredits() == 50;
assert map.getAccumulatedCredits() == 250;
}
public void testBlockingDecrementAndReplenishment2() {
long[] credit_sizes={500, 100, 100, 500, 300};
Decrementer[] decrementers=new Decrementer[credit_sizes.length];
addAll();
boolean rc=map.decrement(800, 100);
assert rc;
for(int i=0; i < credit_sizes.length; i++)
decrementers[i]=new Decrementer(map, credit_sizes[i], 20000, true);
for(Decrementer decr: decrementers)
decr.start();
for(int i=0; i < 10; i++) {
if(countAliveThreads(decrementers) == 3)
break;
Util.sleep(500);
}
int alive=countAliveThreads(decrementers);
assert alive == 3;
replenishAll(400); // the 300 credit decr will succeed now
for(int i=0; i < 10; i++) {
if(countAliveThreads(decrementers) == 2)
break;
Util.sleep(500);
}
assert countAliveThreads(decrementers) == 2;
replenishAll(700); // one of the two 500 creds will succeed
for(int i=0; i < 10; i++) {
if(countAliveThreads(decrementers) == 1)
break;
Util.sleep(500);
}
assert countAliveThreads(decrementers) == 1;
replenishAll(300); // the other one of the 500 creds will succeed
for(int i=0; i < 10; i++) {
if(countAliveThreads(decrementers) == 0)
break;
Util.sleep(500);
}
assert countAliveThreads(decrementers) == 0;
}
public void testClear() {
addAll();
boolean rc=map.decrement(800, 100);
assert rc;
Decrementer decr1=new Decrementer(map, 300, 20000, false), decr2=new Decrementer(map, 500, 20000, false);
decr1.start();
decr2.start();
Util.sleep(500);
map.clear();
for(int i=0; i < 20; i++) {
if(!decr1.isAlive() && !decr2.isAlive())
break;
else
Util.sleep(1000);
}
assert !decr1.isAlive();
assert !decr2.isAlive();
}
public void testGetMembersWithInsufficientCredits() {
addAll();
boolean rc=map.decrement(800, 50);
assert rc;
List<Address> list=map.getMembersWithInsufficientCredits(100);
assert list.isEmpty();
list=map.getMembersWithInsufficientCredits(200);
assert list.isEmpty();
list=map.getMembersWithInsufficientCredits(250);
assert list.size() == 4;
assert list.contains(a) && list.contains(b) && list.contains(c) && list.contains(d);
map.remove(b); map.remove(c);
list=map.getMembersWithInsufficientCredits(250);
assert list.size() == 2;
assert list.contains(a) && list.contains(d);
map.decrement(100, 50);
map.putIfAbsent(b); map.putIfAbsent(c);
// Now A and D have 100 credits, B and C 1000
list=map.getMembersWithInsufficientCredits(800);
assert list.size() == 2;
assert list.contains(a) && list.contains(d);
list=map.getMembersWithInsufficientCredits(100);
assert list.isEmpty();
}
protected int countAliveThreads(Thread[] threads) {
int alive=0;
for(Thread thread: threads)
if(thread.isAlive())
alive++;
return alive;
}
protected static class Decrementer extends Thread {
private final CreditMap map;
private final long amount;
private final long timeout;
protected final boolean loop;
public Decrementer(CreditMap map, long amount, long timeout, boolean loop) {
this.map=map;
this.amount=amount;
this.timeout=timeout;
this.loop=loop;
}
public void run() {
while(true) {
boolean rc=map.decrement(amount, timeout);
if(rc) {
System.out.println("[" + getId() + "] decremented " + amount + " credits");
break;
}
if(!loop)
break;
}
}
}
}