How’s the Debugging Going?

I don’t really read any comics. I rarely did on paper as a kid, and I don’t really do it on the web either. The one exception is Piled Higher and Deeper, the grad student comic strip. Why I read it should be clear, and if not, the current comic should make it blatantly obvious:

How's the debugging going? by www.phdcomics.com

Why is this so appropriate, you ask? Oh, I found another bug in my algorithm.

Share
Posted in Uncategorized | Leave a comment

Print This Post Print This Post  

setOldThread and isOldThread

The instrumentation should actually be pretty easy. I just looked at the bytecode of this class:

public class Test {
public static boolean isOldThread() {
return true;
}
public static void setOldThread() { }

public static void main(String[] args) {
if (isOldThread()) {
// some code
}
else {
setOldThread();
}
}
}

and the main method contains these instructions:

invokestatic Test.isOldThread()Z
ifeq 5
// some code
goto 6
invokestatic Test.setOldThread()V
return

The invokestatic Test.isOldThread()Z puts the return value on the stack. The ifeq jumps over the then block if the return value was false; the goto in line 4 jumps over the else block if it was true and the then block has already been executed.

This just needs to be turned into

invokestatic java/lang/Thread.currentThread()Ljava/lang/Thread;
getfield java/lang/Thread.$$$oldThread$$$
ifeq 6
// some code
goto 9
iconst_1
invokestatic java/lang/Thread.currentThread()Ljava/lang/Thread;
putfield java/lang/Thread.$$$oldThread$$$
return

The old line 1 gets replaced by the new lines 1 and 2, which get the current thread, and then get its $$$oldThread$$$ field.

The old line 5 gets replaced by the new lines 6 to 8, which put true on the stack, get the current thread, and then set its $$$oldThread$$$ field.

monitorenter and monitorexit are even easier to handle.

// put object ref on stack
invokestatic edu/rice/cs/cunit/SyncPointBuffer/monitorEnter

just has to be changed into

// put object ref on stack
monitorenter

Exactly the same applies for monitorexit. The value on top of the stack isn’t used as first parameter anymore, now it’s directly consumed by the monitorenter or monitorexit instruction.

Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

At the End is at the Beginning

I’ve realized that the index in the schedule has to remain the same until a thread is done with a synchronization point (or rather the block following it). Only then, i. e. at the end of the block, may the index be incremented. This way, if a thread is currently executing its code and a second thread enters compactWait, it won’t procede but wait to be woken up. Theoretically, I could create a compactAdvance method that gets executed at the end of a block.

But the end of one block is the beginning of the next block, so both pieces of code can be put into compactWait. Except for the first block of a thread, though: The beginning of the first block is not the end of a previous block, so I need a conditional. The new, redesigned algorithm looks like this:

compactWait:

  1. Repeat this… (“RETRY” loop)
    1. monitorenter _waitAlgoObj (beginning of main synchronized block)
      1. If this is NOT the first sync point for this thread…
        1. Advance the indices.
      2. If the buffer has never been loaded, or if the index is at the end of the buffer…
        1. Load the buffer and reset the indices.
        2. Wake up all threads waiting for a buffer update.
      3. If the current sync point marks the end of the schedule…
        1. Disable scheduled replay.
        2. Wake up all threads waiting for a buffer update.
        3. monitorexit _waitAlgoObj
        4. Break out of the “RETRY” loop.
      4. If there’s a thread waiting for this wait array entry…
        1. Notify it
        2. Set a flag to scan ahead for the current sync point (“SCAN”).
        3. Do not advance indices.
      5. Otherwise…
        1. If the current sync point in the array is the right one…
          1. Do not advance indices.
          2. Allow the thread to exit the “RETRY” loop.
        2. Otherwise…
          1. Set a flag to scan ahead (“SCAN”).
      6. If the “SCAN” flag is set (i.e. either a thread was woken up or the current sync point did not match)…
        1. Look for the sync points in the remaining part of the array.
        2. If it could be found…
          1. Insert a new Object() into corresponding slot of the wait array.
          2. Set that Object() as “wait object” for this method.
          3. Allow the thread to exit the “RETRY” loop.
        3. Otherwise…
          1. Set the new buffer wait object as “wait object” for this method.
          2. Do not allow the thread to exit the “RETRY” loop.
        4. monitorenter waitObj (beginning of wait object synchronized block)
      7. monitorexit _waitAlgoObj (end of main synchronized block)
    2. If a “wait object” has been set for this method…
      1. Call wait() on that object (make sure to continue waiting if interrupted).
      2. monitorexit waitObj (end of wait object synchronized block)
      3. Do not advance indices.
  2. …while the thread is not allowed to exit the loop and scheduled replay is enabled (end “RETRY” loop).

compactThreadExit:

  1. monitorenter _waitAlgoObj (beginning of main synchronized block)
    1. If this is NOT the first sync point for this thread… (Note: Conditional probably not necessary)
      1. Advance the indices.
    2. If the index is at the end of the buffer…
      1. Load the buffer and reset the indices.
      2. Wake up all threads waiting for a buffer update.
    3. Otherwise…
      1. monitorenter _waitArray[index] (beginning of wait object synchronized block)
      2. If there’s a thread waiting for this wait array entry…
        1. Notify it.
      3. Otherwise, if the current sync point marks the end of the schedule…
        1. Disable scheduled replay.
        2. Wake up all threads waiting for a buffer update.
      4. Otherwise…
        1. Do nothing, because there’s still a thread awake that will reach this sync point.
      5. monitorexit _waitArray[index] (end of wait object synchronized block)
  2. monitorexit _waitAlgoObj (end of main synchronized block)

The first good news is that the modified algorithm passes the first simple test without violated assertions or invalid end states:

(Spin Version 4.2.3 -- 5 February 2005)
	+ Partial Order Reduction

Full statespace search for:
	never claim         	- (not selected)
	assertion violations	+
	cycle checks       	- (disabled by -DSAFETY)
	invalid end states	+

State-vector 196 byte, depth reached 140, errors: 0
    1486 states, stored
     725 states, matched
    2211 transitions (= stored+matched)
       0 atomic steps
