1 /* 2 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * - Redistribution of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * - Redistribution in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * Neither the name of Sun Microsystems, Inc. or the names of 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * This software is provided "AS IS," without a warranty of any kind. ALL 20 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, 21 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A 22 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN 23 * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR 24 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR 25 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR 26 * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR 27 * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE 28 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, 29 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF 30 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 31 * 32 * You acknowledge that this software is not designed or intended for use 33 * in the design, construction, operation or maintenance of any nuclear 34 * facility. 35 * 36 * Sun gratefully acknowledges that this software was originally authored 37 * and developed by Kenneth Bradley Russell and Christopher John Kline. 38 */ 39 40 package com.sun.opengl.impl; 41 42 import java.lang.reflect.InvocationTargetException; 43 import java.security.*; 44 import java.util.*; 45 import javax.media.opengl.*; 46 47 /** Singleton thread upon which all OpenGL work is performed by 48 default. Unfortunately many vendors' OpenGL drivers are not really 49 thread-safe and stability is much improved by performing OpenGL 50 work on at most one thread. This is the default behavior of the 51 GLAutoDrawable implementations according to the {@link 52 javax.media.opengl.Threading Threading} class. The GLWorkerThread 53 replaces the original AWT event queue thread-based mechanism for 54 two reasons: first, more than one AWT event queue thread may be 55 spawned, for example if a dialog is being shown; second, it avoids 56 blocking the AWT event queue thread during OpenGL rendering. */ 57 58 public class GLWorkerThread { 59 private static volatile boolean started; 60 private static volatile Thread thread; 61 private static Object lock; 62 private static volatile boolean shouldTerminate; 63 private static volatile Throwable exception; 64 65 // The Runnable to execute immediately on the worker thread 66 private static volatile Runnable work; 67 // Queue of Runnables to be asynchronously invoked 68 private static List queue = new LinkedList(); 69 70 /** Should only be called by Threading class if creation of the 71 GLWorkerThread was requested via the opengl.1thread system 72 property. */ start()73 public static void start() { 74 if (!started) { 75 synchronized (GLWorkerThread.class) { 76 if (!started) { 77 lock = new Object(); 78 thread = new Thread(new WorkerRunnable(), 79 "JOGL GLWorkerThread"); 80 thread.setDaemon(true); 81 started = true; 82 synchronized (lock) { 83 thread.start(); 84 try { 85 lock.wait(); 86 } catch (InterruptedException e) { 87 } 88 } 89 90 /* 91 92 // Note: it appears that there is a bug in NVidia's current 93 // drivers where if a context was ever made current on a 94 // given thread and that thread has exited before program 95 // exit, a crash occurs in the drivers. Releasing the 96 // context from the given thread does not work around the 97 // problem. 98 // 99 // For the time being, we're going to work around this 100 // problem by not terminating the GLWorkerThread. In theory, 101 // shutting down the GLWorkerThread cleanly could be a good 102 // general solution to the problem of needing to 103 // cooperatively terminate all Animators at program exit. 104 // 105 // It appears that this doesn't even work around all of the 106 // kinds of crashes. Causing the context to be unilaterally 107 // released from the GLWorkerThread after each invocation 108 // seems to work around all of the kinds of crashes seen. 109 // 110 // These appear to be similar to the kinds of crashes seen 111 // when the Java2D/OpenGL pipeline terminates, and those are 112 // a known issue being fixed, so presumably these will be 113 // fixed in NVidia's next driver set. 114 115 // Install shutdown hook to terminate daemon thread more or 116 // less cooperatively 117 AccessController.doPrivileged(new PrivilegedAction() { 118 public Object run() { 119 Runtime.getRuntime().addShutdownHook(new Thread() { 120 public void run() { 121 Object lockTemp = lock; 122 if (lockTemp == null) { 123 // Already terminating (?) 124 return; 125 } 126 synchronized (lockTemp) { 127 shouldTerminate = true; 128 lockTemp.notifyAll(); 129 try { 130 lockTemp.wait(500); 131 } catch (InterruptedException e) { 132 } 133 } 134 } 135 }); 136 return null; 137 } 138 }); 139 140 */ 141 142 } else { 143 throw new RuntimeException("Should not start GLWorkerThread twice"); 144 } 145 } 146 } 147 } 148 invokeAndWait(Runnable runnable)149 public static void invokeAndWait(Runnable runnable) 150 throws InvocationTargetException, InterruptedException { 151 if (!started) { 152 throw new RuntimeException("May not invokeAndWait on worker thread without starting it first"); 153 } 154 155 Object lockTemp = lock; 156 if (lockTemp == null) { 157 return; // Terminating 158 } 159 160 synchronized (lockTemp) { 161 if (thread == null) { 162 // Terminating 163 return; 164 } 165 166 work = runnable; 167 lockTemp.notifyAll(); 168 lockTemp.wait(); 169 if (exception != null) { 170 Throwable localException = exception; 171 exception = null; 172 throw new InvocationTargetException(localException); 173 } 174 } 175 } 176 invokeLater(Runnable runnable)177 public static void invokeLater(Runnable runnable) { 178 if (!started) { 179 throw new RuntimeException("May not invokeLater on worker thread without starting it first"); 180 } 181 182 Object lockTemp = lock; 183 if (lockTemp == null) { 184 return; // Terminating 185 } 186 187 synchronized (lockTemp) { 188 if (thread == null) { 189 // Terminating 190 return; 191 } 192 193 queue.add(runnable); 194 lockTemp.notifyAll(); 195 } 196 } 197 198 /** Indicates whether the OpenGL worker thread was started, i.e., 199 whether it is currently in use. */ isStarted()200 public static boolean isStarted() { 201 return started; 202 } 203 204 /** Indicates whether the current thread is the OpenGL worker 205 thread. */ isWorkerThread()206 public static boolean isWorkerThread() { 207 return (Thread.currentThread() == thread); 208 } 209 210 static class WorkerRunnable implements Runnable { run()211 public void run() { 212 // Notify starting thread that we're ready 213 synchronized (lock) { 214 lock.notifyAll(); 215 } 216 217 while (!shouldTerminate) { 218 synchronized (lock) { 219 while (!shouldTerminate && 220 (work == null) && 221 queue.isEmpty()) { 222 try { 223 // Avoid race conditions with wanting to release contexts on this thread 224 lock.wait(1000); 225 } catch (InterruptedException e) { 226 } 227 228 if (GLContext.getCurrent() != null) { 229 // Test later to see whether we need to release this context 230 break; 231 } 232 } 233 234 if (shouldTerminate) { 235 lock.notifyAll(); 236 thread = null; 237 lock = null; 238 return; 239 } 240 241 if (work != null) { 242 try { 243 work.run(); 244 } catch (Throwable t) { 245 exception = t; 246 } finally { 247 work = null; 248 lock.notifyAll(); 249 } 250 } 251 252 while (!queue.isEmpty()) { 253 try { 254 Runnable curAsync = (Runnable) queue.remove(0); 255 curAsync.run(); 256 } catch (Throwable t) { 257 System.out.println("Exception occurred on JOGL OpenGL worker thread:"); 258 t.printStackTrace(); 259 } 260 } 261 262 // See about releasing current context 263 GLContext curContext = GLContext.getCurrent(); 264 if (curContext != null && 265 (curContext instanceof GLContextImpl)) { 266 GLContextImpl impl = (GLContextImpl) curContext; 267 if (impl.hasWaiters()) { 268 impl.release(); 269 } 270 } 271 } 272 } 273 } 274 } 275 } 276