Home | History | Annotate | Download | only in jdi
      1 /*
      2  * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package com.sun.tools.jdi;
     27 
     28 import com.sun.jdi.*;
     29 import com.sun.jdi.event.EventQueue;
     30 import com.sun.jdi.event.EventSet;
     31 
     32 import java.util.*;
     33 
     34 public class EventQueueImpl extends MirrorImpl implements EventQueue {
     35 
     36     /*
     37      * Note this is not a synchronized list. Iteration/update should be
     38      * protected through the 'this' monitor.
     39      */
     40     LinkedList<EventSet> eventSets = new LinkedList<EventSet>();
     41 
     42     TargetVM target;
     43     boolean closed = false;
     44 
     45     EventQueueImpl(VirtualMachine vm, TargetVM target) {
     46         super(vm);
     47         this.target = target;
     48         target.addEventQueue(this);
     49     }
     50 
     51     /*
     52      * Override superclass back to default equality
     53      */
     54     public boolean equals(Object obj) {
     55         return this == obj;
     56     }
     57 
     58     public int hashCode() {
     59         return System.identityHashCode(this);
     60     }
     61 
     62     synchronized void enqueue(EventSet eventSet) {
     63         eventSets.add(eventSet);
     64         notifyAll();
     65     }
     66 
     67     synchronized int size() {
     68         return eventSets.size();
     69     }
     70 
     71     synchronized void close() {
     72         if (!closed) {
     73             closed = true; // OK for this the be first since synchronized
     74 
     75             // place VMDisconnectEvent into queue
     76             enqueue(new EventSetImpl(vm,
     77                                      (byte)JDWP.EventKind.VM_DISCONNECTED));
     78         }
     79     }
     80 
     81     public EventSet remove() throws InterruptedException {
     82         return remove(0);
     83     }
     84 
     85     /**
     86      * Filter out events not for user's eyes.
     87      * Then filter out empty sets.
     88      */
     89     public EventSet remove(long timeout) throws InterruptedException {
     90         if (timeout < 0) {
     91             throw new IllegalArgumentException("Timeout cannot be negative");
     92         }
     93 
     94         EventSet eventSet;
     95         while (true) {
     96             EventSetImpl fullEventSet = removeUnfiltered(timeout);
     97             if (fullEventSet == null) {
     98                 eventSet = null;  // timeout
     99                 break;
    100             }
    101             /*
    102              * Remove events from the event set for which
    103              * there is no corresponding enabled request (
    104              * this includes our internally requested events.)
    105              * This never returns null
    106              */
    107             eventSet = fullEventSet.userFilter();
    108             if (!eventSet.isEmpty()) {
    109                 break;
    110             }
    111         }
    112 
    113         if ((eventSet != null) && (eventSet.suspendPolicy() == JDWP.SuspendPolicy.ALL)) {
    114             vm.notifySuspend();
    115         }
    116 
    117         return eventSet;
    118     }
    119 
    120     EventSet removeInternal() throws InterruptedException {
    121         EventSet eventSet;
    122         do {
    123             // Waiting forever, so removeUnfiltered() is never null
    124             eventSet = removeUnfiltered(0).internalFilter();
    125         } while (eventSet == null || eventSet.isEmpty());
    126 
    127         /*
    128          * Currently, no internal events are requested with a suspend
    129          * policy other than none, so we don't check for notifySuspend()
    130          * here. If this changes in the future, there is much
    131          * infrastructure that needs to be updated.
    132          */
    133 
    134         return eventSet;
    135     }
    136 
    137     private TimerThread startTimerThread(long timeout) {
    138         TimerThread thread = new TimerThread(timeout);
    139         thread.setDaemon(true);
    140         thread.start();
    141         return thread;
    142     }
    143 
    144     private boolean shouldWait(TimerThread timerThread) {
    145         return !closed && eventSets.isEmpty() &&
    146                ((timerThread == null) ? true : !timerThread.timedOut());
    147     }
    148 
    149     private EventSetImpl removeUnfiltered(long timeout)
    150                                                throws InterruptedException {
    151         EventSetImpl eventSet = null;
    152 
    153         /*
    154          * Make sure the VM has completed initialization before
    155          * trying to build events.
    156          */
    157         vm.waitInitCompletion();
    158 
    159         synchronized(this) {
    160             if (!eventSets.isEmpty()) {
    161                 /*
    162                  * If there's already something there, no need
    163                  * for anything elaborate.
    164                  */
    165                 eventSet = (EventSetImpl)eventSets.removeFirst();
    166             } else {
    167                 /*
    168                  * If a timeout was specified, create a thread to
    169                  * notify this one when a timeout
    170                  * occurs. We can't use the timed version of wait()
    171                  * because it is possible for multiple enqueue() calls
    172                  * before we see something in the eventSet queue
    173                  * (this is possible when multiple threads call
    174                  * remove() concurrently -- not a great idea, but
    175                  * it should be supported). Even if enqueue() did a
    176                  * notify() instead of notifyAll() we are not able to
    177                  * use a timed wait because there's no way to distinguish
    178                  * a timeout from a notify.  That limitation implies a
    179                  * possible race condition between a timed out thread
    180                  * and a notified thread.
    181                  */
    182                 TimerThread timerThread = null;
    183                 try {
    184                     if (timeout > 0) {
    185                         timerThread = startTimerThread(timeout);
    186                     }
    187 
    188                     while (shouldWait(timerThread)) {
    189                         this.wait();
    190                     }
    191                 } finally {
    192                     if ((timerThread != null) && !timerThread.timedOut()) {
    193                         timerThread.interrupt();
    194                     }
    195                 }
    196 
    197                 if (eventSets.isEmpty()) {
    198                     if (closed) {
    199                         throw new VMDisconnectedException();
    200                     }
    201                 } else {
    202                     eventSet = (EventSetImpl)eventSets.removeFirst();
    203                 }
    204             }
    205         }
    206 
    207         // The build is synchronized on the event set, don't hold
    208         // the queue lock.
    209         if (eventSet != null) {
    210             target.notifyDequeueEventSet();
    211             eventSet.build();
    212         }
    213         return eventSet;
    214     }
    215 
    216     private class TimerThread extends Thread {
    217         private boolean timedOut = false;
    218         private long timeout;
    219 
    220         TimerThread(long timeout) {
    221             super(vm.threadGroupForJDI(), "JDI Event Queue Timer");
    222             this.timeout = timeout;
    223         }
    224 
    225         boolean timedOut() {
    226             return timedOut;
    227         }
    228 
    229         public void run() {
    230             try {
    231                 Thread.sleep(timeout);
    232                 EventQueueImpl queue = EventQueueImpl.this;
    233                 synchronized(queue) {
    234                     timedOut = true;
    235                     queue.notifyAll();
    236                 }
    237             } catch (InterruptedException e) {
    238                 // Exit without notifying
    239             }
    240         }
    241     }
    242 }
    243