hash conflicts: 0 (resolved)

Stats on memory usage (in Megabytes):
0.309 	equivalent memory usage for states (stored*(State-vector + overhead))
0.490 	actual memory usage for states (unsuccessful compression: 158.38%)
	State-vector as stored = 317 byte + 12 byte overhead
2.097 	memory used for hash table (-w19)
0.320 	memory used for DFS stack (-m10000)
0.159 	other (proc and chan stacks)
0.081 	memory lost to fragmentation
2.827 	total actual memory usage

I’m actually pretty sure compactThreadExit can always advance the indices without checking. There should never be a compactThreadExit without a previous compactWait. But I’ll have to check that.

The conditional for “If this is NOT the first sync point for this thread…” means that I have to add another variable to the java.lang.Thread class, and I’ll also have to get and set this variable in compactWait. That’s a little bit difficult since the variable doesn’t exist in the normal uninstrumented class.

So I think what I’ll do is add two empty dummy methods, public static void setOldThread() { } and public static boolean isOldThread() { }, to SyncPointBuffer and then call them from within compactWait. The calls, however, only serve as a markers for an instrumentation strategy which inserts the code to set the flag to true or get its value instead of the call.

I plan to use the same technique for embedding monitorenter and monitorexit instructions that don’t correspond to synchronized blocks.

Now I have to do laundry, though. I wish I could do that and coding concurrently…

Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

Assertions Violated

Indeed, there exist schedules that let the synchronization points occur in the incorrect order. I added two assertions and a counter to the framework that make sure that the program is actually following the schedule. Note that these assertions are outside any kind of lock, of course:

...
printf("[%d] replayWait ends ------------------------\n", tid);

/*-------------------------------------------------------------
* Assert that the events are happening in the correct order */
atomic{
printf("@@@@@ Asserting actual [%d %d] == expected [%d %d] @@@@@\n",
code, tid, schedule[outScheduleIndex], schedule[outScheduleIndex+1]);
assert(code == schedule[outScheduleIndex]);
assert(tid == schedule[outScheduleIndex+1]);
outScheduleIndex = outScheduleIndex + 2;
}

Now I let SPIN simulate all possible interleavings while checking for violated assertions, and at least one violating schedule was found:

pan: assertion violated (tid==schedule[(outScheduleIndex+1)]) (at depth 76)
pan: wrote pan_in.trail
pan: reducing search depth to 75
pan: wrote pan_in.trail
pan: reducing search depth to 73
pan: wrote pan_in.trail
pan: reducing search depth to 71
pan: wrote pan_in.trail
pan: reducing search depth to 69
pan: wrote pan_in.trail
pan: reducing search depth to 69
pan: wrote pan_in.trail
pan: reducing search depth to 65
pan: wrote pan_in.trail
pan: reducing search depth to 61
pan: wrote pan_in.trail
pan: reducing search depth to 60
pan: wrote pan_in.trail
pan: reducing search depth to 59
pan: wrote pan_in.trail
pan: reducing search depth to 58
pan: wrote pan_in.trail
pan: reducing search depth to 56
pan: wrote pan_in.trail
pan: reducing search depth to 54
pan: wrote pan_in.trail
pan: reducing search depth to 53
pan: wrote pan_in.trail
pan: reducing search depth to 52
(Spin Version 4.2.3 -- 5 February 2005)
	+ Partial Order Reduction

Full statespace search for:
	never claim         	- (not selected)
	assertion violations	+
	cycle checks       	- (disabled by -DSAFETY)
	invalid end states	+

State-vector 192 byte, depth reached 139, errors: 14
     953 states, stored
     615 states, matched
    1568 transitions (= stored+matched)
       0 atomic steps
hash conflicts: 0 (resolved)

Stats on memory usage (in Megabytes):
0.194 	equivalent memory usage for states (stored*(State-vector + overhead))
0.706 	actual memory usage for states (unsuccessful compression: 363.30%)
	State-vector as stored = 729 byte + 12 byte overhead
2.097 	memory used for hash table (-w19)
0.002 	memory used for DFS stack (-m52)
0.082 	memory lost to fragmentation
2.724 	total actual memory usage

Here’s a summary of the shortest trace that violates the assertions. It looks exactly as I suspected last night:

preparing trail, please wait...done
Starting :init: with pid 0
  1:	proc  0 (:init:) line  22 "pan_in" (state 1)
	[schedule[0] = 1]
...
 10:	proc  1 (thread) line  12 "waitalgo.pml" (state 1)
	[printf('[%d] replayWait(%d, %d)\\n',tid,code,tid)]
...
 28:	proc  1 (thread) line  95 "waitalgo.pml" (state 63)
	[printf('[%d] sync point match at index = %d\\n',tid,replayIndex)]
...
[1] replayWait ends ------------------------
 36:	proc  1 (thread) line 236 "waitalgo.pml" (state 159)
	[printf('[%d] replayWait ends ------------------------\\n',tid)]
	
 37:	proc  2 (thread) line  22 "waitalgo.pml" (state 2)
	[((algo_lock==0))]	
...
 45:	proc  2 (thread) line  95 "waitalgo.pml" (state 63)
	[printf('[%d] sync point match at index = %d\\n',tid,replayIndex)]
...
[2] replayWait ends ------------------------
 53:	proc  2 (thread) line 236 "waitalgo.pml" (state 159)
	[printf('[%d] replayWait ends ------------------------\\n',tid)]
	
...
 54:	proc  2 (thread) line 243 "waitalgo.pml" (state 161)
	[assert((code==schedule[outScheduleIndex]))]	
spin: line 244 "waitalgo.pml", Error: assertion violated
spin: text of failed assertion: assert((tid==schedule[(outScheduleIndex+1)]))
#processes: 4
 54:	proc  3 (thread) line  17 "waitalgo.pml" (state 156)
 54:	proc  2 (thread) line 244 "waitalgo.pml" (state 162)
 54:	proc  1 (thread) line 240 "waitalgo.pml" (state 164)
 54:	proc  0 (:init:) line  30 "pan_in" (state 12)
4 processes created

Assertions are a good thing, but it’s always sad when they are violated, along with your hopes.

Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

