Java: ExecutorService thread synchronization with CountDownLatch causes dead lock? - java

I have written a game of life for programming practice. There are 3 different implementations of the generator. First: One main thread + N sub threads, Second: SwingWorker + N sub threads, Third: SwingWorker + ExecutorService.
N is the number of availableProcessors or user defined.
The first two implementations runs fine, with one and more threads.
The implementation with the ExecutorServise runs fine with one thread, but locks with more than one. I tried everything, but i can't get the solution.
Here the code of the fine workling implementation (second one):
package example.generator;
import javax.swing.SwingWorker;
/**
* AbstractGenerator implementation 2: SwingWorker + sub threads.
*
* #author Dima
*/
public final class WorldGenerator2 extends AbstractGenerator {
/**
* Constructor.
* #param gamePanel The game panel
*/
public WorldGenerator2() {
super();
}
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startGenerationProcess()
*/
#Override
protected void startGenerationProcess() {
final SwingWorker<Void, Void> worker = this.createWorker();
worker.execute();
}
/**
* Creates a swing worker for the generation process.
* #return The swing worker
*/
private SwingWorker<Void, Void> createWorker() {
return new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws InterruptedException {
WorldGenerator2.this.generationProcessing();
return null;
}
};
}
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startFirstStep()
*/
#Override
public void startFirstStep() throws InterruptedException {
this.getQueue().addAll(this.getLivingCells());
for (int i = 0; i < this.getCoresToUse(); i++) {
final Thread thread = new Thread() {
#Override
public void run() {
WorldGenerator2.this.fistStepProcessing();
}
};
thread.start();
thread.join();
}
}
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startSecondStep()
*/
#Override
protected void startSecondStep() throws InterruptedException {
this.getQueue().addAll(this.getCellsToCheck());
for (int i = 0; i < this.getCoresToUse(); i++) {
final Thread thread = new Thread() {
#Override
public void run() {
WorldGenerator2.this.secondStepProcessing();
}
};
thread.start();
thread.join();
}
}
}
Here is the code of the not working implementation with executor service:
package example.generator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.SwingWorker;
/**
* AbstractGenerator implementation 3: SwingWorker + ExecutorService.
*
* #author Dima
*/
public final class WorldGenerator3 extends AbstractGenerator {
private CountDownLatch countDownLatch;
private ExecutorService executor;
/**
* Constructor.
* #param gamePanel The game panel
*/
public WorldGenerator3() {
super();
}
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startGenerationProcess()
*/
#Override
protected void startGenerationProcess() {
this.executor = Executors.newFixedThreadPool(this.getCoresToUse());
final SwingWorker<Void, Void> worker = this.createWorker();
worker.execute();
}
/**
* Creates a swing worker for the generation process.
* #return The swing worker
*/
private SwingWorker<Void, Void> createWorker() {
return new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws InterruptedException {
WorldGenerator3.this.generationProcessing();
return null;
}
};
}
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startFirstStep()
*/
#Override
public void startFirstStep() throws InterruptedException {
this.getQueue().addAll(this.getLivingCells());
this.countDownLatch = new CountDownLatch(this.getCoresToUse());
for (int i = 0; i < this.getCoresToUse(); i++) {
this.executor.execute(new Runnable() {
#Override
public void run() {
WorldGenerator3.this.fistStepProcessing();
WorldGenerator3.this.countDownLatch.countDown();
}
});
}
this.countDownLatch.await();
}
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startSecondStep()
*/
#Override
protected void startSecondStep() throws InterruptedException {
this.getQueue().addAll(this.getCellsToCheck());
this.countDownLatch = new CountDownLatch(this.getCoresToUse());
for (int i = 0; i < this.getCoresToUse(); i++) {
this.executor.execute(new Runnable() {
#Override
public void run() {
WorldGenerator3.this.secondStepProcessing();
WorldGenerator3.this.countDownLatch.countDown();
}
});
}
this.countDownLatch.await();
}
}
Here you can download, a sample of my application, with a small launcher. it prints only the result of a iteration on the console: Link
Now my code looks like this:
/* (non-Javadoc)
* #see main.generator.AbstractGenerator#startFirstStep()
*/
#Override
public void startFirstStep() throws InterruptedException {
this.getQueue().addAll(this.getLivingCells());
final ArrayList<Callable<Void>> list = new ArrayList<Callable<Void>>(this.getCoresToUse());
for (int i = 0; i < this.getCoresToUse(); i++) {
list.add(new Callable<Void>() {
#Override
public Void call() throws Exception {
WorldGenerator3.this.fistStepProcessing();
return null;
}
}
);
}
this.executor.invokeAll(list);
}
But here is again the same problem. If I run it with one core (thread) there are no problems. If I set the number of cores to more than one, it locks. In my first question there is a link to a example, which you can run (in eclipse). Maybe I overlook something in the previous code.

I find your usage of Executors facilities a little bit odd...
I.e. the idea is to have Executor with a pool of threads, size of which usually is related to number of cores your CPU supports.
Then you submit whatever number of parallel tasks to the Executor, letting it to decide what to execute when and on which available Thread from its pool.
As for the CountDownLatch... Why not use ExecutorService.invokeAll? This method will block untill all submitted tasks are completed or timeout is reached. So it will do counting of the work left on your behalf.
Or a CompletionService which "decouples the production of new asynchronous tasks from the consumption of the results of completed tasks" if you want to consume Task result as soon as it becomes available i.e. not wait for all tasks to complete first.
Something like
private static final int WORKER_THREAD_COUNT_DEFAULT = Runtime.getRuntime().availableProcessors() * 2;
ExecutorService executor = Executors.newFixedThreadPool(WORKER_THREAD_COUNT);
// your tasks may or may not return result so consuming invokeAll return value may not be necessary in your case
List<Future<T>> futuresResult = executor.invokeAll(tasksToRunInParallel, EXECUTE_TIMEOUT,
TimeUnit.SECONDS);

In all variants you are executing threads in serial rather than parallel because you join and await inside the for-loop. That means that the for-loop cannot move on to the next iteration until the thread just started is complete. This amounts to having only one thread live at any given time -- either the main thread or the one thread created in the current loop iteration. If you want to join on multiple threads, you must collect the refs to them and then, outside the loop where you started them all, enter another loop where you join on each one.
As for using CountDownLatch in the Executors variant, what was said for threads goes for the latch here: don't use an instance var; use a local list that collects all latches and await them in a separate loop.
But, you shouldn't really be using the CountDownLatch in the first place: you should put all your parallel tasks in a list of Callables and call ExecutorService.invokeAll with it. It will automatically block until all the tasks are done.

Related

Does runnable run() create a thread everytime I call it?

