commit d2039fdd2189a286a27ca4d952df0c1060f22738
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Apr 10 00:14:31 2019 +0100

    Improve locking in DistortedObject.
    DistortedMaster: slaves can be NULL !

diff --git a/src/main/java/org/distorted/library/main/DistortedMaster.java b/src/main/java/org/distorted/library/main/DistortedMaster.java
index 32e112e..5cad657 100644
--- a/src/main/java/org/distorted/library/main/DistortedMaster.java
+++ b/src/main/java/org/distorted/library/main/DistortedMaster.java
@@ -58,7 +58,8 @@ public class DistortedMaster
       for(int i=0; i<num; i++)
         {
         slave = mSlaves.remove(0);
-        slave.doWork();
+
+        if( slave!=null ) slave.doWork();
         }
       }
     catch(IndexOutOfBoundsException ie)
diff --git a/src/main/java/org/distorted/library/main/DistortedObject.java b/src/main/java/org/distorted/library/main/DistortedObject.java
index 5b41596..296741b 100644
--- a/src/main/java/org/distorted/library/main/DistortedObject.java
+++ b/src/main/java/org/distorted/library/main/DistortedObject.java
@@ -65,6 +65,9 @@ abstract class DistortedObject
   private static long mNextClientID = 0;
   private static long mNextSystemID = 0;
 
+  private final static Object mDoneLock = new Object();
+  private final static Object mToDoLock = new Object();
+
   private long mID;
   private int mType;
 
@@ -78,34 +81,35 @@ abstract class DistortedObject
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // must be called from a thread holding OpenGL Context
 
-  static synchronized boolean toDo()
+  static boolean toDo()
     {
     if( mToDo )
       {
       Job job;
       DistortedObject object;
 
-      for(Long key: mToDoMap.keySet())
+      synchronized(mToDoLock)
         {
-        job = mToDoMap.get(key);
-        object = job.object;
-
-        //android.util.Log.d("Object", object.getClass().getSimpleName()+"  ---> need to "
-        //                  +(job.action==JOB_CREATE ? "create":"delete")+" objectID="+object.mDistortedEffectsID );
-
-        if( job.action==JOB_CREATE )
-          {
-          object.create();
-          mDoneList.add(object);
-          }
-        else if( job.action==JOB_DELETE )
+        for(Long key: mToDoMap.keySet())
           {
-          object.delete();
+          job = mToDoMap.get(key);
+          object = job.object;
+
+          if( job.action==JOB_CREATE )
+            {
+            object.create();
+            mDoneList.add(object);
+            }
+          else if( job.action==JOB_DELETE )
+            {
+            object.delete();
+            }
           }
+
+        mToDoMap.clear();
+        mToDo = false;
         }
 
-      mToDoMap.clear();
-      mToDo = false;
       return true;
       }
 
@@ -114,37 +118,50 @@ abstract class DistortedObject
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  static synchronized void onPause()
+  static void onPause()
     {
     DistortedObject object;
-    int num = mDoneList.size();
 
-    try
+    synchronized(mDoneLock)
       {
-      for (int i = 0; i < num; i++)
+      synchronized(mToDoLock)
         {
-        object = mDoneList.removeFirst();
-        mToDoMap.put(object.mID, object.new Job(object, JOB_CREATE));
-        object.recreate();
+        int num = mDoneList.size();
+
+        try
+          {
+          for (int i = 0; i < num; i++)
+            {
+            object = mDoneList.removeFirst();
+            mToDoMap.put(object.mID, object.new Job(object, JOB_CREATE));
+            object.recreate();
+            }
+          }
+        catch( Exception ex )
+          {
+          // something else removed an object in the meantime; ignore
+          }
         }
       }
-    catch( Exception ex )
-      {
-      // something else removed an object in the meantime; ignore
-      }
 
     mToDo = true;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  static synchronized void onDestroy()
+  static void onDestroy()
     {
-    mToDoMap.clear();
-    mDoneList.clear();
+    synchronized(mToDoLock)
+      {
+      synchronized(mDoneLock)
+        {
+        mToDoMap.clear();
+        mDoneList.clear();
 
-    mToDo = true;
-    mNextClientID = 0;
+        mToDo = true;
+        mNextClientID = 0;
+        }
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -191,7 +208,6 @@ abstract class DistortedObject
     android.util.Log.e("Object", str+printDetails()+msg);
     }
 
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   DistortedObject(int create, int type)
@@ -201,18 +217,28 @@ abstract class DistortedObject
 
     if( create!=DONT_CREATE )
       {
-      mToDoMap.put(mID, new Job(this,JOB_CREATE) );
-      mToDo = true;
+      synchronized(mToDoLock)
+        {
+        mToDoMap.put(mID, new Job(this,JOB_CREATE) );
+        mToDo = true;
+        }
       }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  synchronized void markForCreation()
+  void markForCreation()
     {
-    mDoneList.remove(this);
-    mToDoMap.put(mID, new Job(this,JOB_CREATE) );
-    mToDo = true;
+    synchronized(mDoneLock)
+      {
+      mDoneList.remove(this);
+      }
+
+    synchronized(mToDoLock)
+      {
+      mToDoMap.put(mID, new Job(this,JOB_CREATE) );
+      mToDo = true;
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -221,11 +247,18 @@ abstract class DistortedObject
 /**
  * Mark the underlying OpenGL object for deletion. Actual deletion will take place on the next render.
  */
-  synchronized public void markForDeletion()
+  public void markForDeletion()
     {
-    mDoneList.remove(this);
-    mToDoMap.put(mID, new Job(this,JOB_DELETE) );
-    mToDo = true;
+    synchronized(mDoneLock)
+      {
+      mDoneList.remove(this);
+      }
+
+    synchronized(mToDoLock)
+      {
+      mToDoMap.put(mID, new Job(this,JOB_DELETE) );
+      mToDo = true;
+      }
     }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
