1 /** 2 * Copyright 2010 JogAmp Community. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without modification, are 5 * permitted provided that the following conditions are met: 6 * 7 * 1. Redistributions of source code must retain the above copyright notice, this list of 8 * conditions and the following disclaimer. 9 * 10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 * of conditions and the following disclaimer in the documentation and/or other materials 12 * provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED 15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * 24 * The views and conclusions contained in the software and documentation are those of the 25 * authors and should not be interpreted as representing official policies, either expressed 26 * or implied, of JogAmp Community. 27 */ 28 package jogamp.opengl; 29 30 import java.util.Collection; 31 import java.util.HashMap; 32 import java.util.HashSet; 33 import java.util.Iterator; 34 import java.util.Map; 35 36 import com.jogamp.nativewindow.AbstractGraphicsDevice; 37 import com.jogamp.nativewindow.AbstractGraphicsScreen; 38 import com.jogamp.opengl.GLProfile; 39 40 import com.jogamp.common.ExceptionUtils; 41 import com.jogamp.common.util.InterruptSource; 42 import com.jogamp.common.util.InterruptedRuntimeException; 43 import com.jogamp.common.util.SourcedInterruptedException; 44 import com.jogamp.opengl.GLRendererQuirks; 45 46 public class SharedResourceRunner implements Runnable { 47 protected static final boolean DEBUG = GLDrawableImpl.DEBUG; 48 49 public static interface Resource { isAvailable()50 boolean isAvailable(); getDevice()51 AbstractGraphicsDevice getDevice(); getScreen()52 AbstractGraphicsScreen getScreen(); getDrawable()53 GLDrawableImpl getDrawable(); getContext()54 GLContextImpl getContext(); getRendererQuirks(GLProfile glp)55 GLRendererQuirks getRendererQuirks(GLProfile glp); 56 } 57 58 public static interface Implementation { 59 /** 60 * <p> 61 * Called within synchronized block. 62 * </p> 63 * @param device for creation a {@link AbstractGraphicsDevice} instance. 64 * @return <code>true</code> if the device supports all protocols required for the implementation, otherwise <code>false</code>. 65 */ isDeviceSupported(final AbstractGraphicsDevice device)66 boolean isDeviceSupported(final AbstractGraphicsDevice device); 67 68 /** 69 * <p> 70 * Called within synchronized block. 71 * </p> 72 * @param device for creation a {@link AbstractGraphicsDevice} instance. 73 * @return A new shared resource instance 74 */ createSharedResource(final AbstractGraphicsDevice device)75 Resource createSharedResource(final AbstractGraphicsDevice device); 76 77 /** Called within synchronized block. */ releaseSharedResource(Resource shared)78 void releaseSharedResource(Resource shared); 79 /** Called within synchronized block. */ clear()80 void clear(); 81 82 /** Called within synchronized block. */ mapPut(final AbstractGraphicsDevice device, final Resource resource)83 Resource mapPut(final AbstractGraphicsDevice device, final Resource resource); 84 /** Called within synchronized block. */ mapGet(final AbstractGraphicsDevice device)85 Resource mapGet(final AbstractGraphicsDevice device); 86 /** Called within synchronized block. */ mapValues()87 Collection<Resource> mapValues(); 88 } 89 public static abstract class AImplementation implements Implementation { 90 private final HashMap<String /* uniqueId */, SharedResourceRunner.Resource> sharedMap = new HashMap<String, SharedResourceRunner.Resource>(); 91 /** Called within synchronized block. Use w/ care! */ getSharedMap()92 public Map<String /* uniqueId */, SharedResourceRunner.Resource> getSharedMap() { 93 return sharedMap; 94 } 95 @Override clear()96 public final void clear() { 97 sharedMap.clear(); 98 } 99 @Override mapPut(final AbstractGraphicsDevice device, final SharedResourceRunner.Resource resource)100 public final SharedResourceRunner.Resource mapPut(final AbstractGraphicsDevice device, final SharedResourceRunner.Resource resource) { 101 return sharedMap.put(device.getUniqueID(), resource); 102 } 103 @Override mapGet(final AbstractGraphicsDevice device)104 public final SharedResourceRunner.Resource mapGet(final AbstractGraphicsDevice device) { 105 return sharedMap.get(device.getUniqueID()); 106 } 107 @Override mapValues()108 public final Collection<SharedResourceRunner.Resource> mapValues() { 109 return sharedMap.values(); 110 } 111 } 112 113 final HashSet<String> devicesTried = new HashSet<String>(); 114 final Implementation impl; 115 116 Thread thread; 117 boolean running; 118 boolean ready; 119 boolean shouldRelease; 120 AbstractGraphicsDevice initDevice; 121 AbstractGraphicsDevice releaseDevice; 122 getDeviceTried(final AbstractGraphicsDevice device)123 private boolean getDeviceTried(final AbstractGraphicsDevice device) { // synchronized call 124 return devicesTried.contains(device.getUniqueID()); 125 } addDeviceTried(final AbstractGraphicsDevice device)126 private void addDeviceTried(final AbstractGraphicsDevice device) { // synchronized call 127 devicesTried.add(device.getUniqueID()); 128 } removeDeviceTried(final AbstractGraphicsDevice device)129 private void removeDeviceTried(final AbstractGraphicsDevice device) { // synchronized call 130 devicesTried.remove(device.getUniqueID()); 131 } 132 SharedResourceRunner(final Implementation impl)133 public SharedResourceRunner(final Implementation impl) { 134 this.impl = impl; 135 resetState(); 136 } 137 resetState()138 private void resetState() { // synchronized call 139 devicesTried.clear(); 140 thread = null; 141 ready = false; 142 running = false; 143 shouldRelease = false; 144 initDevice = null; 145 releaseDevice = null; 146 } 147 148 /** 149 * Start the shared resource runner thread, if not running. 150 * <p> 151 * Validate the thread upfront and release all related resource if it was killed. 152 * </p> 153 * 154 * @return the shared resource runner thread. 155 */ start()156 public Thread start() { 157 synchronized (this) { 158 if(null != thread && !thread.isAlive()) { 159 // thread was killed unrecognized .. 160 if (DEBUG) { 161 System.err.println("SharedResourceRunner.start() - dead-old-thread cleanup - "+getThreadName()); 162 } 163 releaseSharedResources(); 164 thread = null; 165 running = false; 166 } 167 if( null == thread ) { 168 if (DEBUG) { 169 System.err.println("SharedResourceRunner.start() - start new Thread - "+getThreadName()); 170 } 171 resetState(); 172 thread = new InterruptSource.Thread(null, this, getThreadName()+"-SharedResourceRunner"); 173 thread.setDaemon(true); // Allow JVM to exit, even if this one is running 174 thread.start(); 175 try { 176 while (!running) { 177 this.wait(); 178 } 179 } catch (final InterruptedException ex) { 180 // Cleanup 181 shouldRelease = true; 182 this.notifyAll(); 183 throw new InterruptedRuntimeException(ex); 184 } 185 } 186 } 187 return thread; 188 } 189 stop()190 public void stop() { 191 synchronized (this) { 192 if(null != thread) { 193 if (DEBUG) { 194 System.err.println("SharedResourceRunner.stop() - "+getThreadName()); 195 } 196 synchronized (this) { 197 shouldRelease = true; 198 this.notifyAll(); 199 try { 200 while (running) { 201 this.wait(); 202 } 203 } catch (final InterruptedException ex) { 204 throw new InterruptedRuntimeException(ex); 205 } 206 } 207 } 208 } 209 } 210 getOrCreateShared(final AbstractGraphicsDevice device)211 public SharedResourceRunner.Resource getOrCreateShared(final AbstractGraphicsDevice device) { 212 SharedResourceRunner.Resource sr = null; 213 if(null != device) { 214 synchronized (this) { 215 start(); 216 sr = impl.mapGet(device); 217 if (null == sr) { 218 if ( !getDeviceTried(device) ) { 219 addDeviceTried(device); 220 if (DEBUG) { 221 System.err.println("SharedResourceRunner.getOrCreateShared() " + device + ": trying - "+getThreadName()); 222 ExceptionUtils.dumpStack(System.err); 223 } 224 if ( impl.isDeviceSupported(device) ) { 225 try { 226 doAndWait(device, null); 227 } catch (final InterruptedException ex) { 228 throw new InterruptedRuntimeException(ex); 229 } 230 sr = impl.mapGet(device); 231 } 232 if (DEBUG) { 233 System.err.println("SharedResourceRunner.getOrCreateShared() " + device + ": "+ ( ( null != sr ) ? "success" : "failed" ) +" - "+getThreadName()); 234 } 235 } 236 } 237 } 238 } 239 return sr; 240 } 241 releaseShared(final AbstractGraphicsDevice device)242 public SharedResourceRunner.Resource releaseShared(final AbstractGraphicsDevice device) { 243 SharedResourceRunner.Resource sr = null; 244 if(null != device) { 245 synchronized (this) { 246 sr = impl.mapGet(device); 247 if (null != sr) { 248 removeDeviceTried(device); 249 if (DEBUG) { 250 System.err.println("SharedResourceRunner.releaseShared() " + device + ": trying - "+getThreadName()); 251 } 252 try { 253 doAndWait(null, device); 254 } catch (final InterruptedException ex) { 255 throw new InterruptedRuntimeException(ex); 256 } 257 if (DEBUG) { 258 System.err.println("SharedResourceRunner.releaseShared() " + device + ": done - "+getThreadName()); 259 } 260 } 261 } 262 } 263 return sr; 264 } 265 doAndWait(final AbstractGraphicsDevice initDevice, final AbstractGraphicsDevice releaseDevice)266 private final void doAndWait(final AbstractGraphicsDevice initDevice, final AbstractGraphicsDevice releaseDevice) throws InterruptedException { 267 synchronized (this) { 268 // wait until thread becomes ready to init new device, 269 // pass the device and release the sync 270 final String threadName = getThreadName(); 271 if (DEBUG) { 272 System.err.println("SharedResourceRunner.doAndWait() START init: " + initDevice + ", release: "+releaseDevice+" - "+threadName); 273 } 274 try { 275 while (!ready && running) { 276 this.wait(); 277 } 278 if (DEBUG) { 279 System.err.println("SharedResourceRunner.doAndWait() set command: " + initDevice + ", release: "+releaseDevice+" - "+threadName); 280 } 281 this.initDevice = initDevice; 282 this.releaseDevice = releaseDevice; 283 this.notifyAll(); 284 285 // wait until thread has init/released the device 286 while ( running && ( !ready || null != this.initDevice || null != this.releaseDevice ) ) { 287 this.wait(); 288 } 289 } catch (final InterruptedException ex) { 290 final InterruptedException ex2 = SourcedInterruptedException.wrap(ex); 291 if (DEBUG) { 292 System.err.println("SharedResourceRunner.doAndWait() INTERRUPT init: " + initDevice + ", release: "+releaseDevice+" - "+threadName); 293 ExceptionUtils.dumpThrowable("", ex2); 294 } 295 // Cleanup initDevice due to exception! 296 final AbstractGraphicsDevice _initDevice = this.initDevice; 297 if( null != _initDevice ) { 298 if (DEBUG) { 299 System.err.println("SharedResourceRunner.doAndWait() Cleanup init: " + _initDevice + " -> release: "+this.releaseDevice+" - "+threadName); 300 } 301 this.releaseDevice = _initDevice; 302 this.initDevice = null; 303 this.notifyAll(); 304 } 305 throw ex2; 306 } 307 if (DEBUG) { 308 System.err.println("SharedResourceRunner.doAndWait() END init: " + initDevice + ", release: "+releaseDevice+" - "+threadName); 309 } 310 } 311 // done 312 } 313 314 @Override run()315 public final void run() { 316 final String threadName = getThreadName(); 317 318 if (DEBUG) { 319 System.err.println("SharedResourceRunner.run(): STARTED - " + threadName); 320 } 321 322 synchronized (this) { 323 running = true; 324 325 while (!shouldRelease) { 326 try { 327 // wait until call-thread issues stop or init/released a device 328 ready = true; 329 if (DEBUG) { 330 System.err.println("SharedResourceRunner.run(): READY - " + threadName); 331 } 332 notifyAll(); 333 while ( !shouldRelease && null == initDevice && null == releaseDevice ) { 334 this.wait(); 335 } 336 } catch (final InterruptedException ex) { 337 shouldRelease = true; 338 ExceptionUtils.dumpThrowable("handled", SourcedInterruptedException.wrap(ex)); // cancelable 339 } 340 ready = false; 341 342 if (!shouldRelease) { 343 if (DEBUG) { 344 System.err.println("SharedResourceRunner.run(): WOKE UP for device connection init: " + initDevice + 345 ", release: " + releaseDevice + " - " + threadName); 346 } 347 if(null != initDevice) { 348 if (DEBUG) { 349 System.err.println("SharedResourceRunner.run(): create Shared for: " + initDevice + " - " + threadName); 350 } 351 Resource sr = null; 352 try { 353 sr = impl.createSharedResource(initDevice); 354 } catch (final Exception e) { 355 ExceptionUtils.dumpThrowable("handled", e); 356 } 357 if (null != sr) { 358 impl.mapPut(initDevice, sr); 359 } 360 } 361 if(null != releaseDevice) { 362 if (DEBUG) { 363 System.err.println("SharedResourceRunner.run(): release Shared for: " + releaseDevice + " - " + threadName); 364 } 365 final Resource sr = impl.mapGet(releaseDevice); 366 if (null != sr) { 367 try { 368 impl.releaseSharedResource(sr); 369 } catch (final Exception e) { 370 ExceptionUtils.dumpThrowable("handled", e); 371 } finally { 372 impl.mapPut(releaseDevice, null); 373 } 374 } 375 } 376 } 377 initDevice = null; 378 releaseDevice = null; 379 } 380 381 if (DEBUG) { 382 System.err.println("SharedResourceRunner.run(): RELEASE START - " + threadName); 383 } 384 385 releaseSharedResources(); 386 387 if (DEBUG) { 388 System.err.println("SharedResourceRunner.run(): RELEASE END - " + threadName); 389 } 390 391 shouldRelease = false; 392 running = false; 393 thread = null; 394 notifyAll(); 395 } 396 } 397 releaseSharedResources()398 private void releaseSharedResources() { // synchronized call 399 devicesTried.clear(); 400 final Collection<Resource> sharedResources = impl.mapValues(); 401 for (final Iterator<Resource> iter = sharedResources.iterator(); iter.hasNext();) { 402 try { 403 impl.releaseSharedResource(iter.next()); 404 } catch (final Throwable t) { 405 ExceptionUtils.dumpThrowable("", t); 406 } 407 } 408 impl.clear(); 409 } 410 getThreadName()411 protected static String getThreadName() { return Thread.currentThread().getName(); } 412 } 413