Perforce Triggers and Windows

I’ve spent a few hours trying to learn about Perforce triggers. These triggers allow the server to automatically run scripts when users perform certain actions, like submitting files. What I wanted to do is automatically upload the RiceMBS to its website and have the javadocs be rebuilt. This is exactly what these triggers are made for. I have the bash script written that deletes the old files, synchronizes with Perforce, and then generates new javadocs. However…

I just can’t get damn Windows to automatically log into my SSH account.

Really. Damn Windows. When I run the commands by hand from the command line, everything works beautifully, but when I let the service run it in the background, I don’t even get an error message.

I need to get away from Windows… Then again, I’m only using Perforce as a one-user repository so my data is backed up and can be rolled back.

Share
Posted in Uncategorized | Leave a comment

Print This Post Print This Post  

Another Mistake?

I haven’t actually verified it, but I have a gut feeling there’s another mistake in my algorithm. Let’s look at the part when a sync point actually matches right away. I’ve excluded all the parts that don’t get executed and shaded the conditionals that don’t get entered gray:

  1. Repeat this… (“RETRY” loop)
    1. monitorenter _waitAlgoObj (beginning of main synchronized block)
      1. If the buffer has never been loaded, or if the index is at the end of the buffer…
      2. If the current sync point marks the end of the schedule…
      3. If there’s a thread waiting for this wait array entry…
      4. Otherwise…
        1. If the current sync point in the array is the right one…
          1. Advance indices.
          2. Allow the thread to exit the “RETRY” loop.
        2. Otherwise…
      5. If the “SCAN” flag is set (i.e. either a thread was woken up or the current sync point did not match)…
      6. monitorexit _waitAlgoObj (end of main synchronized block)
    2. If a “wait object” has been set for this method…
  2. …while the thread is not allowed to exit the loop and scheduled replay is enabled (end “RETRY” loop).

Right after the match is found, the indices are advanced, the algorithm lock is released, and the method is exited. Right at this point, another thread could come along, find another match, race past the first thread and mess up the schedule.

How do I prevent this? Maybe advance the indices at the next wait point. How exactly? I don’t know yet.

Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

Giving It a Good SPIN

I discussed earlier how synchronized blocks are not going to do it anymore, because I have to interleave the regions in which the locks are held. That makes the code significantly more complex. Therefore, I decided to dig out SPIN and Promela again. Fortunately, I had taken a course on it, COMP 607: Automated Program Verification.

This project is a lot more complex than the toy examples I had to work with before. One thing that I found particularly annoying is that Promela doesn’t have a notion of procedures. The code in replayWait and replayThreadExit is going to be executed in many places, and to get it correct, I really want to avoid code duplication. And that’s what procedures should be there for… In Promela, I could have spawned a new process and waited for its completion, but I wanted to have only one Promela process per simulated thread.

Fortunately, SPIN runs the C preprocessor over the Promela source code, which is mainly used for #define directives; it also provides me with the #include directive, though. So I put the different functions into different include files and assume that parameters are passed in a number of local variables, namely tid and possibly code. That’s rather ugly (especially if you stick to the C mantra never to actually include code), but it does work.

Now I have 384 lines of Promela code that model the algorithm described earlier pretty much down to the “T”. I’ve written one test file so far, it just has three threads with one sync point each:

/* Concutest - Concurrent Unit Testing
*
* Test 1
*
* Written by Mathias Ricken, Rice University.
*/

#define SCHEDULE\_SIZE 8
#define REPLAY\_SIZE 4
#define REPLAY\_SIZE\_X2 8

#include "vars.pml"

proctype thread(int tid) {
local int code = 1;
#include "waitalgo.pml"

#include "exitalgo.pml"
}

init {
schedule[0] = 1; schedule[1] = 1;
schedule[2] = 1; schedule[3] = 2;
schedule[4] = 1; schedule[5] = 3;
schedule[6] = 3; schedule[7] = 0;

run thread(1);
run thread(2);
run thread(3);
}

Ugly, huh? As you can see, I use #include directives to include common global variables (vars.pml) and make “calls” to waitalgo.pml (compactWait) and exitalgo.pml (compactThreadExit). There’s another file, loadbuffer.pml, that factors out loading the next sync points out of the provided schedule, resetting the indices, and notifying threads waiting for a buffer update.

So far, it passes the SPIN “invalid end states” verification:

(Spin Version 4.2.3 -- 5 February 2005)

Full statespace search for:
	never claim         	- (not selected)
	assertion violations	- (disabled by -A flag)
	cycle checks       	- (disabled by -DSAFETY)
	invalid end states	+

State-vector 188 byte, depth reached 135, errors: 0
   11377 states, stored
   15019 states, matched
   26396 transitions (= stored+matched)
       0 atomic steps
hash conflicts: 90 (resolved)

Stats on memory usage (in Megabytes):
2.184 	equivalent memory usage for states (stored*(State-vector + overhead))
2.336 	actual memory usage for states (unsuccessful compression: 106.92%)
	State-vector as stored = 201 byte + 4 byte overhead
2.097 	memory used for hash table (-w19)
0.280 	memory used for DFS stack (-m10000)
0.113 	other (proc and chan stacks)
0.084 	memory lost to fragmentation
4.630 	total actual memory usage

This is only a very basic test case, but it passing SPIN’s verification means that at least with this schedule there is no interleaving of threads that can lead to an error. Now I’ll have to create more complicated schedules and threads and simulate them. Once I’ve verified that the algorithm as implemented in Promela is correct, I have to make a careful translation to Java.

Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

Kooprey

We got a request for Kooprey from a colleague today. Kooprey is our automatic generator for predictive recursive descent parsers formulated in our object-oriented way.

I have promised to take another look at it, maybe polish the code a little, and then make it available.

Share
Posted in Research | Leave a comment

Print This Post Print This Post  

Mock-Up

I’ve written a mock-up test of the algorithm with the interleaved synchronized blocks, using a short, inefficient Monitor class that immitates the behavior of Java monitors:

