package org.jgroups.tests;
import org.jgroups.Global;
import org.jgroups.JChannel;
import org.jgroups.blocks.executor.ExecutionRunner;
import org.jgroups.blocks.executor.ExecutionService;
import org.jgroups.protocols.CENTRAL_EXECUTOR;
import org.jgroups.util.FutureListener;
import org.jgroups.util.NotifyingFuture;
import org.jgroups.util.Util;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
/** Tests https://issues.jboss.org/browse/JGRP-1696 */
@Test(groups={Global.FUNCTIONAL,Global.EAP_EXCLUDED}, singleThreaded=true)
public class ExecutingServiceTest2 {
Set<Thread> threads=new HashSet<>();
Set<JChannel> channels=new HashSet<>();
@AfterMethod
public void tearDown() {
threads.forEach(Thread::interrupt);
channels.forEach(JChannel::close);
}
@Test
public void testDisconnect() throws Exception {
JChannel channel1=new JChannel(Util.getTestStack(new CENTRAL_EXECUTOR()));
JChannel channel2=new JChannel(Util.getTestStack(new CENTRAL_EXECUTOR()));
channels.add(channel1);
channels.add(channel2);
channel1.connect("test-cluster");
channel2.connect("test-cluster");
Util.waitUntilAllChannelsHaveSameView(20000, 1000, channel1, channel2);
final ExecutionService executionService=new ExecutionService(channel1);
ExecutionRunner executionRunner1=new ExecutionRunner(channel1);
ExecutionRunner executionRunner2=new ExecutionRunner(channel2);
Thread runner1=new Thread(executionRunner1);
threads.add(runner1);
runner1.start();
Thread runner2=new Thread(executionRunner2);
threads.add(runner2);
runner2.start();
final AtomicInteger submittedTasks=new AtomicInteger();
final AtomicInteger finishedTasks=new AtomicInteger();
final FutureListener<Void> listener=future -> {
finishedTasks.incrementAndGet();
synchronized(ExecutingServiceTest2.this) {
ExecutingServiceTest2.this.notify();
}
};
Thread submitter=new Thread(new Runnable() {
@Override
public void run() {
// Two long running tasks that should be sent to each runner
submit(true);
submit(true);
while(!Thread.interrupted()) {
submit(false);
// Throttle
try {
Thread.sleep(50);
}
catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
private void submit(boolean wait) {
Callable<Void> task=new Wait(wait);
NotifyingFuture<Void> future=executionService.submit(task);
submittedTasks.incrementAndGet();
future.setListener(listener);
}
});
threads.add(submitter);
submitter.start();
// Run for 2 seconds
Thread.sleep(500);
// Close channel
channel2.close();
// Stop submitting
submitter.interrupt();
submitter.join();
// Wait for running tasks to finish
synchronized(this) {
int lastFinished=finishedTasks.get();
while(submittedTasks.get() > finishedTasks.get()) {
wait(10000);
if(lastFinished == finishedTasks.get()) {
assert false : "Tasks still outstanding, none finished in the last 10s";
}
lastFinished=finishedTasks.get();
}
}
Assert.assertEquals(submittedTasks.get(),finishedTasks.get(),"Tasks not finished");
}
private static final class Wait implements Callable<Void>, Serializable {
boolean wait=false;
Wait(boolean wait) {
super();
this.wait=wait;
}
@Override
public Void call() {
if(wait) {
try {
Thread.sleep(5000);
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
}
}