0N/A/*
2362N/A * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.tools.jdi;
0N/A
0N/Aimport com.sun.jdi.*;
0N/Aimport com.sun.jdi.event.EventQueue;
0N/Aimport com.sun.jdi.event.EventSet;
0N/A
0N/Aimport java.util.*;
0N/A
0N/Apublic class EventQueueImpl extends MirrorImpl implements EventQueue {
0N/A
0N/A /*
0N/A * Note this is not a synchronized list. Iteration/update should be
0N/A * protected through the 'this' monitor.
0N/A */
0N/A LinkedList<EventSet> eventSets = new LinkedList<EventSet>();
0N/A
0N/A TargetVM target;
0N/A boolean closed = false;
0N/A
0N/A EventQueueImpl(VirtualMachine vm, TargetVM target) {
0N/A super(vm);
0N/A this.target = target;
0N/A target.addEventQueue(this);
0N/A }
0N/A
0N/A /*
0N/A * Override superclass back to default equality
0N/A */
0N/A public boolean equals(Object obj) {
0N/A return this == obj;
0N/A }
0N/A
0N/A public int hashCode() {
0N/A return System.identityHashCode(this);
0N/A }
0N/A
0N/A synchronized void enqueue(EventSet eventSet) {
0N/A eventSets.add(eventSet);
0N/A notifyAll();
0N/A }
0N/A
0N/A synchronized int size() {
0N/A return eventSets.size();
0N/A }
0N/A
0N/A synchronized void close() {
0N/A if (!closed) {
0N/A closed = true; // OK for this the be first since synchronized
0N/A
0N/A // place VMDisconnectEvent into queue
0N/A enqueue(new EventSetImpl(vm,
0N/A (byte)JDWP.EventKind.VM_DISCONNECTED));
0N/A }
0N/A }
0N/A
0N/A public EventSet remove() throws InterruptedException {
0N/A return remove(0);
0N/A }
0N/A
0N/A /**
0N/A * Filter out events not for user's eyes.
0N/A * Then filter out empty sets.
0N/A */
0N/A public EventSet remove(long timeout) throws InterruptedException {
0N/A if (timeout < 0) {
0N/A throw new IllegalArgumentException("Timeout cannot be negative");
0N/A }
0N/A
0N/A EventSet eventSet;
0N/A while (true) {
0N/A EventSetImpl fullEventSet = removeUnfiltered(timeout);
0N/A if (fullEventSet == null) {
0N/A eventSet = null; // timeout
0N/A break;
0N/A }
0N/A /*
0N/A * Remove events from the event set for which
0N/A * there is no corresponding enabled request (
0N/A * this includes our internally requested events.)
0N/A * This never returns null
0N/A */
0N/A eventSet = fullEventSet.userFilter();
0N/A if (!eventSet.isEmpty()) {
0N/A break;
0N/A }
0N/A }
0N/A
0N/A if ((eventSet != null) && (eventSet.suspendPolicy() == JDWP.SuspendPolicy.ALL)) {
0N/A vm.notifySuspend();
0N/A }
0N/A
0N/A return eventSet;
0N/A }
0N/A
0N/A EventSet removeInternal() throws InterruptedException {
0N/A EventSet eventSet;
0N/A do {
0N/A // Waiting forever, so removeUnfiltered() is never null
0N/A eventSet = removeUnfiltered(0).internalFilter();
0N/A } while (eventSet == null || eventSet.isEmpty());
0N/A
0N/A /*
0N/A * Currently, no internal events are requested with a suspend
0N/A * policy other than none, so we don't check for notifySuspend()
0N/A * here. If this changes in the future, there is much
0N/A * infrastructure that needs to be updated.
0N/A */
0N/A
0N/A return eventSet;
0N/A }
0N/A
0N/A private TimerThread startTimerThread(long timeout) {
0N/A TimerThread thread = new TimerThread(timeout);
0N/A thread.setDaemon(true);
0N/A thread.start();
0N/A return thread;
0N/A }
0N/A
0N/A private boolean shouldWait(TimerThread timerThread) {
0N/A return !closed && eventSets.isEmpty() &&
0N/A ((timerThread == null) ? true : !timerThread.timedOut());
0N/A }
0N/A
0N/A private EventSetImpl removeUnfiltered(long timeout)
0N/A throws InterruptedException {
0N/A EventSetImpl eventSet = null;
0N/A
0N/A /*
0N/A * Make sure the VM has completed initialization before
0N/A * trying to build events.
0N/A */
0N/A vm.waitInitCompletion();
0N/A
0N/A synchronized(this) {
0N/A if (!eventSets.isEmpty()) {
0N/A /*
0N/A * If there's already something there, no need
0N/A * for anything elaborate.
0N/A */
0N/A eventSet = (EventSetImpl)eventSets.removeFirst();
0N/A } else {
0N/A /*
0N/A * If a timeout was specified, create a thread to
0N/A * notify this one when a timeout
0N/A * occurs. We can't use the timed version of wait()
0N/A * because it is possible for multiple enqueue() calls
0N/A * before we see something in the eventSet queue
0N/A * (this is possible when multiple threads call
0N/A * remove() concurrently -- not a great idea, but
0N/A * it should be supported). Even if enqueue() did a
0N/A * notify() instead of notifyAll() we are not able to
0N/A * use a timed wait because there's no way to distinguish
0N/A * a timeout from a notify. That limitation implies a
0N/A * possible race condition between a timed out thread
0N/A * and a notified thread.
0N/A */
0N/A TimerThread timerThread = null;
0N/A try {
0N/A if (timeout > 0) {
0N/A timerThread = startTimerThread(timeout);
0N/A }
0N/A
0N/A while (shouldWait(timerThread)) {
0N/A this.wait();
0N/A }
0N/A } finally {
0N/A if ((timerThread != null) && !timerThread.timedOut()) {
0N/A timerThread.interrupt();
0N/A }
0N/A }
0N/A
0N/A if (eventSets.isEmpty()) {
0N/A if (closed) {
0N/A throw new VMDisconnectedException();
0N/A }
0N/A } else {
0N/A eventSet = (EventSetImpl)eventSets.removeFirst();
0N/A }
0N/A }
0N/A }
0N/A
0N/A // The build is synchronized on the event set, don't hold
0N/A // the queue lock.
0N/A if (eventSet != null) {
0N/A target.notifyDequeueEventSet();
0N/A eventSet.build();
0N/A }
0N/A return eventSet;
0N/A }
0N/A
0N/A private class TimerThread extends Thread {
0N/A private boolean timedOut = false;
0N/A private long timeout;
0N/A
0N/A TimerThread(long timeout) {
0N/A super(vm.threadGroupForJDI(), "JDI Event Queue Timer");
0N/A this.timeout = timeout;
0N/A }
0N/A
0N/A boolean timedOut() {
0N/A return timedOut;
0N/A }
0N/A
0N/A public void run() {
0N/A try {
0N/A Thread.sleep(timeout);
0N/A EventQueueImpl queue = EventQueueImpl.this;
0N/A synchronized(queue) {
0N/A timedOut = true;
0N/A queue.notifyAll();
0N/A }
0N/A } catch (InterruptedException e) {
0N/A // Exit without notifying
0N/A }
0N/A }
0N/A }
0N/A}