class Monitor {
private AtomicBoolean _b = new AtomicBoolean(false);
private Queue _w = new LinkedList();
public void enter() {
while(!_b.compareAndSet(false,true)) sleep(1);
}
public void exit() {
_b.set(false);
}
public void myWait() throws InterruptedException {
Object o = new Object();
_w.add(o);
exit();
boolean waiting = true;
do {
try {
synchronized(o) {
o.wait();
}
waiting = false;
}
catch(InterruptedException e) {
e.printStackTrace();
}
} while(waiting);
enter();
}
public void myNotify() {
Object o = _w.poll();
if (o!=null) {
synchronized(o) {
o.notify();
}
}
}
public void myNotifyAll() {
Object o;
while((o = _w.poll()) != null) {
synchronized(o) {
o.notify();
}
}
}
}

Using this class, I’ve tried to test the implementation, and it seems to work. Now I just have to find a way to easily write this with Java’s regular monitors and the monitorenter and monitorexit instructions. I don’t feel like writing the two methods entirely in Java bytecode, so I’ll probably write two empty methods, public static void monitorEnter(final Object o) and public static void monitorExit(final Object o), and then write an instrumentation strategy that inserts the bytecode aload\_0; monitorenter; return and aload\_0; monitorexit; return, respectively, as bodies.

When interleaving synchronized blocks this way, there is a greater risk of forgetting to release ownership of a monitor somewhere, producing a deadlock. I think it might be a good idea to also implement this algorithm in Promela and use SPIN to verify it.

I’ve found another mistake in the algorithm, by the way: In compactThreadExit, I also have to check if I’ve reached the end of the schedule, and if that is the case, turn off scheduled replay and notify all threads waiting for a new buffer. Here’s the new, corrected algorithm:

compactWait:

  1. Repeat this… (“RETRY” loop)
    1. monitorenter _waitAlgoObj (beginning of main synchronized block)
      1. If the buffer has never been loaded, or if the index is at the end of the buffer…
        1. Load the buffer and reset the indices.
        2. Wake up all threads waiting for a buffer update.
      2. If the current sync point marks the end of the schedule…
        1. Disable scheduled replay.
        2. Wake up all threads waiting for a buffer update.
        3. monitorexit _waitAlgoObj
        4. Break out of the “RETRY” loop.
      3. If there’s a thread waiting for this wait array entry…
        1. Notify it
        2. Set a flag to scan ahead for the current sync point (“SCAN”).
        3. Do not advance indices.
      4. Otherwise…
        1. If the current sync point in the array is the right one…
          1. Advance indices.
          2. Allow the thread to exit the “RETRY” loop.
        2. Otherwise…
          1. Set a flag to scan ahead (“SCAN”).
      5. If the “SCAN” flag is set (i.e. either a thread was woken up or the current sync point did not match)…
        1. Look for the sync points in the remaining part of the array.
        2. If it could be found…
          1. Insert a new Object() into corresponding slot of the wait array.
          2. Set that Object() as “wait object” for this method.
          3. Allow the thread to exit the “RETRY” loop.
        3. Otherwise…
          1. Set the new buffer wait object as “wait object” for this method.
          2. Do not allow the thread to exit the “RETRY” loop.
        4. monitorenter waitObj (beginning of wait object synchronized block)
      6. monitorexit _waitAlgoObj (end of main synchronized block)
    2. If a “wait object” has been set for this method…
      1. Call wait() on that object (make sure to continue waiting if interrupted).
      2. monitorexit waitObj (end of wait object synchronized block)
      3. If scheduled replay is still enabled and the thread is allowed to exit the “RETRY” loop (i.e. it was not a “new buffer” wait)…
        1. monitorenter _waitAlgoObj (beginning of main synchronized block)
          1. Advance the indices.
        2. monitorexit _waitAlgoObj (end of main synchronized block)
  2. …while the thread is not allowed to exit the loop and scheduled replay is enabled (end “RETRY” loop).

compactThreadExit:

  1. monitorenter _waitAlgoObj (beginning of main synchronized block)
    1. If the index is at the end of the buffer…
      1. Load the buffer and reset the indices.
      2. Wake up all threads waiting for a buffer update.
    2. Otherwise…
      1. monitorenter _waitArray[index] (beginning of wait object synchronized block)
      2. If there’s a thread waiting for this wait array entry…
        1. Notify it.
      3. Otherwise, if the current sync point marks the end of the schedule…
        1. Disable scheduled replay.
        2. Wake up all threads waiting for a buffer update.
      4. Otherwise…
        1. Do nothing, because there’s still a thread awake that will reach this sync point.
      5. monitorexit _waitArray[index] (end of wait object synchronized block)
  2. monitorexit _waitAlgoObj (end of main synchronized block)
Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

In Java Bytecode…

I think I found a way how to fulfill both requirements, but so far only in pure Java bytecode. In bytecode, acquiring and releasing monitors isn’t bound to any scoping and thus doesn’t have to happen in a nested, non-overlapping fashion.

If compactWait is written like this (Java/Java byte code blend):

public static void compactWait(long code, long tid) {
...
// beginning of main synchronized block
monitorenter _waitAlgoObj
...
// beginning of waitObj synchronized block
monitorenter waitObj
// end of main synchronized block
monitorexit _waitAlgoObj

// wait for notification
waitObj.wait()

// end of waitObj synchronized blocm
monitorexit waitObj
...
}

and compactThreadExit in a similar way:

public static void compactThreadExit(long tid) {
...
// beginning of main synchronized block
monitorenter _waitAlgoObj
...
// beginning of _waitArray[index] synchronized block
monitorenter _waitArray[index]
// end of main synchronized block
monitorexit _waitAlgoObj

// notify sleeping thread
_waitArray[index].notify()

// end of _waitArray[index] synchronized block
monitorexit _waitArray[index]
...
}

then the regions in which the two monitors are held overlap, and this prevents compactThreadExit from calling notify() before compactWait had a chance to call wait().

