/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package com.sun.glass.ui; import java.util.ArrayDeque; import java.util.Deque; /** * An object representing a nested event loop. */ public final class EventLoop { private static final Deque stack = new ArrayDeque(); /** * Possible states for a nested event loop object. */ public static enum State { /** * A free EventLoop object that can be used to start a new nested event loop. */ IDLE, /** * The nested loop represented by this object is currently running. */ ACTIVE, /** * The nested loop has been requested to leave. */ LEAVING } private State state = State.IDLE; private Object returnValue; EventLoop() { Application.checkEventThread(); } /** * Gets the current {@link State} of this EventLoop instance. * This method must only be invoked on the main (event handling) * thread. */ public State getState() { Application.checkEventThread(); return state; } /** * Starts a nested event loop. * * The EventLoop object must be in the {@code IDLE} state when calling this * method. Upon entering the nested event loop, the state of the object * changes to {@code ACTIVE}. When the method returns, the EventLoop object * is back to the {@code IDLE} state. * * Calling this method temporarily blocks processing of the current event, * and starts a nested event loop to handle other native events. To * proceed with the blocked execution path, the application should call the * {@link #leave(Object)} method. * * Note that this method must only be invoked on the main (event handling) * thread. * * An application may enter several nested loops recursively. Each nested * event loop must be represented by a separate EventLoop instance. There's * no limit of recursion other than that imposed by the native stack size. * * @return an object passed to the leave() method * @throws RuntimeException if the current thread is not the main thread * @throws IllegalStateException if the EventLoop object isn't IDLE */ public Object enter() { Application.checkEventThread(); if (!state.equals(State.IDLE)) { throw new IllegalStateException("The event loop object isn't idle"); } state = State.ACTIVE; stack.push(this); try { Object ret = Application.enterNestedEventLoop(); assert ret == this : "Internal inconsistency - wrong EventLoop"; assert stack.peek() == this : "Internal inconsistency - corrupted event loops stack"; assert state.equals(State.LEAVING) : "The event loop isn't leaving"; return returnValue; } finally { returnValue = null; state = State.IDLE; stack.pop(); if (!stack.isEmpty() && stack.peek().state.equals(State.LEAVING)) { Application.invokeLater(() -> { EventLoop loop = stack.peek(); // we might have already entered another loop, so check again if (loop != null && loop.state.equals(State.LEAVING)) { Application.leaveNestedEventLoop(loop); } }); } } } /** * Requests this nested event loop to terminate. * * The EventLoop object must be in the {@code ACTIVE} state when calling * this method. This method switches the state of the object to {@code * LEAVING}. * * After calling this method and returning from the current event handler, * the execusion returns to the point where the {@link #enter()} method * was called previously. You may specify a return value for the * enter() method by passing the argument {@code retValue} to * the leave(). * * Calls to enter() and leave() may be interleaved for different event * loops. If the EventLoop object is not innermost (i.e. not most recently * started), calling leave() just switches this EventLoop object to the * {@code LEAVING} state w/o actually terminating it after returning from * the current event handler. The enter() method that started this event * loop only returns after all inner event loops are terminated. * * Note that this method must only be invoked on the main (event handling) * thread. * * @throws RuntimeException if the current thread is not the main thread * @throws IllegalStateException if the nested event loop isn't ACTIVE */ public void leave(Object ret) { Application.checkEventThread(); if (!state.equals(State.ACTIVE)) { throw new IllegalStateException("The event loop object isn't active"); } state = State.LEAVING; returnValue = ret; if (stack.peek() == this) { Application.leaveNestedEventLoop(this); } } }