/*
 * 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 java.util.LinkedList;

import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.event.EventQueue;
import com.sun.jdi.event.EventSet;

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<>();

    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.
/** * 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 } } } }