Let’s look at a more detailed trace from the last posting:

  1. Thread 1 does monitorenter _waitAlgoObj
  2. Thread 1 wakes thread 2 up.
  3. Thread 2 wakes up.
  4. Thread 2 wants the _waitAlgoObj monitor…
  5. Thread 1 scans for its sync point and inserts a new wait object into _waitArray[index].
  6. Thread 1 does monitorenter _waitArray[index]
  7. Thread 1 does monitorexit _waitAlgoObj
  8. Thread 2 does monitorenter _waitAlgoObj
  9. Thread 2 advances the index.
  10. Thread 2 does monitorexit _waitAlgoObj
  11. Thread 2 exits compactWait and executes its code.
  12. Thread 2 enters compactThreadExit.
  13. Thread 2 does monitorenter _waitAlgoObj
  14. Thread 2 wants the _waitArray[index] monitor…
  15. Thread 1 calls wait() on that wait object.
  16. Thread 2 does monitorenter _waitArray[index]
  17. Thread 2 checks if there is an object in the wait array and, because there is, calls notify() on the wait object, waking thread 1 up.
  18. Thread 1 wakes up.
  19. Thread 2 does monitorexit _waitArray[index]
  20. Thread 2 exits compactThreadExit.
  21. Thread 2 terminates.
  22. Thread 1 does monitorexit _waitArray[index]
  23. Thread 1 exits compactWait and executes its code.

Whenever a thread wants to call Object.wait() or Object.notify(), the thread first has to own that object’s monitor. By acquiring that monitor in compactWait before the monitor for the entire algorithm is released, we can guarantee that wait() is called before notify() can be called in compactThreadExit.

I’m just not sure what the easiest way to test this is…

Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

Right Direction, Not Right

I’m writing my little unit tests now, and in some cases, the fix with compactThreadExit actually helped. However, now there’s another problem:

Assume we have this schedule:

1 1
1 2
2 1

Let thread 2 go first. It’ll wait to be woken up in the second line. Then thread 1 executes the synchronization point in line 1 right away, then gets to its second synchronization point. Then the following things are supposed to happen:

  1. Thread 1 wakes thread 2 up.
  2. Thread 2 wakes up.
  3. Thread 1 scans for its sync point and inserts a new wait object into the wait array.
  4. Thread 1 calls wait() on that wait object.
  5. Thread 2 advances the index.
  6. Thread 2 exits compactWait and executes its code.
  7. Thread 2 enters compactThreadExit.
  8. Thread 2 checks if there is an object in the wait array and, because there is, calls notify() on the wait object, waking thread 1 up.
  9. Thread 1 wakes up.
  10. Thread 2 terminates.
  11. Thread 1 exits compactWait and executes its code.

Unfortunately, because thread 1 exits the synchronized block that prevents concurrent execution before it waits, thread 2 executes compactThreadExit before thread 1 is actually waiting:

  1. Thread 1 wakes thread 2 up.
  2. Thread 2 wakes up.
  3. Thread 1 scans for its sync point and inserts a new wait object into the wait array.
  4. Thread 2 advances the index.
  5. Thread 2 exits compactWait and executes its code.
  6. Thread 2 enters compactThreadExit.
  7. Thread 2 checks if there is an object in the wait array and, because there is, calls notify() on the wait object, but thread 1 wasn’t waiting yet!
  8. Thread 2 terminates.
  9. Thread 1 calls wait() on that wait object and never gets woken up!

The problem I’m facing now are these two contradictory requirements:

  1. I may not be in the synchronized block when thread 1 calls wait(), because then no other thread can enter the synchronized block (as previously noted).
  2. I may not exit the synchronized block that prevents concurrent execution until thread 1 has begun to wait, i.e. wait() has to be called inside the synchronized block.

It is obvious that a simple synchronized block cannot be used. I think a mutex might work, but I have to play around with the algorithm first. It’s amazing how complicated this is getting…

Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

compactWait and compactThreadExit

Ok, now I have a strategy that inserts calls to edu.rice.cs.cunit.SyncPointBuffer.compactThreadExit(long tid) at the end of java.lang.Thread.exit, and it seems like that’s actually the last thing a thread does. Good. (Note: I’ve just realized the thread ID isn’t actually necessary in compactThreadExit, so maybe I’ll take it out later. Right now, I’ll leave it in as a debugging help.)

Now the algorithm consists of two pieces. Here’s the part in compactWait:

  1. Repeat this… (“RETRY” loop)
    1. Begin of synchronized block
      1. If the buffer has never been loaded, or if the index is at the end of the buffer…
        1. Load the buffer and reset the indices.
        2. Wake up all threads waiting for a buffer update.
      2. If the current sync point marks the end of the schedule…
        1. Disable scheduled replay.
        2. Wake up all threads waiting for a buffer update.
        3. Break out of the “RETRY” loop.
      3. If there’s a thread waiting for this wait array entry…
        1. Notify it
        2. Set a flag to scan ahead for the current sync point (“SCAN”).
        3. Do not advance indices.
      4. Otherwise…
        1. If the current sync point in the array is the right one…
          1. Advance indices.
          2. Allow the thread to exit the “RETRY” loop.
        2. Otherwise…
          1. Set a flag to scan ahead (“SCAN”).
      5. If the “SCAN” flag is set (i.e. either a thread was woken up or the current sync point did not match)…
        1. Look for the sync points in the remaining part of the array.
        2. If it could be found…
          1. Insert a new Object() into corresponding slot of the wait array.
          2. Set that Object() as “wait object” for this method.
          3. Allow the thread to exit the “RETRY” loop.
        3. Otherwise…
          1. Set the new buffer wait object as “wait object” for this method.
          2. Do not allow the thread to exit the “RETRY” loop.
    2. End of synchronized block
    3. If a “wait object” has been set for this method…
      1. Call wait() on that object (make sure to continue waiting if interrupted).
      2. If scheduled replay is still enabled and the thread is allowed to exit the “RETRY” loop (i.e. it was not a “new buffer” wait)…
        1. Begin of synchronized block…
          1. Advance the indices.
        2. End of synchronized block.
  2. …while the thread is not allowed to exit the loop and scheduled replay is enabled (end “RETRY” loop).

This is basically the algorithm I had earlier, without any of the “fixes” to avoid deadlocks. These deadlocks can occur anywhere in the schedule if the order in which the threads reach their next synchronization points is unfortunate, i.e. current thread A is put to sleep in favor of waking up sleeping thread B and thread B then terminates without reaching another synchronization point.

