Improve documentation for the concurrent interpreter

Test: comment-only change
Change-Id: I506872f7ff83b8c99527ff593eecc8fdc6760b53
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/ConcurrentInterpreter.kt b/staticlibs/testutils/devicetests/com/android/testutils/ConcurrentInterpreter.kt
index 40575d3..cbdc017 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/ConcurrentInterpreter.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/ConcurrentInterpreter.kt
@@ -16,14 +16,18 @@
 val INTERPRET_TIME_UNIT = 40L // ms
 
 /**
- * A small interpreter for testing parallel code. The interpreter will read a list of lines
- * consisting of "|"-separated statements. Each column runs in a different concurrent thread
- * and all threads wait for each other in between lines. Each statement is split on ";" then
- * matched with regular expressions in the instructionTable constant, which contains the
- * code associated with each statement. The interpreter supports an object being passed to
- * the interpretTestSpec() method to be passed in each lambda (think about the object under
- * test), and an optional transform function to be executed on the object at the start of
- * every thread.
+ * A small interpreter for testing parallel code.
+ *
+ * The interpreter will read a list of lines consisting of "|"-separated statements, e.g. :
+ *   sleep 2 ; unblock thread2 | wait thread2 time 2..5
+ *   sendMessage "x"           | obtainMessage = "x" time 0..1
+ *
+ * Each column runs in a different concurrent thread and all threads wait for each other in
+ * between lines. Each statement is split on ";" then matched with regular expressions in the
+ * instructionTable constant, which contains the code associated with each statement. The
+ * interpreter supports an object being passed to the interpretTestSpec() method to be passed
+ * in each lambda (think about the object under test), and an optional transform function to be
+ * executed on the object at the start of every thread.
  *
  * The time unit is defined in milliseconds by the interpretTimeUnit member, which has a default
  * value but can be passed to the constructor. Whitespace is ignored.
@@ -33,13 +37,8 @@
  * the regexp match, and the object. See the individual tests for the DSL of that test.
  * Implementors for new interpreting languages are encouraged to look at the defaultInterpretTable
  * constant below for an example of how to write an interpreting table.
- * Some expressions already exist by default and can be used by all interpreters. They include :
- * sleep(x) : sleeps for x time units and returns Unit ; sleep alone means sleep(1)
- * EXPR = VALUE : asserts that EXPR equals VALUE. EXPR is interpreted. VALUE can either be the
- *   string "null" or an int. Returns Unit.
- * EXPR time x..y : measures the time taken by EXPR and asserts it took at least x and at most
- *   y time units.
- * EXPR // any text : comments are ignored.
+ * Some expressions already exist by default and can be used by all interpreters. Refer to
+ * getDefaultInstructions() below for a list and documentation.
  */
 open class ConcurrentInterpreter<T>(
     localInterpretTable: List<InterpretMatcher<T>>,
@@ -65,11 +64,28 @@
         return code(this, r, match)
     }
 
-    // Spins as many threads as needed by the test spec and interpret each program concurrently,
-    // having all threads waiting on a CyclicBarrier after each line.
-    // |lineShift| says how many lines after the call the spec starts. This is used for error
-    // reporting. Unfortunately AFAICT there is no way to get the line of an argument rather
-    // than the line at which the expression starts.
+    /**
+     * Spins as many threads as needed by the test spec and interpret each program concurrently.
+     *
+     * All threads wait on a CyclicBarrier after each line.
+     * |lineShift| says how many lines after the call the spec starts. This is used for error
+     * reporting. Unfortunately AFAICT there is no way to get the line of an argument rather
+     * than the line at which the expression starts.
+     *
+     * This method is mostly meant for implementations that extend the ConcurrentInterpreter
+     * class to add their own directives and instructions. These may need to operate on some
+     * data, which can be passed in |initial|. For example, an interpreter specialized in callbacks
+     * may want to pass the callback there. In some cases, it's necessary that each thread
+     * performs a transformation *after* it starts on that value before starting ; in this case,
+     * the transformation can be passed to |threadTransform|. The default is to return |initial| as
+     * is. Look at some existing child classes of this interpreter for some examples of how this
+     * can be used.
+     *
+     * @param spec The test spec, as a string of lines separated by pipes.
+     * @param initial An initial value passed to all threads.
+     * @param lineShift How many lines after the call the spec starts, for error reporting.
+     * @param threadTransform an optional transformation that each thread will apply to |initial|
+     */
     fun interpretTestSpec(
         spec: String,
         initial: T,
@@ -79,11 +95,19 @@
         // For nice stack traces
         val callSite = getCallingMethod()
         val lines = spec.trim().trim('\n').split("\n").map { it.split("|") }
-        // |threads| contains arrays of strings that make up the statements of a thread : in other
+        // |lines| contains arrays of strings that make up the statements of a thread : in other
         // words, it's an array that contains a list of statements for each column in the spec.
+        // E.g. if the string is """
+        //   a | b | c
+        //   d | e | f
+        // """, then lines is [ [ "a", "b", "c" ], [ "d", "e", "f" ] ].
         val threadCount = lines[0].size
         assertTrue(lines.all { it.size == threadCount })
         val threadInstructions = (0 until threadCount).map { i -> lines.map { it[i].trim() } }
+        // |threadInstructions| is a list where each element is the list of instructions for the
+        // thread at the index. In other words, it's just |lines| transposed. In the example
+        // above, it would be [ [ "a", "d" ], [ "b", "e" ], [ "c", "f" ] ]
+        // mapIndexed below will pass in |instructions| the list of instructions for this thread.
         val barrier = CyclicBarrier(threadCount)
         var crash: InterpretException? = null
         threadInstructions.mapIndexed { threadIndex, instructions ->
@@ -142,6 +166,16 @@
     }
 }
 
+/**
+ * Default instructions available to all interpreters.
+ * sleep(x) : sleeps for x time units and returns Unit ; sleep alone means sleep(1)
+ * EXPR = VALUE : asserts that EXPR equals VALUE. EXPR is interpreted. VALUE can either be the
+ *   string "null" or an int. Returns Unit.
+ * EXPR time x..y : measures the time taken by EXPR and asserts it took at least x and at most
+ *   y time units.
+ * EXPR // any text : comments are ignored.
+ * EXPR fails : checks that EXPR throws some exception.
+ */
 private fun <T> getDefaultInstructions() = listOf<InterpretMatcher<T>>(
     // Interpret an empty line as doing nothing.
     Regex("") to { _, _, _ -> null },