sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Fix an application freeze when an image operation executed by `TileOpExecutor` recursively uses `TileOpExecutor` on another image.
Date Wed, 08 Apr 2020 12:43:17 GMT
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 4c63b80  Fix an application freeze when an image operation executed by `TileOpExecutor`
recursively uses `TileOpExecutor` on another image.
4c63b80 is described below

commit 4c63b80824fd66cd6600a5fb322b3125e8057e96
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Wed Apr 8 14:41:37 2020 +0200

    Fix an application freeze when an image operation executed by `TileOpExecutor` recursively
uses `TileOpExecutor` on another image.
---
 .../sis/internal/coverage/j2d/TileOpExecutor.java  | 35 +++++++++++++++++++++-
 .../apache/sis/internal/system/CommonExecutor.java | 23 ++++++++++++++
 2 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TileOpExecutor.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TileOpExecutor.java
index 5461ca4..5224ca9 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TileOpExecutor.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TileOpExecutor.java
@@ -33,10 +33,12 @@ import java.awt.image.RenderedImage;
 import java.awt.image.WritableRaster;
 import java.awt.image.WritableRenderedImage;
 import java.awt.image.ImagingOpException;
+import org.apache.sis.util.Classes;
 import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.internal.feature.Resources;
 import org.apache.sis.internal.system.CommonExecutor;
+import org.apache.sis.internal.util.Strings;
 
 
 /**
@@ -596,6 +598,7 @@ public class TileOpExecutor {
          *
          * @param  <R>           the final type of the result. This is often the same
type than <var>A</var>.
          * @param  workers       handlers of all worker threads other than the current threads.
+         *                       Content of this array may be modified by this method.
          * @param  collector     provides the finisher to use for computing final result
of type <var>R</var>.
          * @param  errorHandler  where to report exceptions, or {@code null} for throwing
them.
          * @return the final result computed by finisher (may be {@code null}).
@@ -604,8 +607,18 @@ public class TileOpExecutor {
          * @throws RuntimeException if an exception occurred elsewhere (for example in the
combiner or finisher).
          */
         final <R> R finish(final Future<?>[] workers, final Collector<?,A,R>
collector, final Consumer<LogRecord> errorHandler) {
+            /*
+             * Before to wait for other threads to complete their work, we need to remove
from executor queue all
+             * workers that did not yet started their run. Those threads may be waiting for
an executor thread to
+             * become available, which may never happen if all threads are waiting for a
non-running task.
+             */
+            for (int i=0; i<workers.length; i++) {
+                if (CommonExecutor.unschedule(workers[i])) {
+                    workers[i] = null;
+                }
+            }
             for (final Future<?> task : workers) try {
-                task.get();
+                if (task != null) task.get();
             } catch (ExecutionException ex) {
                 /*
                  * This is not an exception that occurred in Worker.executeOnCurrentTile(),
RenderedImage.getTile(…)
@@ -672,6 +685,26 @@ public class TileOpExecutor {
                 }
             }
         }
+
+        /**
+         * Returns a string representation of this cursor for debugging purposes.
+         */
+        @Override
+        public String toString() {
+            final int index = get();
+            String tile = "done";
+            if (index >= 0) {
+                final int tx = Math.addExact(minTileX, index % numXTiles);
+                final int ty = Math.addExact(minTileY, index / numXTiles);
+                if (ty <= maxTileY) {
+                    tile = "(" + tx + ", " + ty + ')';
+                }
+            }
+            return Strings.toString(getClass(),
+                    "image",      Classes.getShortClassName(image),
+                    "numWorkers", getNumWorkers(),
+                    "tile",       tile);
+        }
     }
 
 
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/system/CommonExecutor.java
b/core/sis-utility/src/main/java/org/apache/sis/internal/system/CommonExecutor.java
index 6f81bd0..7cfbd95 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/system/CommonExecutor.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/system/CommonExecutor.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.internal.system;
 
+import java.util.concurrent.Future;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadFactory;
@@ -76,6 +77,28 @@ public final class CommonExecutor extends AtomicInteger implements ThreadFactory
     }
 
     /**
+     * If the given task has been scheduled for execution but its execution did not yet started,
+     * removes it from the scheduled list. Otherwise does nothing. The given task should
be one
+     * of the following values:
+     *
+     * <ul>
+     *   <li>The {@link Runnable} value given to {@link ExecutorService#execute(Runnable)}.</li>
+     *   <li>The {@link Future} value returned by {@link ExecutorService#submit(Runnable)}.
+     *       In that case, the {@code Future} wrapper created by {@link ThreadPoolExecutor}
+     *       is actually an instance of {@link java.util.concurrent.RunnableFuture}.</li>
+     * </ul>
+     *
+     * @param  task  the task to remove from the list of tasks to execute.
+     * @return whether the given task has been removed.
+     */
+    public static boolean unschedule(final Object task) {
+        if (task instanceof Runnable) {
+            return ((ThreadPoolExecutor) INSTANCE).remove((Runnable) task);
+        }
+        return false;
+    }
+
+    /**
      * For the singleton {@link #INSTANCE}.
      */
     private CommonExecutor() {


Mime
View raw message