Now here comes the second part of the algorithm, compactThreadExit:

  1. Begin of synchronized block…
    1. If there’s a thread waiting for this wait array entry…
      1. Notify it
    2. Otherwise…
      1. Do nothing, because there’s still a thread awake that will reach this sync point.
  2. End of synchronized block.

In the situation described above, thread B terminates, but the last thing it does is check if there’s a thread waiting for the next sync point already. If so, it wakes it up before the thread terminates. If not, then there still is a thread awake that is going to reach this sync point, so nothing has to be done (yes, most of the time, there’s only one thread awake at a time, but at the beginning of the program and after a buffer load multiple threads are looking for their sync points concurrently).

Let’s see how this works…

Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

When a Thread Terminates

I’m still dealing with this deadlock problem. In some cases, the following might happen: Thread 1 enters compactWait, but finds out thread 2 is waiting for this synchronization point. Thread 2 wakes thread 1 up and waits for the next synchronization point. Thread 1 terminates, and thread 2 never gets woken up.

What I should try is insert a call to compactWait (or some other method, maybe compactEndThread) at the very end of java.lang.Thread.exit. I’m hoping that this will actually be at the end of a thread’s life, and there won’t be any more sync points afterwards. Otherwise, this is not going to work.

This call will check if there is a thread waiting to be woken up at the current index, and if so, wake it up. Otherwise, it’s a no-op, because there should be a thread running already, about to enter compactWait and execute.

If this doesn’t work, e.g. because there are still sync points happening after java.lang.Thread.exit, then I could try some kind of “counting scheme”. I count the number of threads alive and how many of them are currently waiting. If the second number ever becomes equal to the first number, I’d have to wake a thread up. But that seems more complicated right now.

I don’t feel too well right now – felt awful yesterday, too – but I have a date. Time for dinner and a movie.

Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

Downgrade

Ok, I was fed up with the disgusting “rich” editor in WordPress 2.0 and spent the last 90 minutes downgrading to my trusted WordPress 1.5.2.

It was nearly impossible to paste code or any other preformatted text with the rich editor: It kept swallowing end-of-lines, and quite often the cursor would just get stuck somewhere and could only be moved with the mouse. HTML tags could only be inserted in the special “HTML” view. Sure, there were nice things in WordPress 2.0, like the fancy color fades, but what am I using the most? The editor. I need a good editor.

The process of downgrading was quite painful. At first I just reinstalled the 1.5.2 software, but it wouldn’t accept the 2.0 database. Apparently, the format has changed. I had made a backup before the upgrade, of course, and could restore it without a hitch.

The last thing to do was somehow transfer the posts I have made since the upgrade. To do that, I let both software versions run concurrently, in two separate MySQL databases. I then created new postings in the 1.5.2 version, copy & pasted and hand-formatted the postings, and fixed the time stamps.

WordPress 2.0 was a very, very bad experience. It may be shiny and nice for people who just write English. But for me, the editor was just horrible. I feel like the WordPress developers deserve an angry email, but they have already wasted enough of my time.

Share
Posted in Uncategorized | Leave a comment

Print This Post Print This Post  

The End is Important

When I implemented last night’s sketch, I realized that the LASTBUFFER flag actually doesn’t quite work. The problem is that, when a thread has been woken up, I don’t have to check if the currently pointed to sync point is SP\_END – that is the thread that was just put to sleep. I have to look at the sync point beyond that, so a look-ahead of just 1 is not enough. Assume for a second that a look-ahead of 2 is enough. Then there are the following cases possible:

  1. index-1: thread just woken up, currently running
    index  : thread just put to sleep
    index+1: SP_END
  2. index-1: thread just woken up, currently running
    index  : thread just put to sleep
    -------- end of buffer --------
    0      : SP_END
  3. index-1: thread just woken up, currently running
    -------- end of buffer --------
    0      : thread just put to sleep
    1      : SP_END

Case 3 actually doesn’t quite happen this way. The thread entering compactWait notices that a thread has been waiting for this sync point already, so it wakes it up and scans for its own sync point. It doesn’t find its sync point, so it waits for the next buffer load.

Case 1 is the easiest to handle: I can just check if the end of schedule marker is at index+1. Cases 2 and 3, however, would have to involve the LASTBUFFER flag. Again, assuming a look-ahead of 2 is enough, LASTBUFFER would be true if the next buffer is either a) completely empty, b) contains just SP\_END, or c) contains one sync point and SP\_END.

This would be easy enough to handle. Unfortunately, I don’t think it works. Assume a schedule that ends like this:

...
x  : SP_MONITOREXIT 1 (1 waits)
x+1: SP_MONITOREXIT 2
x+2: SP_MONITOREXIT 3 (3 waits)
x+3: SP_MONITOREXIT 4 (4 waits)
x+4: SP_END 0

Let us furthermore assume that threads 1, 3, and 4 have entered compactWait but weren’t supposed to run, so they are waiting for the sync points at x, x+2, and x+3. Let index = x right now.

Now thread 2 enters compactWait and notices that thread 1 has been waiting for this sync point at index. So it wakes thread 1 up and puts itself to sleep. Thread 1 increments index, so index = x+1 now. Then thread 1 exits compactWait and terminates – it was the last sync point for thread 1.

The result is that threads 2, 3, and 4 are all waiting to be woken up, but all threads are asleep, even though x+1 wasn’t the sync point immediately before SP\_END. This can, of course, be extended to any number of sync points.

I think I’m on the wrong path here.

For a while, I also thought that I could just disable scheduled replay as soon as I see a thread ID 9 (“DestroyJavaVM”). I haven’t tried yet, but I’m pretty sure that thread won’t be launched until all other threads have finished. If there’s still one thread asleep, waiting for a sync point, I won’t ever get a thread ID 9. This would be the easiest solution, but I’m rather sure it won’t work.

I think I’m beginning to understand that this whole issue isn’t about the end of the schedule, it’s about the end of a thread! With our compactWait algorithm, we always have only one thread running (unless a new buffer has been loaded). If that single thread ends, compactWait won’t be entered again, and the thread waiting for the next sync point will never be woken up. The program is deadlocked.

