/*
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.glass.ui;
import java.util.*;
import java.util.concurrent.*;
A dispatcher for Application.invokeLater() that submits deferred runnables
to the native system one by one.
A native system may by default execute all submitted runnables before
actually leaving a nested event loop. However, there is an assumption that a
runnable that calls the Application.leaveNestedEventLoop() method must be
the last runnable executed in the current inner event loop. The next
runnable that might have already been submitted by client code, must be
invoked in the context of the outer event loop. To satisfy this requirement
the runnables must be submitted to the native system one by one. This allows
for fine grain control over executing the deferred tasks when it comes to
entering/leaving nested event loops despite of native system limitations.
/**
* A dispatcher for Application.invokeLater() that submits deferred runnables
* to the native system one by one.
*
* A native system may by default execute all submitted runnables before
* actually leaving a nested event loop. However, there is an assumption that a
* runnable that calls the Application.leaveNestedEventLoop() method must be
* the last runnable executed in the current inner event loop. The next
* runnable that might have already been submitted by client code, must be
* invoked in the context of the outer event loop. To satisfy this requirement
* the runnables must be submitted to the native system one by one. This allows
* for fine grain control over executing the deferred tasks when it comes to
* entering/leaving nested event loops despite of native system limitations.
*/
public final class InvokeLaterDispatcher extends Thread {
// The runnables queue
private final BlockingDeque<Runnable> deque = new LinkedBlockingDeque<Runnable>();
// Main lock
private final Object LOCK = new StringBuilder("InvokeLaterLock");
// Indicates if the application has entered a nested event loop
private boolean nestedEventLoopEntered = false;
// Indicates if the application is currently leaving a nested event loop
private volatile boolean leavingNestedEventLoop = false;
An InvokeLaterDispatcher client implements this interface to allow
the dispatcher to submit runnables to the native system.
/**
* An InvokeLaterDispatcher client implements this interface to allow
* the dispatcher to submit runnables to the native system.
*/
public static interface InvokeLaterSubmitter {
Submits the runnable to the native system for later execution and
returns immediately.
/**
* Submits the runnable to the native system for later execution and
* returns immediately.
*/
public void submitForLaterInvocation(Runnable r);
}
private final InvokeLaterSubmitter invokeLaterSubmitter;
public InvokeLaterDispatcher(InvokeLaterSubmitter invokeLaterSubmitter) {
super("InvokeLaterDispatcher");
setDaemon(true);
this.invokeLaterSubmitter = invokeLaterSubmitter;
}
private class Future implements Runnable {
private boolean done = false;
private final Runnable runnable;
public Future(Runnable r) {
this.runnable = r;
}
Tells whether the runnbale has finished execution.
This method must be called under the LOCK lock.
/**
* Tells whether the runnbale has finished execution.
*
* This method must be called under the LOCK lock.
*/
public boolean isDone() {
return done;
}
@Override public void run() {
try {
this.runnable.run();
} finally {
synchronized (LOCK) {
this.done = true;
LOCK.notifyAll();
}
}
}
}
@Override public void run() {
try {
while (true) {
Runnable r = deque.takeFirst();
if (leavingNestedEventLoop) {
// Defer invocation of the runnable till the current inner
// event loop returns from its enterNestedEventLoop()
deque.addFirst(r);
synchronized (LOCK) {
while (leavingNestedEventLoop) {
LOCK.wait();
}
}
} else {
// Submit the runnable to the native system
final Future future = new Future(r);
invokeLaterSubmitter.submitForLaterInvocation(future);
synchronized (LOCK) {
try {
while (!future.isDone() && !nestedEventLoopEntered) {
LOCK.wait();
}
// Continue processing other runnables if we entered
// an inner event loop while excuting this runnable
} finally {
nestedEventLoopEntered = false;
}
}
}
}
} catch (InterruptedException ex) {
// OK, let's stop this thread
}
}
public void invokeAndWait(Runnable runnable) {
final Future future = new Future(runnable);
invokeLaterSubmitter.submitForLaterInvocation(future);
synchronized (LOCK) {
try {
while (!future.isDone()) {
LOCK.wait();
}
} catch (InterruptedException ex) {
}
}
}
Schedules execution of a runnable on the event thread.
See Also: - invokeLater.invokeLater(Runnable)
/**
* Schedules execution of a runnable on the event thread.
*
* @see Application#invokeLater(Runnable)
*/
public void invokeLater(Runnable command) {
deque.addLast(command);
}
Notifies that a nested event loop is going to be entered.
An InvokeLaterDispatcher client is responsible for calling this method
just before the enterNestedEventLoop() is dispatched to the native
system for actually starting the nested loop.
/**
* Notifies that a nested event loop is going to be entered.
*
* An InvokeLaterDispatcher client is responsible for calling this method
* just before the enterNestedEventLoop() is dispatched to the native
* system for actually starting the nested loop.
*/
public void notifyEnteringNestedEventLoop() {
synchronized (LOCK) {
nestedEventLoopEntered = true;
LOCK.notifyAll();
}
}
Notifies that the application is leaving a nested event loop.
/**
* Notifies that the application is leaving a nested event loop.
*/
public void notifyLeavingNestedEventLoop() {
synchronized (LOCK) {
leavingNestedEventLoop = true;
LOCK.notifyAll();
}
}
Notifies that the application has left a nested event loop.
/**
* Notifies that the application has left a nested event loop.
*/
public void notifyLeftNestedEventLoop() {
synchronized (LOCK) {
leavingNestedEventLoop = false;
LOCK.notifyAll();
}
}
}