5

I have a litte race condition in my current android instrumentation test. What I want is:

  1. T1: Start Thread T2
  2. T2: Do something
  3. T1: Join with T2

With step 1 and 3 being Android live cycle events. But because in the instrumentation test everything happens very fast I get:

  1. T1: Start Thread T2
  2. T1: Join with T2 (which turn out to be a no-op)
  3. T2: Do something

Sure I could add a few sleeps to get the desired behaviour but I wonder if there is better way to do it. i.E. is there a way to make sure the thread which was just start ()-ed did actually start for good and is not still sitting in some scheduling queue awaiting start-up.

(Andy boy, do I miss Ada's rendezvous based multitasking)

And to answer mat's question:

  if (this.thread != null && this.thread.isAlive ())
  {
     this.stop.set (true);

     try
     {
        this.thread.join (1000);
     }
     catch (final InterruptedException Exception)
     {
        android.util.Log.w (Actor.TAG, "Thread did not want to join.", Exception);
     } // try
  } // if

As I said: no-op when because the thread has not started yet.

Martin
  • 11,577
  • 16
  • 80
  • 110
  • that doesn't make sens `Thread.join()` waits for the joined thread to die (unless you set a very short timeout). Are you sure the error is not somehwere else? – Mat Apr 24 '11 at 14:56
  • The issue isn't the speed of the unit tests - it's the way the Thread scheduling is playing out. – jefflunt Apr 24 '11 at 15:09
  • @Mat: I added a code sample to answer that question. – Martin Apr 24 '11 at 15:19
  • @Martin: why are you testing `thread != null` there? Are you sure you're entering that `if` statement? Does your second thread check whatever `stop.set(true)` sets before it starts processing? – Mat Apr 24 '11 at 15:27
  • @Martin - take a look at - [Awaitility](http://code.google.com/p/awaitility/) .. might be useful. – Premraj Apr 24 '11 at 16:42
  • Well, this is Android where everything is “Inversion of control” and step 1 and 3 are different live cycle events. In my instrumentation test I know that onStart is called before onDestroy and this.tread will not be null (also confirmed by the looking at the log file). However, in the final application it is possible that a Service is destroyed without ever being started. – Martin Apr 24 '11 at 17:37

2 Answers2

15

I typically use a CountDownLatch e.g. see this answer on testing asynchronous processes.

If you want to synchronise the starting of many threads you can also use a CyclicBarrier.

Community
  • 1
  • 1
Martin
  • 7,089
  • 3
  • 28
  • 43
  • Thanks! CountDownLatch and CyclicBarrier where exactly what I was looking for. CountDownLatch for the current problem. And CyclicBarrier I keep in mind for future use as it offers the base functionality of Ada's rendezvous which I always considered the best of all multitasking paradigm. – Martin Apr 24 '11 at 15:22
0

Martin, looking at your code I get the feeling that you may not be using the Thread class the way it was designed to be used. In particular, testing whether the other thread is alive seems like an anti-pattern. In most practical scenarios you can omit the this.thread.isAlive () condition from your code, and the program will still work.

It seems that you're trying to make two threads (that should do two different things) run the same piece of code, and you use logical conditions (such as this.thread != null) to decide which of the two threads is currently running.

Typically, you'd write two classes each one extending Thread and implementing a run() method. Each run() method realizes the logic of a single thread. Then you'd launch the 2nd thread from the first, and call join() on that 2nd thread to wait for it to complete.

public class SecondThread extends Thread {
  public void run() {
    ...
  }      
}

public class FirstThread extends Thread {
  public void run() {
    // Only FirstThread is running
    ...

    SecondThread st = new SecondThread();
    st.start();

    // Now both threads are running
    ...

    st.join(); // Wait for SecondThread to complete

    // Only FirstThread is running
    ...

  }                
}
Itay Maman
  • 30,277
  • 10
  • 88
  • 118
  • 1
    Note: From a OOP perspective it'd be better to implement Runnable and not extend Thread itself. – Voo Apr 24 '11 at 16:25
  • Sorry, I should have stressed the fact that I am speaking about an Android instrumentation tests which implies “Inversion of Control”. Step one is the onStart live cycle event and step 3 the onDestroy. The `this.thread != null` is there because there is a little in-balance (onCreate, onStart, onDestroy but no onStop) and onDestroy might be called without inStart. – Martin Apr 24 '11 at 17:49