I wrote a class which I use like as follows:
EventWrapperBuilder.newWrapperBuilder().
addSync(this::<some_method>).
addSync(this::<some_method>).
addSync(this::<some_method>).
addAsync(() -> <some_method>, Duration.ofSeconds(10)).
GET();
My code is the following:
private final List<EventWrapper> _wrappers = new ArrayList<>();
public EventWrapperBuilder addSync(final Runnable task)
{
_wrappers.add(new EventWrapper(task, Duration.ZERO));
return this;
}
public EventWrapperBuilder addAsync(final Runnable task, final Duration duration)
{
_wrappers.add(new EventWrapper(task, duration));
return this;
}
/**
* #return {#code List} of all {#code Future}
*/
public List<Future<?>> GET()
{
final List<Future<?>> list = new ArrayList<>();
for (final EventWrapper wrapper : getWrappers())
{
if (!wrapper.getDuration().isZero())
{
list.add(ThreadPoolManager.getInstance().scheduleEvent(wrapper.getTask(), wrapper.getDuration().toMillis()));
}
else
{
wrapper.getTask().run();
}
}
return list;
}
/**
* #param builder
* #return {#code EventWrapperBuilder}
*/
public EventWrapperBuilder COMBINE(final EventWrapperBuilder builder)
{
_wrappers.addAll(builder.getWrappers());
return this;
}
/**
* #return {#code List} of all {#code EventWrapper}
*/
public List<EventWrapper> getWrappers()
{
return _wrappers;
}
//#formatter:off
private static record EventWrapper (Runnable getTask, Duration getDuration) {}
//#formatter:on
public static EventWrapperBuilder newWrapperBuilder()
{
return new EventWrapperBuilder();
}
My question is: Do I create a new thread every time I execute this if it's instant i.e. the EventWrappers duration is zero?
I obviously know that the
list.add(ThreadPoolManager.getInstance().scheduleEvent(wrapper.getTask(), wrapper.getDuration().toMillis()));
creates a thread and execute it after the scheduled time but the
wrapper.getTask().run();
is real time without a thread right?
I don't want my code to create threads and therefore to be heavy when it executes run().
No, calling run() of the Runnable interface will not spawn a new thread.
On the other hand, when you wrap the Runnable with a Thread class and call start() then the JVM will spawn a new thread and execute the Runnable in it's context.
In your code, only the Async tasks will run in a separate thread since the Threadpool is managing their execution.
In your code, calling run() will execute the Runnable in the current thread, while calling start() will run the Runnable in the new thread.
https://docs.oracle.com/javase/7/docs/api/java/lang/Runnable.html#run()
https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#start()

Java FX Platform.runLater(() -> equivalent for long running Tasks

I learned that in JavaFX the equivalent of
SwingUtilities.invokeLater(new Runnable() {
public void run() {
dosomething();
}
});
might simply be
Platform.runLater(() ->{ dosomething()};
for a long running task I learned that you need to wrap things with a Task like:
Task<Void> task = new Task<Void>() {
#Override
public Void call() {
dosomething();
}
};
new Thread(task).start();
Now it would be great to be able to have a similar lambda shortcut like
TaskLaunch.start(() -> dosomething());
I found
JAVA FX - Lambda for Task interface
Swing timer alternative for JavaFX and the thread management difference
Thread with Lambda expression
discussing some of the issues around this and tried:
package com.bitplan.task.util;
import java.util.concurrent.Callable;
import javafx.concurrent.Task;
/**
* this is a utility task to launch tasks with lambda expressions
*
* #author wf
*
*/
public class TaskLaunch {
/**
*
* #param callable
* #return the new task
*/
public static <T> Task<T> task(Callable<T> callable) {
Task<T> task = new Task<T>() {
#Override
public T call() throws Exception {
return callable.call();
}
};
return task;
}
}
with a JUnit test:
Integer counter=0;
boolean running=false;
public Integer increment() {
running=true;
while (running) {
counter++;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
}
return counter;
}
/**
* #throws Exception
*/
#Test
public void testTaskLaunch() throws Exception {
// https://stackoverflow.com/questions/30089593/java-fx-lambda-for-task-interface
Task<Integer> task=TaskLaunch.task(() -> increment());
try {
Thread.sleep(20);
} catch (InterruptedException e) {
//
}
running=false;
assertTrue(task.get()>10);
}
Which doesn't quite do what I'd like to see yet. The issue seems to be that
the lambda expression runs in the same Thread and the
new Thread(task).start();
part needs to be integrated.
What is needed to get (at least close to) the short one liner mentioned above?
Is a
TaskLaunch.start(() -> dosomething());
feasible?
based on #Damianos proposal https://stackoverflow.com/a/44817217/1497139
I tried:
package com.bitplan.task;
import java.util.concurrent.Callable;
import javafx.concurrent.Task;
/**
* this is a utility task to launch tasks with lambda expressions
*
* #author wf
*
*/
public class TaskLaunch<T> {
Thread thread;
Task<T> task;
Callable<T> callable;
Throwable throwable;
Class<T> clazz;
public Thread getThread() {
return thread;
}
public void setThread(Thread thread) {
this.thread = thread;
}
public Task<T> getTask() {
return task;
}
public void setTask(Task<T> task) {
this.task = task;
}
public Callable<T> getCallable() {
return callable;
}
public void setCallable(Callable<T> callable) {
this.callable = callable;
}
public Throwable getThrowable() {
return throwable;
}
public void setThrowable(Throwable throwable) {
this.throwable = throwable;
}
public Class<T> getClazz() {
return clazz;
}
public void setClazz(Class<T> clazz) {
this.clazz = clazz;
}
/**
* construct me from a callable
*
* #param callable
*/
public TaskLaunch(Callable<T> callable, Class<T> clazz) {
this.callable = callable;
this.task = task(callable);
this.clazz = clazz;
}
/**
*
* #param callable
* #return the new task
*/
public static <T> Task<T> task(Callable<T> callable) {
Task<T> task = new Task<T>() {
#Override
public T call() throws Exception {
return callable.call();
}
};
return task;
}
/**
* start
*/
public void start() {
thread = new Thread(task);
thread.start();
}
/**
* start the given callable
* #param callable
* #param clazz - the return Type class
* #return - the launch result
*/
#SuppressWarnings({ "unchecked", "rawtypes" })
public static TaskLaunch start(Callable<?> callable, Class<?> clazz) {
TaskLaunch<?> launch = new TaskLaunch(callable, clazz);
launch.start();
return launch;
}
}
and changed the test to:
/**
* #throws Exception
*/
#SuppressWarnings("unchecked")
#Test
public void testTaskLaunch() throws Exception {
// https://stackoverflow.com/questions/30089593/java-fx-lambda-for-task-interface
TaskLaunch<Integer> launch = TaskLaunch.start(()->increment(),Integer.class);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
//
}
running=false;
assertTrue(launch.getTask().get()>10);
}
This is close to what i am up to but I get:
java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:273)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:268)
at javafx.application.Platform.runLater(Platform.java:83)
at javafx.concurrent.Task.runLater(Task.java:1225)
at javafx.concurrent.Task$TaskCallable.call(Task.java:1417)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.lang.Thread.run(Thread.java:745)
At least TaskLaunch now wraps:
Thread
task
callable
a potential Exception/Throwable
the runtime class of the result of the Task
Some of these 5 items might be redundant and available from the standard java concepts. I think at least its handy to have quick access to these after running things from a one liner.
Hope this gets to a working state and thanks for the help!
Just new Thread(() -> dosomething()).start() should do the trick
This is sort of a traditional XY problem.
A Task is much more than just a background thread, hence for this you can use regular threads. It's the beauty of the properties!
The real benefit of using Task is that all state changes and progress updates can safely be observed and bound to a live scene, while doing all the background work on a different thread. It's the work of the class to do the heavy-lifting and call Platform.runLater.
The reason you need a subclass and not a runnable is so you can call its protected updateXxx() methods without worrying for threading issues.
With this said, you'll have no benefit if this would've been a single line code. For this use simple threads.
Hope this helps.
Doing this will cause you to lose the ability to update stuff back to the UI thread natively supported by Task class. On the other hand, I do agree this can be useful if you want to do something in background in "do-and-forget" style.
The problem is just like what you said - you didn't add new Thead() and Thread.start() in. Do this:
public static void runInBackground(Runnable runnable) {
Task<Void> task = new Task<>() {
#Override
public Void call() throws Exception {
runnable.run();
return null;
}
};
new Thead(task).start();
}
runInBackground(() -> System.out.println(Thread.currentThread().hashCode()));
Note that your Task can no longer be non-void, because it cannot return anything back now. Your lambda needs to be able to reference the Task object to return a result asynchronously - that is never going to be possible using lambda.
The answer is now in the question based on Damianos hint.
The workaround for the exception I found is
com.sun.javafx.application.PlatformImpl.startup(() -> {
});
But seems a little bit hacky ...

