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