Something needs to be done when a thread ends. I don’t quite know what that is or how it can be done, though.

Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

Even Trickier

I’ve just realized there’s a potential problem with this algorithm too: Assume the second to last sync point of the schedule is being processed, and a thread is waiting for it. This thread will be woken up, and the current thread will wait for the last sync point to be hit. However, since it’s the last sync point in the schedule, compactWait will never be entered again. The last thread will never complete!

This isn’t really a problem in this application, though, since the there are these eight sync points produced by “DestroyJavaVM” (thread 9) that don’t show up in the recorded trace. Thread 9 will enter compactWait and wake up the last thread. But now thread 9 will look for its sync point, and it’s not in the schedule. So it will wait for a buffer update… which will never come.

Therefore, when a thread is woken up so it can continue, it needs to check if the next entry in the sync point array marks the end of the schedule (SP_END). If so, replay needs to be disabled and all threads waiting for a buffer update need to be woken up.

Another problem arises, unfortunately: If it’s the end of the buffer, compactWait cannot determine if the end of the schedule has been reached without reloading the buffer. But reloading the buffer needs to happen in a synchronized block.

It’s probably the easiest to have another static flag, LASTBUFFER, that the master VM sets if this is the last block. compactWait will disable scheduled replay if the index is at the end of the buffer and the LASTBUFFER flag is set, or if the next sync point marks the end. Note that this “or” has to short-circuit.

So, without further ado, another sketch of the algorithm:

  1. Repeat this… (“RETRY” loop)
    1. Begin of synchronized block
      1. If the buffer has never been loaded, or if the index is at the end of the buffer…
        1. If the LASTBUFFER flag is set…
          1. Disable scheduled replay.
          2. Wake up all threads waiting for a buffer update.
          3. Break out of the “RETRY” loop.
        2. Otherwise…
          1. Load the buffer and reset the indices.
          2. Wake up all threads waiting for a buffer update.
      2. If the current sync point marks the end of the schedule…
        1. Disable scheduled replay.
        2. Wake up all threads waiting for a buffer update.
        3. Break out of the “RETRY” loop.
      3. If there’s a thread waiting for this wait array entry…
        1. Notify it
        2. Set a flag to scan ahead for the current sync point (“SCAN”).
        3. Do not advance indices.
      4. Otherwise…
        1. If the current sync point in the array is the right one…
          1. Advance indices.
          2. Allow the thread to exit the “RETRY” loop.
        2. Otherwise…
          1. Set a flag to scan ahead (“SCAN”).
      5. If the “SCAN” flag is set (i.e. either a thread was woken up or the current sync point did not match)…
        1. Look for the sync points in the remaining part of the array.
        2. If it could be found…
          1. Insert a new Object() into corresponding slot of the wait array.
          2. Set that Object() as “wait object” for this method.
          3. Allow the thread to exit the “RETRY” loop.
        3. Otherwise…
          1. Set the new buffer wait object as “wait object” for this method.
          2. Do not allow the thread to exit the “RETRY” loop.
    2. End of synchronized block
    3. If a “wait object” has been set for this method…
      1. Call wait() on that object (make sure to continue waiting if interrupted).
      2. If scheduled replay is still enabled and the thread is allowed to exit the “RETRY” loop (i.e. it was not a “new buffer” wait)…
        1. Advance the indices.
        2. If the index is at the end of the buffer and the LASTBUFFER flag is set, or the sync point at the new index marks the end of the schedule (note: this must short-circuit)…
          1. Disable scheduled replay.
          2. Wake up all threads waiting for a buffer update.
          3. Break out of the “RETRY” loop.
  2. …while the thread is not allowed to exit the loop and scheduled replay is enabled (end “RETRY” loop).
Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

Definitely Tricky

I just noticed that my algorithm still isn’t correct. Right now, when compactWait finds a thread that is waiting, it wakes it up and continues with the current thread if the next sync point matches. Then we have two threads running concurrently. Instead, when a waiting thread is woken up, the current thread must wait.

It seems like this can be accomplished the easiest by not advancing the indices if a thread was woken up, and then entering the “scan ahead” portion. The indices are advanced only after a thread waiting on a sync point has been woken up again.

This has the effect that when a thread was woken up, the scan for the current thread starts with the next sync point, and the current thread will wait for there. The newly awoken thread then advances the the indices, and the current thread will be woken up the next time a sync point is encountered.

This seems to be the algorithm now:

  1. Repeat this… (“RETRY” loop)
    1. Begin of synchronized block
      1. If the buffer has never been loaded, or if the index is at the end of the buffer, reload buffer and notify all threads waiting for a buffer update.
      2. If there’s a thread waiting for this wait array entry…
        1. Notify it
        2. Set a flag to scan ahead for the current sync point (“SCAN”).
        3. Do not advance indices.
      3. Otherwise…
        1. If the current sync point in the array is the right one…
          1. Advance indices.
          2. Allow the thread to exit the “RETRY” loop.
        2. Otherwise…
          1. Set a flag to scan ahead (“SCAN”).
      4. If the “SCAN” flag is set (i.e. either a thread was woken up or the current sync point did not match)…
        1. Look for the sync points in the remaining part of the array.
        2. If it could be found…
          1. Insert a new Object() into the wait array.
          2. Set that Object() as “wait object” for this method.
          3. Allow the thread to exit the “RETRY” loop.
        3. Otherwise…
          1. Set the new buffer wait object as “wait object” for this method.
          2. Do not allow the thread to exit the “RETRY” loop.
    2. End of synchronized block
    3. If a “wait object” has been set for this method…
      1. Call wait() on that object (make sure to continue waiting if interrupted).
      2. If the thread is allowed to exit the “RETRY” loop (i.e. it was not a “new buffer” wait)…
        1. Advance the indices.
  2. …until the thread is allowed to exit the loop (end “RETRY” loop).
Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

Tricky Bit of Code

The wait algorithm is turning out to be trickier than expected. I think I have answers for the two questions I posed a few days ago. Let’s go in reverse order, actually:

  1. When one thread has to wait inside compactWait, which is a synchronized method, can other threads even enter it?

