From b9adfc2c67d2bc46cae887ed39a5953b1e74e96a Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Fri, 8 Oct 2010 01:56:46 +0200
Subject: Fix: JOGL GLContextLock starvation; Tighten NEWT/AWT focus unit tests

Fix: JOGL GLContextLock starvation
    - Apply changes made in RecursiveToolkitLock (c8a9c59e4838cd43090378a7ed60544449472801),
      ie notifyAll() -> notify(), plus sync (flow/mem) usage.

Fix: Tighten NEWT/AWT focus unit tests
    - AWTRobotUtil.requestFocusAndWait() waits for EventCountAdapter gain and lost focus as well.
    - In case of NewtCanvasAWT, additionally wait for it's lost focus
---
 .../com/jogamp/opengl/impl/GLContextLock.java      | 75 +++++++++++++---------
 1 file changed, 43 insertions(+), 32 deletions(-)

(limited to 'src/jogl/classes/com/jogamp/opengl/impl/GLContextLock.java')

diff --git a/src/jogl/classes/com/jogamp/opengl/impl/GLContextLock.java b/src/jogl/classes/com/jogamp/opengl/impl/GLContextLock.java
index 56a5b023f..6d0c5d984 100644
--- a/src/jogl/classes/com/jogamp/opengl/impl/GLContextLock.java
+++ b/src/jogl/classes/com/jogamp/opengl/impl/GLContextLock.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright (c) 2010 JogAmp Community. All rights reserved.
  * 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -49,36 +50,38 @@ import javax.media.opengl.*;
     be raised. */
 
 public class GLContextLock {
-  private Object lock = new Object();
-  private Thread owner;
-  private boolean failFastMode = true;
-  private volatile int waiters;
+  static class SyncData {
+      Thread owner = null;
+      boolean failFastMode = true;
+      int waiters = 0;
+  }
+  private SyncData sdata = new SyncData(); // synchronized (flow/mem)  mutable access
 
   /** Locks this GLContextLock on the current thread. If fail fast
       mode is enabled and the GLContextLock is already owned by
       another thread, throws GLException. */
-  public void lock() throws GLException {
-    synchronized(lock) {
+  public final void lock() throws GLException {
+    synchronized(sdata) {
       Thread current = Thread.currentThread();
-      if (owner == null) {
-        owner = current;
-      } else if (owner != current) {
-        while (owner != null) {
-          if (failFastMode) {
+      if (sdata.owner == null) {
+        sdata.owner = current;
+      } else if (sdata.owner != current) {
+        while (sdata.owner != null) {
+          if (sdata.failFastMode) {
             throw new GLException("Attempt to make context current on thread " + current +
-                                  " which is already current on thread " + owner);
+                                  " which is already current on thread " + sdata.owner);
           } else {
             try {
-              ++waiters;
-              lock.wait();
+              ++sdata.waiters;
+              sdata.wait();
             } catch (InterruptedException e) {
               throw new GLException(e);
             } finally {
-              --waiters;
+              --sdata.waiters;
             }
           }
         }
-        owner = current;
+        sdata.owner = current;
       } else {
         throw new GLException("Attempt to make the same context current twice on thread " + current);
       }
@@ -86,16 +89,18 @@ public class GLContextLock {
   }
 
   /** Unlocks this GLContextLock. */
-  public void unlock() throws GLException {
-    synchronized (lock) {
+  public final void unlock() throws GLException {
+    synchronized (sdata) {
       Thread current = Thread.currentThread();
-      if (owner == current) {
-        owner = null;
-        lock.notifyAll();
+      if (sdata.owner == current) {
+        sdata.owner = null;
+        // Assuming notify() implementation weaks up the longest waiting thread, to avoid starvation. 
+        // Otherwise we would need to have a Thread queue implemented, using sleep(timeout) and interrupt.
+        sdata.notify();
       } else {
-        if (owner != null) {
+        if (sdata.owner != null) {
           throw new GLException("Attempt by thread " + current +
-                                " to release context owned by thread " + owner);
+                                " to release context owned by thread " + sdata.owner);
         } else {
           throw new GLException("Attempt by thread " + current +
                                 " to release unowned context");
@@ -105,22 +110,28 @@ public class GLContextLock {
   }
 
   /** Indicates whether this lock is held by the current thread. */
-  public boolean isHeld() {
-    synchronized(lock) {
+  public final boolean isHeld() {
+    synchronized(sdata) {
       Thread current = Thread.currentThread();
-      return (owner == current);
+      return (sdata.owner == current);
     }
   }
 
-  public void setFailFastMode(boolean onOrOff) {
-    failFastMode = onOrOff;
+  public final void setFailFastMode(boolean onOrOff) {
+    synchronized(sdata) {
+        sdata.failFastMode = onOrOff;
+    }
   }
 
-  public boolean getFailFastMode() {
-    return failFastMode;
+  public final boolean getFailFastMode() {
+    synchronized(sdata) {
+        return sdata.failFastMode;
+    }
   }
 
-  public boolean hasWaiters() {
-    return (waiters != 0);
+  public final boolean hasWaiters() {
+    synchronized(sdata) {
+        return (sdata.waiters != 0);
+    }
   }
 }
-- 
cgit v1.2.3