From 9291731fec7103301ba36511ceb4375f63170e5c Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Sun, 11 Dec 2011 04:47:27 +0100
Subject: Fix concurrency bug of GLProfile initialization ; Fix
 SharedResourceRunner 'dead' thread (Applets)

GLDrawableFactory:
  - clarify: public getWasSharedContextCreated(..) -> protected createSharedResource(..)
  - add: getSharesResourceThread()

GLProfile:
  - proper locking of initSingletion(..) path:
    - Use RecursiveThreadGroupLock and add/remove GLDrawableFactory's sharesResourceThread
      while creating it's the sharedResource.
      This simplifies and fixes GLProfile's locking code.

  - Fix and simplify initSingleton(boolean) API doc - mark it deprecated.

  - Add initSingleton() for controlled initialization only, pairing w/ shutdown(..)

Remove initSingleton(boolean) calls in code and test!

+++

Fix SharedResourceRunner 'dead' thread (Applets)
  In Applets, stopping an Applet makes the browser Java plugin interrupting
  and killing all related threads, including our SharedResourceRunner thread.

  - Validate whether the shared resource thread is alive

  - Catch interruption in shared resource thread and assume it's a kill signal

  - releaseSharedResource: clear devicesTried set
---
 src/jogl/classes/jogamp/opengl/GLContextImpl.java  |   2 -
 .../jogamp/opengl/SharedResourceRunner.java        | 149 +++++++++++++++------
 .../classes/jogamp/opengl/awt/VersionApplet.java   |   2 -
 .../jogamp/opengl/egl/EGLDrawableFactory.java      |   6 +-
 .../macosx/cgl/MacOSXCGLDrawableFactory.java       |   8 +-
 .../windows/wgl/WindowsWGLDrawableFactory.java     |  17 ++-
 .../opengl/x11/glx/X11GLXDrawableFactory.java      |  17 ++-
 7 files changed, 134 insertions(+), 67 deletions(-)

(limited to 'src/jogl/classes/jogamp/opengl')

