OSDN Git Service

Make DalvikRunner more resilient to running immediately after device reboot
authorBrian Carlstrom <bdc@google.com>
Thu, 11 Feb 2010 07:07:32 +0000 (23:07 -0800)
committerBrian Carlstrom <bdc@google.com>
Thu, 11 Feb 2010 19:15:32 +0000 (11:15 -0800)
    Changed EnvironmentDevice.prepare to waitForDevice and
    waitForNonEmptyDirectory("/sdcard") before proceeding to fix problem with
    running immediately after "fastboot flashall"

dalvik/libcore/tools/runner/java/dalvik/runner/EnvironmentDevice.java

    Added Adb.waitForDevice and Adb.waitForNonEmptyDirectory

dalvik/libcore/tools/runner/java/dalvik/runner/Adb.java

    Added Command.executeWithTimeout based on code refactored from Mode.java

dalvik/libcore/tools/runner/java/dalvik/runner/Command.java
dalvik/libcore/tools/runner/java/dalvik/runner/Mode.java

libcore/tools/runner/java/dalvik/runner/Adb.java
libcore/tools/runner/java/dalvik/runner/Command.java
libcore/tools/runner/java/dalvik/runner/EnvironmentDevice.java
libcore/tools/runner/java/dalvik/runner/Mode.java

index 7c1ed64..075ca5f 100644 (file)
@@ -17,6 +17,8 @@
 package dalvik.runner;
 
 import java.io.File;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
 
 /**
  * An adb command.
@@ -50,4 +52,39 @@ final class Adb {
         new Command("adb", "forward", "tcp:" + localPort, "tcp:" + devicePort)
                 .execute();
     }
+
+    public void waitForDevice() {
+        new Command("adb", "wait-for-device").execute();
+    }
+
+    /**
+     * Loop until we see a non-empty directory on the device. For
+     * example, wait until /sdcard is mounted.
+     */
+    public void waitForNonEmptyDirectory(File path, int timeoutSeconds) {
+        final int millisPerSecond = 1000;
+        final long start = System.currentTimeMillis();
+        final long deadline = start + (millisPerSecond * timeoutSeconds);
+
+        while (true) {
+            final long remainingSeconds = ((deadline - System.currentTimeMillis())
+                                           / millisPerSecond);
+            Command command = new Command("adb", "shell", "ls", path.getPath());
+            List<String> output;
+            try {
+                output = command.executeWithTimeout(remainingSeconds);
+            } catch (TimeoutException e) {
+                throw new RuntimeException("Timed out after " + timeoutSeconds +
+                                           " seconds waiting for file " + path, e);
+            }
+            try {
+                Thread.sleep(millisPerSecond);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+            if (!output.isEmpty()) {
+                return;
+            }
+        }
+    }
 }
index 9af02a2..4319cf9 100644 (file)
@@ -25,6 +25,13 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.logging.Logger;
 
 /**
@@ -124,6 +131,38 @@ final class Command {
         }
     }
 
+    /**
+     * Executes a command with a specified timeout. Output is returned
+     * if the command succeeds. If Otherwise null is returned if the
+     * command timed out.
+     */
+    public List<String> executeWithTimeout(long timeoutSeconds)
+            throws TimeoutException {
+        ExecutorService outputReader
+            = Executors.newFixedThreadPool(1, Threads.daemonThreadFactory());
+        try {
+            start();
+            // run on a different thread to allow a timeout
+            Future<List<String>> future = outputReader.submit(new Callable<List<String>>() {
+                    public List<String> call() throws Exception {
+                        return gatherOutput();
+                    }
+                });
+            return future.get(timeoutSeconds, TimeUnit.SECONDS);
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to execute process: " + args, e);
+        } catch (InterruptedException e) {
+            throw new RuntimeException("Interrupted while executing process: " + args, e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (isStarted()) {
+                getProcess().destroy(); // to release the output reader
+            }
+            outputReader.shutdown();
+        }
+    }
+
     static class Builder {
         private final List<String> args = new ArrayList<String>();
         private File workingDirectory;
index 90b2889..c44152d 100644 (file)
@@ -34,6 +34,8 @@ class EnvironmentDevice extends Environment {
     }
 
     @Override void prepare() {
+        adb.waitForDevice();
+        adb.waitForNonEmptyDirectory(runnerDir.getParentFile(), 5 * 60);
         if (cleanBefore) {
             adb.rm(runnerDir);
         }
index 0f0163f..c30e023 100644 (file)
@@ -25,10 +25,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Properties;
 import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.logging.Logger;
 import java.util.regex.Pattern;
@@ -73,9 +69,6 @@ abstract class Mode {
             new File("out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar").getAbsoluteFile());
 
 
-    protected final ExecutorService outputReaders
-            = Executors.newFixedThreadPool(1, Threads.daemonThreadFactory());
-
     Mode(Environment environment, long timeoutSeconds, File sdkJar) {
         this.environment = environment;
         this.timeoutSeconds = timeoutSeconds;
@@ -206,14 +199,7 @@ abstract class Mode {
         List<String> output = null;
         for (final Command command : commands) {
             try {
-                command.start();
-
-                // run on a different thread to allow a timeout
-                output = outputReaders.submit(new Callable<List<String>>() {
-                        public List<String> call() throws Exception {
-                            return command.gatherOutput();
-                        }
-                    }).get(timeoutSeconds, TimeUnit.SECONDS);
+                output = command.executeWithTimeout(timeoutSeconds);
             } catch (TimeoutException e) {
                 testRun.setResult(Result.EXEC_TIMEOUT,
                         Collections.singletonList("Exceeded timeout! (" + timeoutSeconds + "s)"));
@@ -221,10 +207,6 @@ abstract class Mode {
             } catch (Exception e) {
                 testRun.setResult(Result.ERROR, e);
                 return;
-            } finally {
-                if (command.isStarted()) {
-                    command.getProcess().destroy(); // to release the output reader
-                }
             }
         }
         // we only look at the output of the last command
@@ -257,7 +239,6 @@ abstract class Mode {
      * Cleans up after all test runs have completed.
      */
     void shutdown() {
-        outputReaders.shutdown();
         environment.shutdown();
     }
 }