How to use LinkedBlockingQueue to run tasks

I am currently trying to get my code to print what is in the method of simpleTask 20 times using the code in simpleTesting. The idea is that simpleTesting adds 20 instances of simpleTask to a queue then they are taken from the queue in simplePoolThread. What should happen is that it prints out the testing message 20 times then continues to run while looking for more things from the queue (but there are none). Instead it is currently just not printing anything and continuously running. Here is my code (a lot of it is interfaces, I believe the problem lies in the simpleThreadPool code):
package simpleThreadPool;
/**
* <<-- Pool Thread -->>
*
* It will be running continuously. It will try to retrieve new tasks when it is idle.
*/
public interface ISimplePoolThread extends Runnable {
/**
* Use an infinite loop to retrieve and perform tasks.
*/
#Override
public void run();
}
.
package simpleThreadPool;
/**
* <<-- Simple Task -->>
*
* ISimpleTask is to be performed by PoolThread.
*/
public interface ISimpleTask{
/**
* #1. Create a class to implement ISimpleTask, put content of the task to method run().
*/
public void run();
}
.
package simpleThreadPool;
/**
* <<-- Thread Pool -->>
* It manages a queue of tasks, starts some pool threads.
*
* #1. Create a task queue by using queue data structures, or designing your own data structure.
*/
public interface ISimpleThreadPool {
/**
* #1. Initialize your queue (or do so in somewhere)
* #2. Starts some ISimplePoolThreads.
*/
public void start();
/**
* #1. Stops everything
*/
public void stop();
/**
* #1. Add a task to your queue.
*/
public void addTask(ISimpleTask task);
}
.
package simpleThreadPool;
public class SimpleTask implements ISimpleTask {
#Override
public void run() {
System.out.println("testing testing 1 2 3");
}
}
.
I think the problem lies in this piece of code, where the tasks are taken from the queue:
package simpleThreadPool;
import java.util.concurrent.LinkedBlockingQueue;
public class SimplePoolThread implements ISimplePoolThread, Runnable {
private LinkedBlockingQueue<ISimpleTask> queue = new LinkedBlockingQueue<>();
#Override
public void run() {
while(true) {
System.out.println("Inserting Element: ");
try {
queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
.
package simpleThreadPool;
import java.util.concurrent.LinkedBlockingQueue;
public class SimpleThreadPool implements ISimpleThreadPool {
private LinkedBlockingQueue<ISimpleTask> queue = new LinkedBlockingQueue<>();
#Override
public void start() {
(new Thread(new SimplePoolThread())).start();
}
#Override
public void stop() {
try {
queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void addTask(ISimpleTask task) {
try {
queue.put(task);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
.
the testing file:
package simpleThreadPool;
public class SimpleTesting implements ISimpleTask{
private int i;
public SimpleTesting(int i){
this.i = i;
}
#Override
public void run() {
System.out.println(i);
}
public static void main(String args[]){
// Initialize thread pool
SimpleThreadPool pool = new SimpleThreadPool();
pool.start();
// Create 20 tasks
for(int i = 1; i<=20; i++){
pool.addTask(new SimpleTesting(i));
}
}
}
The task queue in your SimplePoolThread is a blocking queue. As soon as it starts it executes queue.take(). Take is a blocking operation. The thread sits there waiting forever until something else adds a task into the queue.
Your hunch on the problem location was pretty close. The issue is that the queue in SimplePoolThread and the queue in SimpleThreadPool are not the same; you have two separate queues. So when SimpleTesting adds tasks they go into Pool's queue and NOT into the Thread's queue. So the thread will sit there forever waiting on nothing. You also forgot to actually run your tasks inside of SimplePoolThread.
Try the following instead.
public class SimpleThreadPool implements ISimpleThreadPool {
private LinkedBlockingQueue<ISimpleTask> queue = new LinkedBlockingQueue<>();
#Override
public void start() {
(new Thread(new SimplePoolThread(queue))).start();
}
Note that the queue from the pool was passed into the thread. The thread then keeps a reference to this queue. During the thread's run() it now also actually runs the task.
public class SimplePoolThread implements ISimplePoolThread, Runnable {
private LinkedBlockingQueue<ISimpleTask> queue;
public SimplePoolThread(LinkedBlockingQueue<ISimpleTask> queue)
{
this.queue = queue;
}
#Override
public void run() {
while(true) {
System.out.println("Inserting Element: ");
try {
ISimpleTask task = queue.take();
task.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Here's the output:
Inserting Element:
1
Inserting Element:
2
Inserting Element:
3
..etc..
I assume this is for homework otherwise I would tell you not to reinvent the wheel and go use Java's built-in pooling services.
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html

Resettable CountdownLatch

I need something which is directly equivalent to CountDownLatch, but is resettable (remaining thread-safe!). I can't use classic synchronisation constructs as they simply don't work in this situation (complex locking issues). At the moment, I'm creating many CountDownLatch objects, each replacing the previous one. I believe this is doing in the young generation in the GC (due to the sheer number of objects). You can see the code which uses the latches below (it's part of the java.net mock for a ns-3 network simulator interface).
Some ideas might be to try CyclicBarrier (JDK5+) or Phaser (JDK7)
I can test code and get back to anyone that finds a solution to this problem, since I'm the only one who can insert it into the running system to see what happens :)
/**
*
*/
package kokunet;
import java.io.IOException;
import java.nio.channels.ClosedSelectorException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import kokuks.IConnectionSocket;
import kokuks.KKSAddress;
import kokuks.KKSSocket;
import kokuks.KKSSocketListener;
/**
* KSelector
* #version 1.0
* #author Chris Dennett
*/
public class KSelector extends SelectorImpl {
// True if this Selector has been closed
private volatile boolean closed = false;
// Lock for close and cleanup
final class CloseLock {}
private final Object closeLock = new CloseLock();
private volatile boolean selecting = false;
private volatile boolean wakeup = false;
class SocketListener implements KKSSocketListener {
protected volatile CountDownLatch latch = null;
/**
*
*/
public SocketListener() {
newLatch();
}
protected synchronized CountDownLatch newLatch() {
return this.latch = new CountDownLatch(1);
}
protected synchronized void refreshReady(KKSSocket socket) {
if (!selecting) return;
synchronized (socketToChannel) {
SelChImpl ch = socketToChannel.get(socket);
if (ch == null) {
System.out.println("ks sendCB: channel not found for socket: " + socket);
return;
}
synchronized (channelToKey) {
SelectionKeyImpl sk = channelToKey.get(ch);
if (sk != null) {
if (handleSelect(sk)) {
latch.countDown();
}
}
}
}
}
#Override
public void connectionSucceeded(KKSSocket socket) {
refreshReady(socket);
}
#Override
public void connectionFailed(KKSSocket socket) {
refreshReady(socket);
}
#Override
public void dataSent(KKSSocket socket, long bytesSent) {
refreshReady(socket);
}
#Override
public void sendCB(KKSSocket socket, long bytesAvailable) {
refreshReady(socket);
}
#Override
public void onRecv(KKSSocket socket) {
refreshReady(socket);
}
#Override
public void newConnectionCreated(KKSSocket socket, KKSSocket newSocket, KKSAddress remoteaddress) {
refreshReady(socket);
}
#Override
public void normalClose(KKSSocket socket) {
wakeup();
}
#Override
public void errorClose(KKSSocket socket) {
wakeup();
}
}
protected final Map<KKSSocket, SelChImpl> socketToChannel = new HashMap<KKSSocket, SelChImpl>();
protected final Map<SelChImpl, SelectionKeyImpl> channelToKey = new HashMap<SelChImpl, SelectionKeyImpl>();
protected final SocketListener currListener = new SocketListener();
protected Thread selectingThread = null;
SelChImpl getChannelForSocket(KKSSocket s) {
synchronized (socketToChannel) {
return socketToChannel.get(s);
}
}
SelectionKeyImpl getSelKeyForChannel(KKSSocket s) {
synchronized (channelToKey) {
return channelToKey.get(s);
}
}
protected boolean markRead(SelectionKeyImpl impl) {
synchronized (impl) {
if (!impl.isValid()) return false;
impl.nioReadyOps(impl.readyOps() | SelectionKeyImpl.OP_READ);
return selectedKeys.add(impl);
}
}
protected boolean markWrite(SelectionKeyImpl impl) {
synchronized (impl) {
if (!impl.isValid()) return false;
impl.nioReadyOps(impl.readyOps() | SelectionKeyImpl.OP_WRITE);
return selectedKeys.add(impl);
}
}
protected boolean markAccept(SelectionKeyImpl impl) {
synchronized (impl) {
if (!impl.isValid()) return false;
impl.nioReadyOps(impl.readyOps() | SelectionKeyImpl.OP_ACCEPT);
return selectedKeys.add(impl);
}
}
protected boolean markConnect(SelectionKeyImpl impl) {
synchronized (impl) {
if (!impl.isValid()) return false;
impl.nioReadyOps(impl.readyOps() | SelectionKeyImpl.OP_CONNECT);
return selectedKeys.add(impl);
}
}
/**
* #param provider
*/
protected KSelector(SelectorProvider provider) {
super(provider);
}
/* (non-Javadoc)
* #see kokunet.SelectorImpl#implClose()
*/
#Override
protected void implClose() throws IOException {
provider().getApp().printMessage("implClose: closed: " + closed);
synchronized (closeLock) {
if (closed) return;
closed = true;
for (SelectionKey sk : keys) {
provider().getApp().printMessage("dereg1");
deregister((AbstractSelectionKey)sk);
provider().getApp().printMessage("dereg2");
SelectableChannel selch = sk.channel();
if (!selch.isOpen() && !selch.isRegistered())
((SelChImpl)selch).kill();
}
implCloseInterrupt();
}
}
protected void implCloseInterrupt() {
wakeup();
}
private boolean handleSelect(SelectionKey k) {
synchronized (k) {
boolean notify = false;
if (!k.isValid()) {
k.cancel();
((SelectionKeyImpl)k).channel.socket().removeListener(currListener);
return false;
}
SelectionKeyImpl ski = (SelectionKeyImpl)k;
if ((ski.interestOps() & SelectionKeyImpl.OP_READ) != 0) {
if (ski.channel.socket().getRxAvailable() > 0) {
notify |= markRead(ski);
}
}
if ((ski.interestOps() & SelectionKeyImpl.OP_WRITE) != 0) {
if (ski.channel.socket().getTxAvailable() > 0) {
notify |= markWrite(ski);
}
}
if ((ski.interestOps() & SelectionKeyImpl.OP_CONNECT) != 0) {
if (!ski.channel.socket().isConnectionless()) {
IConnectionSocket cs = (IConnectionSocket)ski.channel.socket();
if (!ski.channel.socket().isAccepting() && !cs.isConnecting() && !cs.isConnected()) {
notify |= markConnect(ski);
}
}
}
if ((ski.interestOps() & SelectionKeyImpl.OP_ACCEPT) != 0) {
//provider().getApp().printMessage("accept check: ski: " + ski + ", connectionless: " + ski.channel.socket().isConnectionless() + ", listening: " + ski.channel.socket().isListening() + ", hasPendingConn: " + (ski.channel.socket().isConnectionless() ? "nope!" : ((IConnectionSocket)ski.channel.socket()).hasPendingConnections()));
if (!ski.channel.socket().isConnectionless() && ski.channel.socket().isListening()) {
IConnectionSocket cs = (IConnectionSocket)ski.channel.socket();
if (cs.hasPendingConnections()) {
notify |= markAccept(ski);
}
}
}
return notify;
}
}
private boolean handleSelect() {
boolean notify = false;
// get initial status
for (SelectionKey k : keys) {
notify |= handleSelect(k);
}
return notify;
}
/* (non-Javadoc)
* #see kokunet.SelectorImpl#doSelect(long)
*/
#Override
protected int doSelect(long timeout) throws IOException {
processDeregisterQueue();
long timestartedms = System.currentTimeMillis();
synchronized (selectedKeys) {
synchronized (currListener) {
wakeup = false;
selectingThread = Thread.currentThread();
selecting = true;
}
try {
handleSelect();
if (!selectedKeys.isEmpty() || timeout == 0) {
return selectedKeys.size();
}
//TODO: useless op if we have keys available
for (SelectionKey key : keys) {
((SelectionKeyImpl)key).channel.socket().addListener(currListener);
}
try {
while (!wakeup && isOpen() && selectedKeys.isEmpty()) {
CountDownLatch latch = null;
synchronized (currListener) {
if (wakeup || !isOpen() || !selectedKeys.isEmpty()) {
break;
}
latch = currListener.newLatch();
}
try {
if (timeout > 0) {
long currtimems = System.currentTimeMillis();
long remainingMS = (timestartedms + timeout) - currtimems;
if (remainingMS > 0) {
latch.await(remainingMS, TimeUnit.MILLISECONDS);
} else {
break;
}
} else {
latch.await();
}
} catch (InterruptedException e) {
}
}
return selectedKeys.size();
} finally {
for (SelectionKey key : keys) {
((SelectionKeyImpl)key).channel.socket().removeListener(currListener);
}
}
} finally {
synchronized (currListener) {
selecting = false;
selectingThread = null;
wakeup = false;
}
}
}
}
/* (non-Javadoc)
* #see kokunet.SelectorImpl#implRegister(kokunet.SelectionKeyImpl)
*/
#Override
protected void implRegister(SelectionKeyImpl ski) {
synchronized (closeLock) {
if (closed) throw new ClosedSelectorException();
synchronized (channelToKey) {
synchronized (socketToChannel) {
keys.add(ski);
socketToChannel.put(ski.channel.socket(), ski.channel);
channelToKey.put(ski.channel, ski);
}
}
}
}
/* (non-Javadoc)
* #see kokunet.SelectorImpl#implDereg(kokunet.SelectionKeyImpl)
*/
#Override
protected void implDereg(SelectionKeyImpl ski) throws IOException {
synchronized (channelToKey) {
synchronized (socketToChannel) {
keys.remove(ski);
socketToChannel.remove(ski.channel.socket());
channelToKey.remove(ski.channel);
SelectableChannel selch = ski.channel();
if (!selch.isOpen() && !selch.isRegistered())
((SelChImpl)selch).kill();
}
}
}
/* (non-Javadoc)
* #see kokunet.SelectorImpl#wakeup()
*/
#Override
public Selector wakeup() {
synchronized (currListener) {
if (selecting) {
wakeup = true;
selecting = false;
selectingThread.interrupt();
selectingThread = null;
}
}
return this;
}
}
Cheers,
Chris
I copied CountDownLatch and implemented a reset() method that resets the internal Sync class to its initial state (starting count) :) Appears to work fine. No more unnecessary object creation \o/ It was not possible to subclass because sync was private. Boo.
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
/**
* A synchronization aid that allows one or more threads to wait until
* a set of operations being performed in other threads completes.
*
* <p>A {#code CountDownLatch} is initialized with a given <em>count</em>.
* The {#link #await await} methods block until the current count reaches
* zero due to invocations of the {#link #countDown} method, after which
* all waiting threads are released and any subsequent invocations of
* {#link #await await} return immediately. This is a one-shot phenomenon
* -- the count cannot be reset. If you need a version that resets the
* count, consider using a {#link CyclicBarrier}.
*
* <p>A {#code CountDownLatch} is a versatile synchronization tool
* and can be used for a number of purposes. A
* {#code CountDownLatch} initialized with a count of one serves as a
* simple on/off latch, or gate: all threads invoking {#link #await await}
* wait at the gate until it is opened by a thread invoking {#link
* #countDown}. A {#code CountDownLatch} initialized to <em>N</em>
* can be used to make one thread wait until <em>N</em> threads have
* completed some action, or some action has been completed N times.
*
* <p>A useful property of a {#code CountDownLatch} is that it
* doesn't require that threads calling {#code countDown} wait for
* the count to reach zero before proceeding, it simply prevents any
* thread from proceeding past an {#link #await await} until all
* threads could pass.
*
* <p><b>Sample usage:</b> Here is a pair of classes in which a group
* of worker threads use two countdown latches:
* <ul>
* <li>The first is a start signal that prevents any worker from proceeding
* until the driver is ready for them to proceed;
* <li>The second is a completion signal that allows the driver to wait
* until all workers have completed.
* </ul>
*
* <pre>
* class Driver { // ...
* void main() throws InterruptedException {
* CountDownLatch startSignal = new CountDownLatch(1);
* CountDownLatch doneSignal = new CountDownLatch(N);
*
* for (int i = 0; i < N; ++i) // create and start threads
* new Thread(new Worker(startSignal, doneSignal)).start();
*
* doSomethingElse(); // don't let run yet
* startSignal.countDown(); // let all threads proceed
* doSomethingElse();
* doneSignal.await(); // wait for all to finish
* }
* }
*
* class Worker implements Runnable {
* private final CountDownLatch startSignal;
* private final CountDownLatch doneSignal;
* Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
* this.startSignal = startSignal;
* this.doneSignal = doneSignal;
* }
* public void run() {
* try {
* startSignal.await();
* doWork();
* doneSignal.countDown();
* } catch (InterruptedException ex) {} // return;
* }
*
* void doWork() { ... }
* }
*
* </pre>
*
* <p>Another typical usage would be to divide a problem into N parts,
* describe each part with a Runnable that executes that portion and
* counts down on the latch, and queue all the Runnables to an
* Executor. When all sub-parts are complete, the coordinating thread
* will be able to pass through await. (When threads must repeatedly
* count down in this way, instead use a {#link CyclicBarrier}.)
*
* <pre>
* class Driver2 { // ...
* void main() throws InterruptedException {
* CountDownLatch doneSignal = new CountDownLatch(N);
* Executor e = ...
*
* for (int i = 0; i < N; ++i) // create and start threads
* e.execute(new WorkerRunnable(doneSignal, i));
*
* doneSignal.await(); // wait for all to finish
* }
* }
*
* class WorkerRunnable implements Runnable {
* private final CountDownLatch doneSignal;
* private final int i;
* WorkerRunnable(CountDownLatch doneSignal, int i) {
* this.doneSignal = doneSignal;
* this.i = i;
* }
* public void run() {
* try {
* doWork(i);
* doneSignal.countDown();
* } catch (InterruptedException ex) {} // return;
* }
*
* void doWork() { ... }
* }
*
* </pre>
*
* <p>Memory consistency effects: Actions in a thread prior to calling
* {#code countDown()}
* <i>happen-before</i>
* actions following a successful return from a corresponding
* {#code await()} in another thread.
*
* #since 1.5
* #author Doug Lea
*/
public class ResettableCountDownLatch {
/**
* Synchronization control For CountDownLatch.
* Uses AQS state to represent count.
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
public final int startCount;
Sync(int count) {
this.startCount = count;
setState(startCount);
}
int getCount() {
return getState();
}
public int tryAcquireShared(int acquires) {
return getState() == 0? 1 : -1;
}
public boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
public void reset() {
setState(startCount);
}
}
private final Sync sync;
/**
* Constructs a {#code CountDownLatch} initialized with the given count.
*
* #param count the number of times {#link #countDown} must be invoked
* before threads can pass through {#link #await}
* #throws IllegalArgumentException if {#code count} is negative
*/
public ResettableCountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
/**
* Causes the current thread to wait until the latch has counted down to
* zero, unless the thread is {#linkplain Thread#interrupt interrupted}.
*
* <p>If the current count is zero then this method returns immediately.
*
* <p>If the current count is greater than zero then the current
* thread becomes disabled for thread scheduling purposes and lies
* dormant until one of two things happen:
* <ul>
* <li>The count reaches zero due to invocations of the
* {#link #countDown} method; or
* <li>Some other thread {#linkplain Thread#interrupt interrupts}
* the current thread.
* </ul>
*
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
* <li>is {#linkplain Thread#interrupt interrupted} while waiting,
* </ul>
* then {#link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* #throws InterruptedException if the current thread is interrupted
* while waiting
*/
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void reset() {
sync.reset();
}
/**
* Causes the current thread to wait until the latch has counted down to
* zero, unless the thread is {#linkplain Thread#interrupt interrupted},
* or the specified waiting time elapses.
*
* <p>If the current count is zero then this method returns immediately
* with the value {#code true}.
*
* <p>If the current count is greater than zero then the current
* thread becomes disabled for thread scheduling purposes and lies
* dormant until one of three things happen:
* <ul>
* <li>The count reaches zero due to invocations of the
* {#link #countDown} method; or
* <li>Some other thread {#linkplain Thread#interrupt interrupts}
* the current thread; or
* <li>The specified waiting time elapses.
* </ul>
*
* <p>If the count reaches zero then the method returns with the
* value {#code true}.
*
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
* <li>is {#linkplain Thread#interrupt interrupted} while waiting,
* </ul>
* then {#link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* <p>If the specified waiting time elapses then the value {#code false}
* is returned. If the time is less than or equal to zero, the method
* will not wait at all.
*
* #param timeout the maximum time to wait
* #param unit the time unit of the {#code timeout} argument
* #return {#code true} if the count reached zero and {#code false}
* if the waiting time elapsed before the count reached zero
* #throws InterruptedException if the current thread is interrupted
* while waiting
*/
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/**
* Decrements the count of the latch, releasing all waiting threads if
* the count reaches zero.
*
* <p>If the current count is greater than zero then it is decremented.
* If the new count is zero then all waiting threads are re-enabled for
* thread scheduling purposes.
*
* <p>If the current count equals zero then nothing happens.
*/
public void countDown() {
sync.releaseShared(1);
}
/**
* Returns the current count.
*
* <p>This method is typically used for debugging and testing purposes.
*
* #return the current count
*/
public long getCount() {
return sync.getCount();
}
/**
* Returns a string identifying this latch, as well as its state.
* The state, in brackets, includes the String {#code "Count ="}
* followed by the current count.
*
* #return a string identifying this latch, as well as its state
*/
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
}
Phaser has more options, we can implement resettable countdownLatch using that.
Please read below basic concepts from the following sites
https://examples.javacodegeeks.com/core-java/util/concurrent/phaser/java-util-concurrent-phaser-example/
http://netjs.blogspot.in/2016/01/phaser-in-java-concurrency.html
import java.util.concurrent.Phaser;
/**
* Resettable countdownLatch using phaser
*/
public class PhaserExample {
public static void main(String[] args) throws InterruptedException {
Phaser phaser = new Phaser(3); // you can use constructor hint or
// register() or mixture of both
// register self... so parties are incremented to 4 (3+1) now
phaser.register();
//register is one time call for all the phases.
//means no need to register for every phase
int phasecount = phaser.getPhase();
System.out.println("Phasecount is " + phasecount);
new PhaserExample().testPhaser(phaser, 2000);
new PhaserExample().testPhaser(phaser, 4000);
new PhaserExample().testPhaser(phaser, 6000);
// similar to await() in countDownLatch/CyclicBarrier
// parties are decremented to 3 (4+1) now
phaser.arriveAndAwaitAdvance();
// once all the thread arrived at same level, barrier opens
System.out.println("Barrier has broken.");
phasecount = phaser.getPhase();
System.out.println("Phasecount is " + phasecount);
//second phase
new PhaserExample().testPhaser(phaser, 2000);
new PhaserExample().testPhaser(phaser, 4000);
new PhaserExample().testPhaser(phaser, 6000);
phaser.arriveAndAwaitAdvance();
// once all the thread arrived at same level, barrier opens
System.out.println("Barrier has broken.");
phasecount = phaser.getPhase();
System.out.println("Phasecount is " + phasecount);
}
private void testPhaser(final Phaser phaser, final int sleepTime) {
// phaser.register(); //Already constructor hint is given so not
// required
new Thread() {
#Override
public void run() {
try {
Thread.sleep(sleepTime);
System.out.println(Thread.currentThread().getName() + " arrived");
// phaser.arrive(); //similar to CountDownLatch#countDown()
phaser.arriveAndAwaitAdvance();// thread will wait till Barrier opens
// arriveAndAwaitAdvance is similar to CyclicBarrier#await()
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " after passing barrier");
}
}.start();
}
}
Based on #Fidel -s answer, I made a drop-in replacement for ResettableCountDownLatch. The changes I made
mLatch is private volatile
mInitialCount is private final
the return type of the simple await() has changed to void.
Otherwise, the original code is cool too. So, this is the full, enhanced code:
public class ResettableCountDownLatch {
private final int initialCount;
private volatile CountDownLatch latch;
public ResettableCountDownLatch(int count) {
initialCount = count;
latch = new CountDownLatch(count);
}
public void reset() {
latch = new CountDownLatch(initialCount);
}
public void countDown() {
latch.countDown();
}
public void await() throws InterruptedException {
latch.await();
}
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
return latch.await(timeout, unit);
}
}
Update
Based on #Systemplanet-s comment, here is a safer version of reset():
// An atomic reference is required because reset() is not that atomic anymore, not even with `volatile`.
private final AtomicReference<CountDownLatch> latchHolder = new AtomicReference<>();
public void reset() {
// obtaining a local reference for modifying the required latch
final CountDownLatch oldLatch = latchHolder.getAndSet(null);
if (oldLatch != null) {
// checking the count each time to prevent unnecessary countdowns due to parallel countdowns
while (0L < oldLatch.getCount()) {
oldLatch.countDown();
}
}
}
Basically, it's a choice between simplicity and safety. I.e. if you are willing to move the responsibility to the client of your code, then it's enough to set the reference null in reset().
On the other hand, if you want to make it easy for the users of this code, then you need to use a little more tricks.
I'm not sure if this is fatally flawed but I recently had the same problem and solved it by simply instantiating a new CountDownLatch object each time I wanted to reset. Something like this:
Waiter:
bla();
latch.await();
//now the latch has counted down to 0
blabla();
CountDowner
foo();
latch.countDown();
//now the latch has counted down to 0
latch = new CountDownLatch(1);
Waiter.receiveReferenceToNewLatch(latch);
bar();
Obviously this is a heavy abstraction but thus far it has worked for me and doesn't require you to tinker with any class definitions.
Use Phaser.
if only one thread should to do work. U can join AtomicBoolean and Phaser
AtomicBoolean someConditionInProgress = new AtomicBoolean("false"); Phaser onConditionalPhaser = new Phaser(1);
(some function) if (!someConditionInProgress.compareAndSet(false, true)) {
try {
//do something
} finally {
someConditionInProgress.set(false);
//release barier
onConditionalPhaser.arrive();
}
} else {
onConditionalPhaser.awaitAdvance(onConditionalPhaser.getPhase());
}
Looks like you want to turn asynchronous I/O to synchronous. The whole idea of using asynchronous I/O is to avoid threads, but CountDownLatch requres using threads. This is an obvious contradiction in your question. So, you can:
keep using threads and employ synchronous I/O instead of Selectors and the suff. This will be much more simple and reliable
keep using asynchronous I/0 and give up CountDownLatch. Then you need an asynchronous library - look at RxJava, Akka, or df4j.
continue to develop your project for fun. Then you can try to use java.util.Semaphore instead of CountDownLatch, or program your own synchronization class using synchronized/wait/notify.
public class ResettableLatch {
private static final class Sync extends AbstractQueuedSynchronizer {
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return getState() == 0 ? 1 : -1;
}
public void reset(int count) {
setState(count);
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c - 1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
private final Sync sync;
public ResettableLatch(int count) {
if (count < 0)
throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public void countDown() {
sync.releaseShared(1);
}
public long getCount() {
return sync.getCount();
}
public void reset(int count) {
sync.reset(count);
}
}
This worked for me.
From what I was able to understand from the OP explanation and source code, the resettable CountDownLatch is not quite adequate concept for the problem he was going to solve. The documentation of the CountDownLatch itself mentions the OP's use case as simple gate initialized with a count of one:
CountDownLatch initialized with a count of one serves as a simple
on/off latch, or gate: all threads invoking await wait at the gate
until it is opened by a thread invoking countDown.
, but CountDownLatch implementation does not go any further in this direction.
So, myself having a problem similar to that of OP's I decided to introduce a SimpleGate class with the following properties:
Number of permits is one, which means it can be either in On or Off state;
There is a dedicated thread, called Gate Keeper that is only allowed to shut off or open up the Gate;
The right of gate keeping is transferable;
the opening up the Gate immediately allows the threads, that tried to come through the Gate, to do it (this very logical feature has been overlooked in the other answers);
as the thread contention is expected to be high, fairness is supported as an option, this allows to decrease an effect of thread barging.
public class SimpleGate {
private static class Sync extends AbstractQueuedSynchronizer {
// State
private static final int SHUT = 1;
private static final int OPEN = 0;
private boolean fair;
public void setFair(boolean fair) {
this.fair = fair;
}
public void shutOff() {
super.setState(SHUT);
}
#Override
protected int tryAcquireShared(int arg) {
if (fair && super.hasQueuedPredecessors())
return -1;
return super.getState() == OPEN ? 1 : -1;
}
#Override
protected boolean tryReleaseShared(int arg) {
super.setState(OPEN);
return true;
}
}
private Sync sync = new Sync();
private volatile Thread gateKeeper = Thread.currentThread();
public SimpleGate(){
this(true);
}
public SimpleGate(boolean shutOff){
this(shutOff, false);
}
public SimpleGate(boolean shutOff, boolean fair){
if (shutOff)
sync.shutOff();
sync.setFair(fair);
}
public void comeThrough(){
if (Thread.currentThread() == gateKeeper)
throw new IllegalStateException("Gate Keeper thread is not supposed to come through the gate");
sync.acquireShared(0);
}
public void shutOff(){
if (Thread.currentThread() != gateKeeper)
throw new IllegalStateException("Only a Gate Keeper thread is allowed to shut off");
sync.shutOff();
}
public void openUp(){
if (Thread.currentThread() != gateKeeper)
throw new IllegalStateException("Only a Gate Keeper thread is allowed to open up");
sync.releaseShared(0);
}
public void transferOwnership(Thread newGateKeeper){
this.gateKeeper = newGateKeeper;
}
// an addition of waiting interruptibly and waiting for specified amount of time,
//if they are needed, is trivial
}
Another drop-in replacement
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class ResettableCountDownLatch {
int mInitialCount;
CountDownLatch mLatch;
public ResettableCountDownLatch(int count) {
mInitialCount = count;
mLatch = new CountDownLatch(count);
}
public void reset() {
mLatch = new CountDownLatch(mInitialCount);
}
public void countDown() {
mLatch.countDown();
}
public boolean await() throws InterruptedException {
boolean result = mLatch.await();
return result;
}
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
boolean result = mLatch.await(timeout, unit);
return result;
}
}

Detecting when a user is finished resizing SWT shell

I've got an SWT shell that's resizable. Every time it is resized, I have to do something computationally intensive.
I can register a ControlListener on my shell, but this generates events continuously throughout the resize operation, and I have no idea when a resize drag type mouse operation ends.
I'd like to be able to detect when the user is finished resizing the shell and then initiate my computationally intensive operation. Any ideas how to go about that?
How about using a timer and start your operation after a delay of say one sec since last received resize event?
A rough draft:
long lastEvent;
ActionListener taskPerformer = new ActionListener() {
public void doCalc(ActionEvent evt) {
if ( (lastEvent + 1000) < System.currentTimeMillis() ) {
hardcoreCalculationTask();
} else {
// this can be timed better
new Timer(1000, taskPerformer).start();
}
}
};
}
In your resize event:
lastEvent = System.currentTimeMillis();
new Timer(1000, taskPerformer).start();
The solution below was inspired by stacker's and is pretty much the same except that it uses only SWT API and also makes sure the mouse button is up before starting the CPU intensive task.
First the type that does the job:
private class ResizeListener implements ControlListener, Runnable, Listener {
private long lastEvent = 0;
private boolean mouse = true;
public void controlMoved(ControlEvent e) {
}
public void controlResized(ControlEvent e) {
lastEvent = System.currentTimeMillis();
Display.getDefault().timerExec(500, this);
}
public void run() {
if ((lastEvent + 500) < System.currentTimeMillis() && mouse) {
...work
} else {
Display.getDefault().timerExec(500, this);
}
}
public void handleEvent(Event event) {
mouse = event.type == SWT.MouseUp;
}
}
Then we need to register it. Also make sure to unregister when done. One may also want to change the component used for mouse listening in order to be little bit more specific.
ResizeListener listener = new ResizeListener();
widget.addControlListener(listener);
widget.getDisplay().addFilter(SWT.MouseDown, listener);
widget.getDisplay().addFilter(SWT.MouseUp, listener);
Here's an alternative suggestion for the same problem: [platform-swt-dev] Mouse resize listener:
You could try setting a flag and defering the resize work using Display.asyncExec(). When you get a resize, if the flag is set, just return. This should cause the resize work only when the UI is idle.
My instant idea was to listen to mouse up events but obviously (I just tried it), mouse events are not fired for mouse actions on the shell's border. Could be so damn easy...
I solved this problem in a generic way by creating an Executor that can "throttle" tasks.
Tasks (Runnables) are put into a DelayQueue, from where a Scheduler-Thread takes and executes them. The latest scheduled task is also remembered in a variable, so if the Scheduler retrieves a new task from the queue, he checks if this is the latest task that was scheduled. If so, he executes it, if not it's skipped.
I use a String-identifier to check which tasks are considered to belong to one "throttle".
This is the code, it also includes normal scheduling-abilities, but you can check out the essential bits in there.
package org.uilib.util;
import com.google.common.collect.Maps;
import java.util.Map;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class SmartExecutor implements Throttle, Executor {
//~ Static fields/initializers -------------------------------------------------------------------------------------
private static final Logger L = LoggerFactory.getLogger(SmartExecutor.class);
//~ Instance fields ------------------------------------------------------------------------------------------------
private final ExecutorService executor = Executors.newCachedThreadPool();
private final DelayQueue<DelayedRunnable> taskQueue = new DelayQueue<DelayedRunnable>();
private final Map<String, ThrottledRunnable> throttledTasks = Maps.newHashMap();
//~ Constructors ---------------------------------------------------------------------------------------------------
/* schedule a Runnable to be executed a fixed period of time after it was scheduled
* if a new Runnable with the same throttleName is scheduled before this one was called, it will overwrite this */
public SmartExecutor() {
this.executor.execute(new Scheduler());
}
//~ Methods --------------------------------------------------------------------------------------------------------
/* execute a Runnable once */
#Override
public void execute(final Runnable runnable) {
this.executor.execute(runnable);
}
/* schedule a Runnable to be executed after a fixed period of time */
public void schedule(final long delay, final TimeUnit timeUnit, final Runnable runnable) {
this.taskQueue.put(new DelayedRunnable(runnable, delay, timeUnit));
}
/* schedule a Runnable to be executed using a fixed delay between the end of a run and the start of the next one */
public void scheduleAtFixedRate(final long period, final TimeUnit timeUnit, final Runnable runnable) {
this.taskQueue.put(new RepeatingRunnable(runnable, period, timeUnit));
}
/* shut the the executor down */
public void shutdown() {
this.executor.shutdownNow();
}
#Override
public void throttle(final String throttleName, final long delay, final TimeUnit timeUnit, final Runnable runnable) {
final ThrottledRunnable thrRunnable = new ThrottledRunnable(runnable, throttleName, delay, timeUnit);
this.throttledTasks.put(throttleName, thrRunnable);
this.taskQueue.put(thrRunnable);
}
//~ Inner Classes --------------------------------------------------------------------------------------------------
private static class DelayedRunnable implements Delayed, Runnable {
protected final Runnable runnable;
private final long endOfDelay;
public DelayedRunnable(final Runnable runnable, final long delay, final TimeUnit delayUnit) {
this.runnable = runnable;
this.endOfDelay = delayUnit.toMillis(delay) + System.currentTimeMillis();
}
#Override
public int compareTo(final Delayed other) {
final Long delay1 = this.getDelay(TimeUnit.MILLISECONDS);
final Long delay2 = other.getDelay(TimeUnit.MILLISECONDS);
return delay1.compareTo(delay2);
}
#Override
public long getDelay(final TimeUnit unit) {
return unit.convert(this.endOfDelay - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
#Override
public void run() {
this.runnable.run();
}
}
private static final class RepeatingRunnable extends DelayedRunnable {
private final long periodInMillis;
public RepeatingRunnable(final Runnable runnable, final long period, final TimeUnit delayUnit) {
super(runnable, period, delayUnit);
this.periodInMillis = delayUnit.convert(period, TimeUnit.MILLISECONDS);
}
public RepeatingRunnable reschedule() {
return new RepeatingRunnable(this.runnable, this.periodInMillis, TimeUnit.MILLISECONDS);
}
}
private final class Scheduler implements Runnable {
#Override
public void run() {
while (true) {
try {
/* wait for the next runnable to become available */
final DelayedRunnable task = SmartExecutor.this.taskQueue.take();
if (task instanceof RepeatingRunnable) {
/* tell executor to run the action and reschedule it afterwards */
SmartExecutor.this.executor.execute(
new Runnable() {
#Override
public void run() {
task.run();
SmartExecutor.this.taskQueue.put(((RepeatingRunnable) task).reschedule());
}
});
} else if (task instanceof ThrottledRunnable) {
final ThrottledRunnable thrTask = (ThrottledRunnable) task;
/* run only if this is the latest task in given throttle, otherwise skip execution */
if (SmartExecutor.this.throttledTasks.get(thrTask.getThrottleName()) == thrTask) {
SmartExecutor.this.executor.execute(task);
}
} else {
/* tell the executor to just run the action */
SmartExecutor.this.executor.execute(task);
}
} catch (final InterruptedException e) {
SmartExecutor.L.debug("scheduler interrupted (shutting down)");
return;
}
}
}
}
private static final class ThrottledRunnable extends DelayedRunnable {
private final String throttleName;
public ThrottledRunnable(final Runnable runnable, final String throttleName, final long period,
final TimeUnit delayUnit) {
super(runnable, period, delayUnit);
this.throttleName = throttleName;
}
public String getThrottleName() {
return this.throttleName;
}
}
}
If the problem is blocking the UI Thread during resizing, you should consider the method asyncExec of the class Display
/**
* Causes the <code>run()</code> method of the runnable to
* be invoked by the user-interface thread at the next
* reasonable opportunity. The caller of this method continues
* to run in parallel, and is not notified when the
* runnable has completed. Specifying <code>null</code> as the
* runnable simply wakes the user-interface thread when run.
* <p>
* Note that at the time the runnable is invoked, widgets
* that have the receiver as their display may have been
* disposed. Therefore, it is necessary to check for this
* case inside the runnable before accessing the widget.
* </p>
*
* #param runnable code to run on the user-interface thread or <code>null</code>
*
* #exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* #see #syncExec
*/
public void asyncExec (Runnable runnable) {
synchronized (Device.class) {
if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED);
synchronizer.asyncExec (runnable);
}
}

Categories