diff --git a/src/jogl/classes/jogamp/opengl/GLContextImpl.java b/src/jogl/classes/jogamp/opengl/GLContextImpl.java
index 5ea1a9bac..0d372587e 100644
--- a/src/jogl/classes/jogamp/opengl/GLContextImpl.java
+++ b/src/jogl/classes/jogamp/opengl/GLContextImpl.java
@@ -459,8 +459,6 @@ public abstract class GLContextImpl extends GLContext {
               throw new GLException("drawable has invalid handle: "+drawable);
           }
           if (!isCreated()) {
-            GLProfile.initProfiles(
-                    getGLDrawable().getNativeSurface().getGraphicsConfiguration().getScreen().getDevice());
             final GLContextImpl shareWith = (GLContextImpl) GLContextShareSet.getShareContext(this);
             if (null != shareWith) {
                 shareWith.getDrawableImpl().lockSurface();
diff --git a/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java b/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java
index e24a9f8ed..a33e03a1a 100644
--- a/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java
+++ b/src/jogl/classes/jogamp/opengl/SharedResourceRunner.java
@@ -44,6 +44,10 @@ public class SharedResourceRunner implements Runnable {
     }
 
     public static interface Implementation {
+        /**
+         * @param connection for creation a {@link AbstractGraphicsDevice} instance. 
+         * @return A new shared resource instance 
+         */
         Resource createSharedResource(String connection);
         void releaseSharedResource(Resource shared);
         void clear();
@@ -53,15 +57,15 @@ public class SharedResourceRunner implements Runnable {
         Collection<Resource> mapValues();
     }
 
-    Implementation impl = null;
+    final HashSet<String> devicesTried = new HashSet<String>();
+    final Implementation impl;
 
-    boolean ready = false;
-    boolean released = false;
-    boolean shouldRelease = false;
-    String initConnection = null;
-    String releaseConnection = null;
-
-    HashSet<String> devicesTried = new HashSet<String>();
+    Thread thread;
+    boolean ready;
+    boolean released;
+    boolean shouldRelease;
+    String initConnection;
+    String releaseConnection;
 
     private boolean getDeviceTried(String connection) {
         synchronized (devicesTried) {
@@ -81,22 +85,82 @@ public class SharedResourceRunner implements Runnable {
 
     public SharedResourceRunner(Implementation impl) {
         this.impl = impl;
+        resetState();
+    }
+    
+    private void resetState() {
+        devicesTried.clear();
+        thread = null;
+        ready = false;
+        released = false;
+        shouldRelease = false;
+        initConnection = null;
+        releaseConnection = null;
     }
 
+    /** 
+     * Start the shared resource runner thread, if not running.
+     * <p>
+     * Validate the thread upfront and release all related resource if it was killed.
+     * </p>
+     * 
+     * @return the shared resource runner thread.
+     */
+    public Thread start() {
+        if(null != thread && !thread.isAlive()) {
+            // thread was killed unrecognized ..
+            if (DEBUG) {
+                System.err.println("SharedResourceRunner.start() - dead-old-thread cleanup - "+Thread.currentThread().getName());
+            }
+            releaseSharedResources();
+            thread = null;
+        }        
+        if(null == thread) {
+            if (DEBUG) {
+                System.err.println("SharedResourceRunner.start() - start new Thread - "+Thread.currentThread().getName());
+            }
+            resetState();
+            thread = new Thread(this, Thread.currentThread().getName()+"-SharedResourceRunner");
+            thread.setDaemon(true); // Allow JVM to exit, even if this one is running
+            thread.start();
+        }
+        return thread;
+    }
+    
+    public void stop() {
+        if(null != thread) {
+            if (DEBUG) {
+                System.err.println("SharedResourceRunner.stop() - "+Thread.currentThread().getName());
+            }
+            synchronized (this) {
+                shouldRelease = true;
+                this.notifyAll();
+    
+                while (!released) {
+                    try {
+                        this.wait();
+                    } catch (InterruptedException ex) {
+                    }
+                }
+            }
+        }
+    }
+    
     public SharedResourceRunner.Resource getOrCreateShared(AbstractGraphicsDevice device) {
         SharedResourceRunner.Resource sr = null;
         if(null != device) {
+            start();
             final String connection = device.getConnection();
             sr = impl.mapGet(connection);
             if (null == sr && !getDeviceTried(connection)) {
                 addDeviceTried(connection);
                 if (DEBUG) {
-                    System.err.println("getOrCreateShared() " + connection + ": trying");
+                    System.err.println("SharedResourceRunner.getOrCreateShared() " + connection + ": trying - "+Thread.currentThread().getName());
                 }
                 doAndWait(connection, null);
                 sr = impl.mapGet(connection);
                 if (DEBUG) {
-                    System.err.println("getOrCreateShared() " + connection + ": "+ ( ( null != sr ) ? "success" : "failed" ) );               
+                    System.err.println("SharedResourceRunner.getOrCreateShared() " + connection + ": "+ ( ( null != sr ) ? "success" : "failed" ) +" - "+Thread.currentThread().getName());
                 }
             }
         }
@@ -111,11 +175,11 @@ public class SharedResourceRunner implements Runnable {
             if (null != sr) {
                 removeDeviceTried(connection);
                 if (DEBUG) {
-                    System.err.println("releaseShared() " + connection + ": trying");
+                    System.err.println("SharedResourceRunner.releaseShared() " + connection + ": trying - "+Thread.currentThread().getName());
                 }
                 doAndWait(null, connection);
                 if (DEBUG) {
-                    System.err.println("releaseShared() " + connection + ": done");
+                    System.err.println("SharedResourceRunner.releaseShared() " + connection + ": done - "+Thread.currentThread().getName());
                 }
             }
         }
@@ -125,9 +189,9 @@ public class SharedResourceRunner implements Runnable {
     private final void doAndWait(String initConnection, String releaseConnection) {
         // wait until thread becomes ready to init new device,
         // pass the device and release the sync
-        String threadName = Thread.currentThread().getName();
+        final String threadName = Thread.currentThread().getName();
         if (DEBUG) {
-            System.err.println(threadName + " doAndWait START init: " + initConnection + ", release: "+releaseConnection);
+            System.err.println("SharedResourceRunner.doAndWait() START init: " + initConnection + ", release: "+releaseConnection+" - "+threadName);
         }
         synchronized (this) {
             while (!ready) {
@@ -137,7 +201,7 @@ public class SharedResourceRunner implements Runnable {
                 }
             }
             if (DEBUG) {
-                System.err.println(threadName + " initializeAndWait set command init: " + initConnection + ", release: "+releaseConnection);
+                System.err.println("SharedResourceRunner.doAndWait() set command: " + initConnection + ", release: "+releaseConnection+" - "+threadName);
             }
             this.initConnection = initConnection;
             this.releaseConnection = releaseConnection;
@@ -151,31 +215,17 @@ public class SharedResourceRunner implements Runnable {
                 }
             }
             if (DEBUG) {
-                System.err.println(threadName + " initializeAndWait END init: " + initConnection + ", release: "+releaseConnection);
+                System.err.println("SharedResourceRunner.initializeAndWait END init: " + initConnection + ", release: "+releaseConnection+" - "+threadName);
             }
         }
         // done
     }
 
-    public final void releaseAndWait() {
-        synchronized (this) {
-            shouldRelease = true;
-            this.notifyAll();
-
-            while (!released) {
-                try {
-                    this.wait();
-                } catch (InterruptedException ex) {
-                }
-            }
-        }
-    }
-
     public final void run() {
-        String threadName = Thread.currentThread().getName();
+        final String threadName = Thread.currentThread().getName();
 
         if (DEBUG) {
-            System.err.println(threadName + " STARTED");
+            System.err.println("SharedResourceRunner.run(): STARTED - " + threadName);
         }
 
         synchronized (this) {
@@ -184,21 +234,27 @@ public class SharedResourceRunner implements Runnable {
                     // wait for stop or init
                     ready = true;
                     if (DEBUG) {
-                        System.err.println(threadName + " -> ready");
+                        System.err.println("SharedResourceRunner.run(): READY - " + threadName);
                     }
                     notifyAll();
                     this.wait();
-                } catch (InterruptedException ex) { }
+                } catch (InterruptedException ex) { 
+                    shouldRelease = true;
+                    if(DEBUG) {
+                        System.err.println("SharedResourceRunner.run(): INTERRUPTED - "+Thread.currentThread().getName());                        
+                        ex.printStackTrace();
+                    }
+                }
                 ready = false;
 
                 if (!shouldRelease) {
                     if (DEBUG) {
-                        System.err.println(threadName + " woke up for device connection init: " + initConnection +
-                                                        ", release: " + releaseConnection);
+                        System.err.println("SharedResourceRunner.run(): WOKE UP for device connection init: " + initConnection +
+                                           ", release: " + releaseConnection + " - " + threadName);
                     }
                     if(null != initConnection) {
                         if (DEBUG) {
-                            System.err.println(threadName + " create Shared for: " + initConnection);
+                            System.err.println("SharedResourceRunner.run(): create Shared for: " + initConnection + " - " + threadName);
                         }
                         Resource sr = null;
                         try {
@@ -212,7 +268,7 @@ public class SharedResourceRunner implements Runnable {
                     }
                     if(null != releaseConnection) {
                         if (DEBUG) {
-                            System.err.println(threadName + " release Shared for: " + releaseConnection);
+                            System.err.println("SharedResourceRunner.run(): release Shared for: " + releaseConnection + " - " + threadName);
                         }
                         Resource sr = impl.mapGet(releaseConnection);
                         if (null != sr) {
@@ -230,25 +286,34 @@ public class SharedResourceRunner implements Runnable {
             }
 
             if (DEBUG) {
-                System.err.println(threadName + " release START");
+                System.err.println("SharedResourceRunner.run(): RELEASE START - " + threadName);
             }
 
             releaseSharedResources();
 
             if (DEBUG) {
-                System.err.println(threadName + " release END");
+                System.err.println("SharedResourceRunner.run(): RELEASE END - " + threadName);
             }
 
+            shouldRelease = false;
             released = true;
-            ready = false;
+            thread = null;
             notifyAll();
         }
     }
 
     private void releaseSharedResources() {
+        synchronized (devicesTried) {
+            devicesTried.clear();
+        }
         Collection<Resource> sharedResources = impl.mapValues();
         for (Iterator<Resource> iter = sharedResources.iterator(); iter.hasNext();) {
-            impl.releaseSharedResource(iter.next());
+            try {
+                impl.releaseSharedResource(iter.next());
+            } catch (Throwable t) {
+                System.err.println("Catched Exception: "+t.getStackTrace()+" - "+Thread.currentThread().getName());
+                t.printStackTrace();
+            }
         }
         impl.clear();
     }
diff --git a/src/jogl/classes/jogamp/opengl/awt/VersionApplet.java b/src/jogl/classes/jogamp/opengl/awt/VersionApplet.java
index b7c90a18b..55fb3f9a2 100644
--- a/src/jogl/classes/jogamp/opengl/awt/VersionApplet.java
+++ b/src/jogl/classes/jogamp/opengl/awt/VersionApplet.java
@@ -131,7 +131,6 @@ public class VersionApplet extends Applet {
 
   public void init() {
     System.err.println("VersionApplet: init() - begin");
-    GLProfile.initSingleton(false);
     my_init();
     System.err.println("VersionApplet: init() - end");
   }
@@ -151,7 +150,6 @@ public class VersionApplet extends Applet {
   public void destroy() {
     System.err.println("VersionApplet: destroy() - start");
     my_release();
-    GLProfile.shutdown(GLProfile.ShutdownType.SHARED_ONLY);
     System.err.println("VersionApplet: destroy() - end");
   }
 
diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java
index f6988a73f..cd6d61a22 100644
--- a/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java
+++ b/src/jogl/classes/jogamp/opengl/egl/EGLDrawableFactory.java
@@ -232,7 +232,11 @@ public class EGLDrawableFactory extends GLDrawableFactoryImpl {
         return sr;
     }
 
-    public final boolean getWasSharedContextCreated(AbstractGraphicsDevice device) {
+    protected final Thread getSharedResourceThread() {
+        return null;
+    }
+    
+    protected final boolean createSharedResource(AbstractGraphicsDevice device) {
         try {
             SharedResource sr = getOrCreateEGLSharedResource(device);
             if(null!=sr) {
diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java
index 45445067e..ed53a9ee5 100644
--- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java
+++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLDrawableFactory.java
@@ -252,8 +252,12 @@ public class MacOSXCGLDrawableFactory extends GLDrawableFactoryImpl {
     }
     return sr;
   }
-    
-  public final boolean getWasSharedContextCreated(AbstractGraphicsDevice device) {
+   
+  protected final Thread getSharedResourceThread() {
+    return null;
+  }
+  
+  protected final boolean createSharedResource(AbstractGraphicsDevice device) {
     try {
         SharedResource sr = getOrCreateOSXSharedResource(device);
         if(null!=sr) {
diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java
index 917402b0a..43c16240d 100644
--- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java
+++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java
@@ -119,17 +119,14 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl {
     
         // Init shared resources off thread
         // Will be released via ShutdownHook
-        sharedResourceImpl = new SharedResourceImplementation();
-        sharedResourceRunner = new SharedResourceRunner(sharedResourceImpl);
-        sharedResourceThread = new Thread(sharedResourceRunner, Thread.currentThread().getName()+"-SharedResourceRunner");
-        sharedResourceThread.setDaemon(true); // Allow JVM to exit, even if this one is running
-        sharedResourceThread.start();
+        sharedResourceRunner = new SharedResourceRunner(new SharedResourceImplementation());
+        sharedResourceRunner.start();
     }
   }
 
   protected final void destroy(ShutdownType shutdownType) {
     if(null != sharedResourceRunner) {
-        sharedResourceRunner.releaseAndWait();
+        sharedResourceRunner.stop();
         sharedResourceRunner = null;
     }
     if(null != sharedMap) {
@@ -153,9 +150,7 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl {
   }
 
   private WindowsGraphicsDevice defaultDevice;
-  private SharedResourceImplementation sharedResourceImpl;
   private SharedResourceRunner sharedResourceRunner;
-  private Thread sharedResourceThread;
   private HashMap<String /*connection*/, SharedResourceRunner.Resource> sharedMap;
 
   private long processAffinityChanges = 0;
@@ -397,7 +392,11 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl {
   final static String WGL_ARB_make_current_read = "WGL_ARB_make_current_read";
   final static String wglMakeContextCurrent = "wglMakeContextCurrent";
 
-  public final boolean getWasSharedContextCreated(AbstractGraphicsDevice device) {
+  protected final Thread getSharedResourceThread() {
+    return sharedResourceRunner.start();
+  }
+  
+  protected final boolean createSharedResource(AbstractGraphicsDevice device) {
     try {
         SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(device);
         if(null!=sr) {
diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java
index 96153dd27..235aea499 100644
--- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java
+++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXDrawableFactory.java
@@ -111,17 +111,14 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl {
         
         // Init shared resources off thread
         // Will be released via ShutdownHook
-        sharedResourceImpl = new SharedResourceImplementation();
-        sharedResourceRunner = new SharedResourceRunner(sharedResourceImpl);
-        sharedResourceThread = new Thread(sharedResourceRunner, Thread.currentThread().getName()+"-SharedResourceRunner");
-        sharedResourceThread.setDaemon(true); // Allow JVM to exit, even if this one is running
-        sharedResourceThread.start();        
+        sharedResourceRunner = new SharedResourceRunner(new SharedResourceImplementation());
+        sharedResourceRunner.start();
     }    
   }
 
   protected final void destroy(ShutdownType shutdownType) {
     if(null != sharedResourceRunner) {
-        sharedResourceRunner.releaseAndWait();
+        sharedResourceRunner.stop();
         sharedResourceRunner = null;
     }
     if(null != sharedMap) {
@@ -147,9 +144,7 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl {
   }
 
   private X11GraphicsDevice defaultDevice;
-  private SharedResourceImplementation sharedResourceImpl;
   private SharedResourceRunner sharedResourceRunner;
-  private Thread sharedResourceThread;
   private HashMap<String /* connection */, SharedResourceRunner.Resource> sharedMap;  
 
   static class SharedResource implements SharedResourceRunner.Resource {
@@ -322,7 +317,11 @@ public class X11GLXDrawableFactory extends GLDrawableFactoryImpl {
       return false;
   }
 
-  public final boolean getWasSharedContextCreated(AbstractGraphicsDevice device) {
+  protected final Thread getSharedResourceThread() {
+    return sharedResourceRunner.start();
+  }
+    
+  protected final boolean createSharedResource(AbstractGraphicsDevice device) {
     try {
         SharedResourceRunner.Resource sr = sharedResourceRunner.getOrCreateShared(device);
         if(null!=sr) {
-- 
cgit v1.2.3