I’m almost embarrassed I had to ask this. Of course, other threads cannot enter compactWait at the same time. The current thread relinquishes control of the object it is waiting on (either the one in the wait array or the one for a new buffer), but it doesn’t reliquish control of all monitors it has. This, of course, means that the entire method cannot be synchronized. Certain portions, however, do have to be synchronized, as explained below.

  1. When threads are notified that a new buffer has been loaded, they might all work with the list of sync points simultaneously. This might create race conditions. Are these important?

It’s true that the sync points are unique: There will (or at least should) never be two threads looking for the same sync point. However, all threads are manipulating the indices into the sync point and wait arrays concurrently, so I think this actually is an issue.

When threads resume after waiting for a new buffer, they should not concurrently run through the method; otherwise, they might, for example, dispatch a thread that’s waiting twice, or attempt to do so but cause a NullPointerException because the entry in the wait array was non-null, but another thread made it null before the first thread could call notify(). Another really important thing is that buffer reloads are synchronized; otherwise, two threads could concurrently try to reload the buffer, and nasty things would happen.

This pretty much leads to the follwing structure:

  1. Repeat this… (“RETRY” loop)
    1. Begin of synchronized block
      1. If the buffer has never been loaded, or if the index is at the end of the buffer, reload buffer and notify all threads waiting for a buffer update.
      2. If there’s a thread waiting for this wait array entry, notify it and continue with the next sync point.
      3. If the current sync point in the array is the right one…
        1. Advance indices.
        2. Allow the thread to exit the “RETRY” loop.
      4. Otherwise…
        1. Look for the sync points in the remaining part of the array.
        2. If it could be found…
          1. Insert a new Object() into the wait array.
          2. Set that Object() as “wait object” for this method.
          3. Allow the thread to exit the “RETRY” loop.
        3. Otherwise…
          1. Set the new buffer wait object as “wait object” for this method.
          2. Do not allow the thread to exit the “RETRY” loop.
    2. End of synchronized block
    3. If a “wait object” has been set for this method, call wait() on that object (make sure to continue waiting if interrupted).
  2. …until the thread is allowed to exit the loop (end “RETRY” loop).

The most important change here is that the entire method is not synchronized anymore, but the entire block that does notification and scanning is. The waiting happens outside the synchronized block. This allows other threads to run through the block when threads are waiting.

I’m writing a piece of code that simulates the behavior outside of the actual program right now. I should probably write unit test, hm?

Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post  

Happy New Year

Happy New Year! I’m in the process of upgrading to WordPress 2.0. So far, I regret the decision. The List Manager doesn’t work anymore, so I’ve had to replace all lists with inlined versions, and the LiveCalendar plugin doesn’t work anymore either.

Share
Posted in Uncategorized | Leave a comment

Print This Post Print This Post  

Don’t Know, But Managed Anyway

I’ve thought about where these sync points come from over the last few days. I still can’t exactly figure it out.

However, I decided to just enable recording at the beginning of the main method, just like I do with replay. Now the sync points line up. There are two sync points that don’t belong to the ones I’m interested in:

1 0 // 000030b6 00000002 sun.misc.Launcher$AppClassLoader.loadClass(Ljava/lang/String;Z)Ljava/lang/Class;
2 0 // 000030b6 00000002 sun.misc.Launcher$AppClassLoader.loadClass(Ljava/lang/String;Z)Ljava/lang/Class;

But these occur both during recording and replaying, so that’s not a problem. I still can’t understand why the sync points didn’t match earlier, because I happened to record them but I tagged them as “comments”. I switched from comment to real sync point exactly at the same time I enable recording now. Fortunately, it seems like I don’t have to dig into this mystery right now…

I modified the logs that the CompactDebugProcessor generates to make it compatible with the other logs. As you can see above, all the annotations about class and method index and the decyphered method signature are now behind // as comments. Therefore, the replay application can parse it without modification.

I’ve begun to design the replay scheduler now. It’s turning out to be more complicated than expected. Here’s an outlide:

  1. If the buffer has never been loaded, or if the index is at the end of the buffer…
    1. Load the buffer.
    2. Reset the index.
    3. Notify all threads waiting on the buffer reload Object().
  2. If there’s a thread waiting for this sync point…
    1. Notify the thread so it can continue to run.
    2. Move on to the next sync point.
  3. Repeat this… (RETRY loop)
    1. If the current sync point is the one we look for…
      1. Move on to the next sync point.
      2. Allow the thread to exit the “RETRY” loop.
    2. Otherwise…
      1. Loop over all sync points, starting with the one after the current sync point… (BUFFER loop)
        1. If this is the sync point we are looking for…
          1. Insert a new Object() into the wait array.
          2. Call wait() on that object (make sure to continue waiting if interrupted).
          3. …other threads will continue…
          4. When notified, break out of the “BUFFER” loop and allow the thread to exit the “RETRY” loop.
        2. Otherwise, if this sync point has the same thread ID as the sync point we’re looking for, but a different event code…
          1. Issue a warning: Somehow a thread has missed a sync point.
          2. Undefined behavior after this; maybe disable scheduled replay.
      2. End “BUFFER” loop.
      3. If the “BUFFER” loop went through the entire rest of the buffer without finding the sync point…
        1. Call wait() on the buffer reload Object() (make sure to continue waiting if interrupted).
        2. …other threads will continue…
        3. When notified, do not allow thread to exit “RETRY” loop.
  4. …until the thread is allowed to exit the loop (end “RETRY” loop).

The questions I have on my mind right now are these:

  1. When threads are notified that a new buffer has been loaded, they might all work with the list of sync points simultaneously. This might create race conditions. Are these important? My gut feeling tells me no, because each thread is only looking for one sync points, so the code-threadID pairs may be treated as unique.
  2. When one thread has to wait inside compactWait, which is a synchronized method, can other threads even enter it?

I think I will create a small-scale model outside the entire system to investigate these issues. Now it’s dinner time.

Share
Posted in Concurrent Unit Testing | Leave a comment

Print This Post Print This Post