/* | 
|
 * Copyright (c) 1998, 2006, 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.tools.jdi;  | 
|
import com.sun.jdi.*;  | 
|
import com.sun.jdi.event.EventQueue;  | 
|
import com.sun.jdi.event.EventSet;  | 
|
import java.util.*;  | 
|
public class EventQueueImpl extends MirrorImpl implements EventQueue {  | 
|
    /* | 
|
     * Note this is not a synchronized list. Iteration/update should be | 
|
     * protected through the 'this' monitor. | 
|
*/  | 
|
LinkedList<EventSet> eventSets = new LinkedList<EventSet>();  | 
|
TargetVM target;  | 
|
boolean closed = false;  | 
|
EventQueueImpl(VirtualMachine vm, TargetVM target) {  | 
|
super(vm);  | 
|
this.target = target;  | 
|
target.addEventQueue(this);  | 
|
}  | 
|
    /* | 
|
     * Override superclass back to default equality | 
|
*/  | 
|
public boolean equals(Object obj) {  | 
|
return this == obj;  | 
|
}  | 
|
    public int hashCode() { | 
|
return System.identityHashCode(this);  | 
|
}  | 
|
synchronized void enqueue(EventSet eventSet) {  | 
|
eventSets.add(eventSet);  | 
|
notifyAll();  | 
|
}  | 
|
    synchronized int size() { | 
|
return eventSets.size();  | 
|
}  | 
|
    synchronized void close() { | 
|
if (!closed) {  | 
|
closed = true; // OK for this the be first since synchronized  | 
|
            // place VMDisconnectEvent into queue | 
|
enqueue(new EventSetImpl(vm,  | 
|
(byte)JDWP.EventKind.VM_DISCONNECTED));  | 
|
}  | 
|
}  | 
|
public EventSet remove() throws InterruptedException {  | 
|
return remove(0);  | 
|
}  | 
|
    /** | 
|
     * Filter out events not for user's eyes. | 
|
     * Then filter out empty sets. | 
|
*/  | 
|
public EventSet remove(long timeout) throws InterruptedException {  | 
|
if (timeout < 0) {  | 
|
throw new IllegalArgumentException("Timeout cannot be negative");  | 
|
}  | 
|
EventSet eventSet;  | 
|
        while (true) { | 
|
EventSetImpl fullEventSet = removeUnfiltered(timeout);  | 
|
if (fullEventSet == null) {  | 
|
eventSet = null; // timeout  | 
|
break;  | 
|
}  | 
|
            /* | 
|
             * Remove events from the event set for which | 
|
             * there is no corresponding enabled request ( | 
|
             * this includes our internally requested events.) | 
|
             * This never returns null | 
|
*/  | 
|
eventSet = fullEventSet.userFilter();  | 
|
if (!eventSet.isEmpty()) {  | 
|
break;  | 
|
}  | 
|
}  | 
|
if ((eventSet != null) && (eventSet.suspendPolicy() == JDWP.SuspendPolicy.ALL)) {  | 
|
vm.notifySuspend();  | 
|
}  | 
|
return eventSet;  | 
|
}  | 
|
EventSet removeInternal() throws InterruptedException {  | 
|
EventSet eventSet;  | 
|
        do { | 
|
            // Waiting forever, so removeUnfiltered() is never null | 
|
eventSet = removeUnfiltered(0).internalFilter();  | 
|
} while (eventSet == null || eventSet.isEmpty());  | 
|
/*  | 
|
* Currently, no internal events are requested with a suspend  | 
|
* policy other than none, so we don't check for notifySuspend()  | 
|
* here. If this changes in the future, there is much  | 
|
* infrastructure that needs to be updated.  | 
|
*/  | 
|
return eventSet;  | 
|
}  | 
|
private TimerThread startTimerThread(long timeout) {  | 
|
TimerThread thread = new TimerThread(timeout);  | 
|
thread.setDaemon(true);  | 
|
thread.start();  | 
|
return thread;  | 
|
}  | 
|
private boolean shouldWait(TimerThread timerThread) {  | 
|
return !closed && eventSets.isEmpty() &&  | 
|
((timerThread == null) ? true : !timerThread.timedOut());  | 
|
}  | 
|
private EventSetImpl removeUnfiltered(long timeout)  | 
|
throws InterruptedException {  | 
|
EventSetImpl eventSet = null;  | 
|
        /* | 
|
         * Make sure the VM has completed initialization before | 
|
         * trying to build events. | 
|
*/  | 
|
vm.waitInitCompletion();  | 
|
        synchronized(this) { | 
|
if (!eventSets.isEmpty()) {  | 
|
                /* | 
|
                 * If there's already something there, no need | 
|
                 * for anything elaborate. | 
|
*/  | 
|
eventSet = (EventSetImpl)eventSets.removeFirst();  | 
|
            } else { | 
|
                /* | 
|
                 * If a timeout was specified, create a thread to | 
|
                 * notify this one when a timeout | 
|
                 * occurs. We can't use the timed version of wait() | 
|
                 * because it is possible for multiple enqueue() calls | 
|
                 * before we see something in the eventSet queue | 
|
                 * (this is possible when multiple threads call | 
|
                 * remove() concurrently -- not a great idea, but | 
|
                 * it should be supported). Even if enqueue() did a | 
|
                 * notify() instead of notifyAll() we are not able to | 
|
                 * use a timed wait because there's no way to distinguish | 
|
                 * a timeout from a notify.  That limitation implies a | 
|
                 * possible race condition between a timed out thread | 
|
                 * and a notified thread. | 
|
*/  | 
|
TimerThread timerThread = null;  | 
|
                try { | 
|
if (timeout > 0) {  | 
|
timerThread = startTimerThread(timeout);  | 
|
}  | 
|
while (shouldWait(timerThread)) {  | 
|
this.wait();  | 
|
}  | 
|
                } finally { | 
|
if ((timerThread != null) && !timerThread.timedOut()) {  | 
|
timerThread.interrupt();  | 
|
}  | 
|
}  | 
|
if (eventSets.isEmpty()) {  | 
|
if (closed) {  | 
|
throw new VMDisconnectedException();  | 
|
}  | 
|
                } else { | 
|
eventSet = (EventSetImpl)eventSets.removeFirst();  | 
|
}  | 
|
}  | 
|
}  | 
|
// The build is synchronized on the event set, don't hold  | 
|
        // the queue lock. | 
|
if (eventSet != null) {  | 
|
target.notifyDequeueEventSet();  | 
|
eventSet.build();  | 
|
}  | 
|
return eventSet;  | 
|
}  | 
|
private class TimerThread extends Thread {  | 
|
private boolean timedOut = false;  | 
|
private long timeout;  | 
|
        TimerThread(long timeout) { | 
|
super(vm.threadGroupForJDI(), "JDI Event Queue Timer");  | 
|
this.timeout = timeout;  | 
|
}  | 
|
        boolean timedOut() { | 
|
return timedOut;  | 
|
}  | 
|
        public void run() { | 
|
            try { | 
|
Thread.sleep(timeout);  | 
|
EventQueueImpl queue = EventQueueImpl.this;  | 
|
synchronized(queue) {  | 
|
timedOut = true;  | 
|
queue.notifyAll();  | 
|
}  | 
|
} catch (InterruptedException e) {  | 
|
// Exit without notifying  | 
|
}  | 
|
}  | 
|
}  | 
|
}  |