From 4153d25f01f9f840df0988340c759b7d29dfa19d Mon Sep 17 00:00:00 2001
From: CoreRasurae <luis.p.mendes@gmail.com>
Date: Mon, 23 Apr 2018 23:39:39 +0100
Subject: [PATCH] Update: Enable kernel profiling and execution simultaneously
 on multiple devices - part2

---
 .../internal/kernel/KernelManager.java        |  2 +-
 .../internal/kernel/KernelPreferences.java    | 38 +++++++++----------
 .../aparapi/internal/kernel/KernelRunner.java |  8 ++--
 3 files changed, 23 insertions(+), 25 deletions(-)

diff --git a/src/main/java/com/aparapi/internal/kernel/KernelManager.java b/src/main/java/com/aparapi/internal/kernel/KernelManager.java
index 7640a9af..35a20e2c 100644
--- a/src/main/java/com/aparapi/internal/kernel/KernelManager.java
+++ b/src/main/java/com/aparapi/internal/kernel/KernelManager.java
@@ -48,7 +48,7 @@ public class KernelManager {
 
    /**
     * Default KernelManager initialization.<br/>
-    * Convenience method for being overriden to an empty implementation, so that derived 
+    * Convenience method for being overridden to an empty implementation, so that derived 
     * KernelManager classes can provide non static parameters to their constructors.
     */
    protected void setup() {
diff --git a/src/main/java/com/aparapi/internal/kernel/KernelPreferences.java b/src/main/java/com/aparapi/internal/kernel/KernelPreferences.java
index 7c553390..d8cc685d 100644
--- a/src/main/java/com/aparapi/internal/kernel/KernelPreferences.java
+++ b/src/main/java/com/aparapi/internal/kernel/KernelPreferences.java
@@ -19,11 +19,15 @@ import com.aparapi.*;
 import com.aparapi.device.*;
 
 import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
 
+/**
+ * Thread safe class holding the kernel preferences for a given kernel class.
+ */
 public class KernelPreferences {
    private final Class<? extends Kernel> kernelClass;
    private final KernelManager manager;
-   private volatile LinkedList<Device> preferredDevices = null;
+   private final AtomicReference<LinkedList<Device>> preferredDevices = new AtomicReference<>(null);
    private final LinkedHashSet<Device> failedDevices = new LinkedHashSet<>();
 
    public KernelPreferences(KernelManager manager, Class<? extends Kernel> kernelClass) {
@@ -39,14 +43,16 @@ public class KernelPreferences {
    public List<Device> getPreferredDevices(Kernel kernel) {
       maybeSetUpDefaultPreferredDevices();
 
+      ArrayList<Device> copy;
+      synchronized (this) {
+         copy = new ArrayList<>(preferredDevices.get());
+      }
+
       if (kernel == null) {
-         return Collections.unmodifiableList(preferredDevices);
+         return Collections.unmodifiableList(copy);
       }
+      
       List<Device> localPreferredDevices = new ArrayList<>();
-      ArrayList<Device> copy;
-      synchronized (preferredDevices) {
-         copy = new ArrayList(preferredDevices);
-      }
       for (Device device : copy) {
          if (kernel.isAllowDevice(device)) {
             localPreferredDevices.add(device);
@@ -56,12 +62,12 @@ public class KernelPreferences {
    }
 
    synchronized void setPreferredDevices(LinkedHashSet<Device> _preferredDevices) {
-      if (preferredDevices != null) {
-         preferredDevices.clear();
-         preferredDevices.addAll(_preferredDevices);
+      if (preferredDevices.get() != null) {
+         preferredDevices.get().clear();
+         preferredDevices.get().addAll(_preferredDevices);
       }
       else {
-         preferredDevices = new LinkedList<>(_preferredDevices);
+         preferredDevices.set(new LinkedList<>(_preferredDevices));
       }
       failedDevices.clear();
    }
@@ -72,19 +78,13 @@ public class KernelPreferences {
    }
 
    synchronized void markPreferredDeviceFailed() {
-      if (preferredDevices.size() > 0) {
-         failedDevices.add(preferredDevices.remove(0));
+      if (preferredDevices.get().size() > 0) {
+         failedDevices.add(preferredDevices.get().remove(0));
       }
    }
 
    private void maybeSetUpDefaultPreferredDevices() {
-      if (preferredDevices == null) {
-         synchronized (this) {
-            if (preferredDevices == null) {
-               preferredDevices = new LinkedList<>(manager.getDefaultPreferences().getPreferredDevices(null));
-            }
-         }
-      }
+	   preferredDevices.compareAndSet(null, new LinkedList<>(manager.getDefaultPreferences().getPreferredDevices(null)));
    }
 
    public List<Device> getFailedDevices() {
diff --git a/src/main/java/com/aparapi/internal/kernel/KernelRunner.java b/src/main/java/com/aparapi/internal/kernel/KernelRunner.java
index fdb45e3e..58960925 100644
--- a/src/main/java/com/aparapi/internal/kernel/KernelRunner.java
+++ b/src/main/java/com/aparapi/internal/kernel/KernelRunner.java
@@ -1307,6 +1307,8 @@ public class KernelRunner extends KernelRunnerJNI{
             logger.warning("Device failed for " + kernel + ": " + _exception.getMessage());
          }
 
+         //This method is synchronized thus ensuring thread safety on concurrent executions of the same kernel class,
+         //since preferences is shared between such threads.
          preferences.markPreferredDeviceFailed();
 
 //         Device nextDevice = preferences.getPreferredDevice(kernel);
@@ -1337,11 +1339,7 @@ public class KernelRunner extends KernelRunnerJNI{
          boolean legacyExecutionMode = kernel.getExecutionMode() != Kernel.EXECUTION_MODE.AUTO;
 
          ExecutionSettings settings = new ExecutionSettings(preferences, profile, _entrypoint, _range, _passes, legacyExecutionMode);
-         // Two Kernels of the same class share the same KernelPreferences object, and since failure (fallback) generally mutates
-         // the preferences object, we must lock it. Note this prevents two Kernels of the same class executing simultaneously.
-         synchronized (preferences) {
-            return executeInternalOuter(settings);
-         }
+         return executeInternalOuter(settings);
       } finally {
          executing = false;
          clearCancelMultiPass();
-- 
GitLab