[concurrency-interest] Adding toString methods for task objects?

Martin Buchholz martinrb at google.com
Tue Aug 8 21:36:36 EDT 2017


My colleague Charles Munger suggests adding toString methods to various
task classes in j.u.c.  Obviously this makes it easier to debug large
programs that have gone wrong.  But it obviously also is a behavior change
that has the potential for trouble, even when everyone is behaving nicely,
since toString may have the effect of generating an unbounded output.  I
almost started down this road myself a few years ago but chickened out.
What do you think?

Index: Executors.java
===================================================================
RCS file:
/export/home/jsr166/jsr166/jsr166/src/main/java/util/concurrent/Executors.java,v
retrieving revision 1.97
diff -u -U 10 -r1.97 Executors.java
--- Executors.java 15 Apr 2017 00:12:38 -0000 1.97
+++ Executors.java 9 Aug 2017 01:34:25 -0000
@@ -478,20 +478,24 @@
         private final Runnable task;
         private final T result;
         RunnableAdapter(Runnable task, T result) {
             this.task = task;
             this.result = result;
         }
         public T call() {
             task.run();
             return result;
         }
+        public String toString() {
+            return super.toString()
+                + "[task=[" + task + "], result=[" + result + "]]";
+        }
     }

     /**
      * A callable that runs under established access control settings.
      */
     private static final class PrivilegedCallable<T> implements
Callable<T> {
         final Callable<T> task;
         final AccessControlContext acc;

         PrivilegedCallable(Callable<T> task) {
@@ -504,20 +508,24 @@
                 return AccessController.doPrivileged(
                     new PrivilegedExceptionAction<T>() {
                         public T run() throws Exception {
                             return task.call();
                         }
                     }, acc);
             } catch (PrivilegedActionException e) {
                 throw e.getException();
             }
         }
+
+        public String toString() {
+            return super.toString() + "[task=[" + task + "]]";
+        }
     }

     /**
      * A callable that runs under established access control settings and
      * current ClassLoader.
      */
     private static final class PrivilegedCallableUsingCurrentClassLoader<T>
             implements Callable<T> {
         final Callable<T> task;
         final AccessControlContext acc;
@@ -556,20 +564,24 @@
                                 } finally {
                                     t.setContextClassLoader(cl);
                                 }
                             }
                         }
                     }, acc);
             } catch (PrivilegedActionException e) {
                 throw e.getException();
             }
         }
+
+        public String toString() {
+            return super.toString() + "[task=[" + task + "]]";
+        }
     }

     /**
      * The default thread factory.
      */
     private static class DefaultThreadFactory implements ThreadFactory {
         private static final AtomicInteger poolNumber = new
AtomicInteger(1);
         private final ThreadGroup group;
         private final AtomicInteger threadNumber = new AtomicInteger(1);
         private final String namePrefix;
Index: ForkJoinTask.java
===================================================================
RCS file:
/export/home/jsr166/jsr166/jsr166/src/main/java/util/concurrent/ForkJoinTask.java,v
retrieving revision 1.115
diff -u -U 10 -r1.115 ForkJoinTask.java
--- ForkJoinTask.java 19 Apr 2017 23:45:51 -0000 1.115
+++ ForkJoinTask.java 9 Aug 2017 01:34:25 -0000
@@ -1339,37 +1339,43 @@
         T result;
         AdaptedRunnable(Runnable runnable, T result) {
             if (runnable == null) throw new NullPointerException();
             this.runnable = runnable;
             this.result = result; // OK to set this even before completion
         }
         public final T getRawResult() { return result; }
         public final void setRawResult(T v) { result = v; }
         public final boolean exec() { runnable.run(); return true; }
         public final void run() { invoke(); }
+        public String toString() {
+            return super.toString() + "[task=[" + runnable + "], result=["
+ result + "]]";
+        }
         private static final long serialVersionUID = 5232453952276885070L;
     }

     /**
      * Adapter for Runnables without results.
      */
     static final class AdaptedRunnableAction extends ForkJoinTask<Void>
         implements RunnableFuture<Void> {
         final Runnable runnable;
         AdaptedRunnableAction(Runnable runnable) {
             if (runnable == null) throw new NullPointerException();
             this.runnable = runnable;
         }
         public final Void getRawResult() { return null; }
         public final void setRawResult(Void v) { }
         public final boolean exec() { runnable.run(); return true; }
         public final void run() { invoke(); }
+        public String toString() {
+            return super.toString() + "[task=[" + runnable + "]]";
+        }
         private static final long serialVersionUID = 5232453952276885070L;
     }

     /**
      * Adapter for Runnables in which failure forces worker exception.
      */
     static final class RunnableExecuteAction extends ForkJoinTask<Void> {
         final Runnable runnable;
         RunnableExecuteAction(Runnable runnable) {
             if (runnable == null) throw new NullPointerException();
@@ -1402,20 +1408,23 @@
                 result = callable.call();
                 return true;
             } catch (RuntimeException rex) {
                 throw rex;
             } catch (Exception ex) {
                 throw new RuntimeException(ex);
             }
         }
         public final void run() { invoke(); }
         private static final long serialVersionUID = 2838392045355241008L;
+        public String toString() {
+            return super.toString() + "[task=[" + callable + "]]";
+        }
     }

     /**
      * Returns a new {@code ForkJoinTask} that performs the {@code run}
      * method of the given {@code Runnable} as its action, and returns
      * a null result upon {@link #join}.
      *
      * @param runnable the runnable action
      * @return the task
      */
Index: FutureTask.java
===================================================================
RCS file:
/export/home/jsr166/jsr166/jsr166/src/main/java/util/concurrent/FutureTask.java,v
retrieving revision 1.118
diff -u -U 10 -r1.118 FutureTask.java
--- FutureTask.java 10 Sep 2016 04:06:51 -0000 1.118
+++ FutureTask.java 9 Aug 2017 01:34:25 -0000
@@ -444,20 +444,49 @@
                             continue retry;
                     }
                     else if (!WAITERS.compareAndSet(this, q, s))
                         continue retry;
                 }
                 break;
             }
         }
     }

+    public String toString() {
+        String start = super.toString() + "[status=";
+        try {
+            switch (state) {
+            case NEW:
+            case COMPLETING:
+                final Callable<?> callable = this.callable;
+                if (callable != null) {
+                    return start + "PENDING, task=[" + callable + "]]";
+                } else {
+                    return start + "PENDING]";
+                }
+            case NORMAL:
+                return start + "SUCCESS, result=[" + outcome + "]]";
+            case EXCEPTIONAL:
+                return start + "FAILURE, cause=[" + outcome + "]]";
+            case CANCELLED:
+            case INTERRUPTING:
+            case INTERRUPTED:
+                return start + "CANCELLED]";
+            default:
+                throw new IllegalStateException();
+            }
+        } catch (RuntimeException thrownFromToString) {
+            return start + "UNKNOWN, cause=["
+                + thrownFromToString.getClass() + "]";
+        }
+    }
+
     // VarHandle mechanics
     private static final VarHandle STATE;
     private static final VarHandle RUNNER;
     private static final VarHandle WAITERS;
     static {
         try {
             MethodHandles.Lookup l = MethodHandles.lookup();
             STATE = l.findVarHandle(FutureTask.class, "state", int.class);
             RUNNER = l.findVarHandle(FutureTask.class, "runner",
Thread.class);
             WAITERS = l.findVarHandle(FutureTask.class, "waiters",
WaitNode.class);
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20170808/1a354d89/attachment-0001.html>


More information about the Concurrency-interest mailing list