Index: nchmark/mutexStmt/JavaThread.java
===================================================================
--- benchmark/mutexStmt/JavaThread.java	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ 	(revision )
@@ -1,64 +1,0 @@
-public class JavaThread {
-	// Simplistic low-quality Marsaglia Shift-XOR pseudo-random number generator.
-	// Bijective
-	// Cycle length for non-zero values is 4G-1.
-	// 0 is absorbing and should be avoided -- fixed point.
-	// The returned value is typically masked to produce a positive value.
-	static volatile int Ticket = 0 ;
-
-	private static int nextRandom (int x) {
-		if (x == 0) {
-			// reseed the PRNG
-			// Ticket is accessed infrequently and does not constitute a coherence hot-spot.
-			// Note that we use a non-atomic racy increment -- the race is rare and benign.
-			// If the race is a concern switch to an AtomicInteger.
-			// In addition accesses to the RW volatile global "Ticket"  variable are not
-			// (readily) predictable at compile-time so the JIT will not be able to elide
-			// nextRandom() invocations.
-			x = ++Ticket ;
-			if (x == 0) x = 1 ;
-		}
-		x ^= x << 6;
-		x ^= x >>> 21;
-		x ^= x << 7;
-		return x ;
-	}
-	static int x = 2;
-
-	static private long times = Long.parseLong("100000000");
-
-	public static void helper() throws InterruptedException {
-		JavaThread j = new JavaThread();
-		// Inhibit biased locking ...
-		x = (j.hashCode() ^ System.identityHashCode(j)) | 1 ;
-		for(long i = 1; i <= times; i += 1) {
-			x = nextRandom(x);
-			synchronized( j ) {
-                x = nextRandom( x );
-            }
-		}
-	}
-
-	public static void InnerMain() throws InterruptedException {
-		long start = System.nanoTime();
-		helper();
-		long end = System.nanoTime();
-		System.out.println( (end - start) / times );
-	}
-    
-	public static void main(String[] args) throws InterruptedException {
-		if ( args.length > 1 ) System.exit( 1 );
-		if ( args.length == 1 ) { times = Long.parseLong(args[0]); }
-
-		//for (int n = Integer.parseInt("5"); --n >= 0 ; ) {
-			InnerMain();
-			Thread.sleep(2000);     // 2 seconds
-			x = nextRandom(x);
-		//}
-		if ( x == 0 ) System.out.println(x);
-	}
-}
-
-// Local Variables: //
-// tab-width: 4 //
-// End: //
Index: doc/papers/AMA/AMA-stix/Documents/.log
===================================================================
--- doc/papers/AMA/AMA-stix/Documents/.log	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ doc/papers/AMA/AMA-stix/Documents/.log	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
Index: doc/papers/AMA/AMA-stix/Documents/README.txt
===================================================================
--- doc/papers/AMA/AMA-stix/Documents/README.txt	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ doc/papers/AMA/AMA-stix/Documents/README.txt	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
Index: doc/papers/AMA/AMA-stix/Documents/wileyNJD-Doc.tex
===================================================================
--- doc/papers/AMA/AMA-stix/Documents/wileyNJD-Doc.tex	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ doc/papers/AMA/AMA-stix/Documents/wileyNJD-Doc.tex	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
Index: doc/papers/AMA/AMA-stix/Stix-fonts/ly1sti.fd
===================================================================
--- doc/papers/AMA/AMA-stix/Stix-fonts/ly1sti.fd	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ doc/papers/AMA/AMA-stix/Stix-fonts/ly1sti.fd	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
Index: doc/papers/AMA/AMA-stix/ama/NJDnatbib.sty
===================================================================
--- doc/papers/AMA/AMA-stix/ama/NJDnatbib.sty	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ doc/papers/AMA/AMA-stix/ama/NJDnatbib.sty	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
Index: doc/papers/AMA/AMA-stix/ama/wileyNJD-AMA.bbl
===================================================================
--- doc/papers/AMA/AMA-stix/ama/wileyNJD-AMA.bbl	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ doc/papers/AMA/AMA-stix/ama/wileyNJD-AMA.bbl	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
Index: doc/papers/AMA/AMA-stix/ama/wileyNJD-AMA.bib
===================================================================
--- doc/papers/AMA/AMA-stix/ama/wileyNJD-AMA.bib	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ doc/papers/AMA/AMA-stix/ama/wileyNJD-AMA.bib	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
Index: doc/theses/colby_parsons_MMAth/benchmarks/actors/data/nasus_CFA.txt
===================================================================
--- doc/theses/colby_parsons_MMAth/benchmarks/actors/data/nasus_CFA.txt	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ doc/theses/colby_parsons_MMAth/benchmarks/actors/data/nasus_CFA.txt	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -1,7 +1,7 @@
 5
 1 2 4 8 16 24 32 48
-CFA-LV CFA-NS CFA-R 
+Longest-Victim No-Stealing Random 
 executor
-CFA-LV:
+Longest-Victim:
 proc	time (s)
 1	29.22
@@ -45,5 +45,5 @@
 48	1.20
 48	1.20
-CFA-NS:
+No-Stealing:
 proc	time (s)
 1	28.25
@@ -87,5 +87,5 @@
 48	1.18
 48	1.16
-CFA-R:
+Random:
 proc	time (s)
 1	28.58
@@ -131,5 +131,5 @@
 
 matrix
-CFA-LV:
+Longest-Victim:
 proc	time (s)
 1	105.48
@@ -173,5 +173,5 @@
 48	2.75
 48	2.96
-CFA-NS:
+No-Stealing:
 proc	time (s)
 1	106.01
@@ -215,5 +215,5 @@
 48	2.78
 48	2.92
-CFA-R:
+Random:
 proc	time (s)
 1	105.91
@@ -259,5 +259,5 @@
 
 repeat
-CFA-LV:
+Longest-Victim:
 proc	time (s)
 1	1.17
@@ -301,5 +301,5 @@
 48	13.73
 48	14.55
-CFA-NS:
+No-Stealing:
 proc	time (s)
 1	1.15
@@ -343,5 +343,5 @@
 48	13.03
 48	12.83
-CFA-R:
+Random:
 proc	time (s)
 1	1.15
@@ -387,5 +387,5 @@
 
 balance_one
-CFA-LV:
+Longest-Victim:
 proc	time (s)
 1	20.06
@@ -429,5 +429,5 @@
 48	1.11
 48	1.12
-CFA-NS:
+No-Stealing:
 proc	time (s)
 1	20.13
@@ -471,5 +471,5 @@
 48	19.95
 48	20.00
-CFA-R:
+Random:
 proc	time (s)
 1	19.92
@@ -515,5 +515,5 @@
 
 balance_multi
-CFA-LV:
+Longest-Victim:
 proc	time (s)
 1	8.17
@@ -557,5 +557,5 @@
 48	5.75
 48	5.68
-CFA-NS:
+No-Stealing:
 proc	time (s)
 1	8.10
@@ -599,5 +599,5 @@
 48	9.28
 48	9.26
-CFA-R:
+Random:
 proc	time (s)
 1	8.08
Index: doc/theses/colby_parsons_MMAth/benchmarks/actors/data/pyke_CFA.txt
===================================================================
--- doc/theses/colby_parsons_MMAth/benchmarks/actors/data/pyke_CFA.txt	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ doc/theses/colby_parsons_MMAth/benchmarks/actors/data/pyke_CFA.txt	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -1,7 +1,7 @@
 5
 1 2 4 8 16 24 32 48
-CFA-LV CFA-NS CFA-R 
+Longest-Victim No-Stealing Random 
 executor
-CFA-LV:
+Longest-Victim:
 proc	time (s)
 1	29.04
@@ -45,5 +45,5 @@
 48	2.58
 48	2.55
-CFA-NS:
+No-Stealing:
 proc	time (s)
 1	28.15
@@ -87,5 +87,5 @@
 48	2.59
 48	2.60
-CFA-R:
+Random:
 proc	time (s)
 1	29.06
@@ -131,5 +131,5 @@
 
 matrix
-CFA-LV:
+Longest-Victim:
 proc	time (s)
 1	127.44
@@ -173,5 +173,5 @@
 48	6.83
 48	6.81
-CFA-NS:
+No-Stealing:
 proc	time (s)
 1	127.64
@@ -215,5 +215,5 @@
 48	6.77
 48	6.74
-CFA-R:
+Random:
 proc	time (s)
 1	127.26
@@ -259,5 +259,5 @@
 
 repeat
-CFA-LV:
+Longest-Victim:
 proc	time (s)
 1	1.16
@@ -301,5 +301,5 @@
 48	19.75
 48	19.71
-CFA-NS:
+No-Stealing:
 proc	time (s)
 1	1.18
@@ -343,5 +343,5 @@
 48	13.88
 48	13.71
-CFA-R:
+Random:
 proc	time (s)
 1	1.18
@@ -387,5 +387,5 @@
 
 balance_one
-CFA-LV:
+Longest-Victim:
 proc	time (s)
 1	19.46
@@ -429,5 +429,5 @@
 48	2.12
 48	2.17
-CFA-NS:
+No-Stealing:
 proc	time (s)
 1	21.00
@@ -471,5 +471,5 @@
 48	47.50
 48	47.72
-CFA-R:
+Random:
 proc	time (s)
 1	20.81
@@ -515,5 +515,5 @@
 
 balance_multi
-CFA-LV:
+Longest-Victim:
 proc	time (s)
 1	7.94
@@ -557,5 +557,5 @@
 48	14.38
 48	14.50
-CFA-NS:
+No-Stealing:
 proc	time (s)
 1	8.48
@@ -599,5 +599,5 @@
 48	21.50
 48	21.15
-CFA-R:
+Random:
 proc	time (s)
 1	8.49
Index: doc/theses/colby_parsons_MMAth/benchmarks/channels/plotData.py
===================================================================
--- doc/theses/colby_parsons_MMAth/benchmarks/channels/plotData.py	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ doc/theses/colby_parsons_MMAth/benchmarks/channels/plotData.py	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -70,5 +70,5 @@
     if currBench == Bench.Unset:
         if line == "contend:":
-            name = "Contend"
+            name = "Channel Contention"
             currBench = Bench.Contend
         elif line == "zero:":
Index: doc/theses/colby_parsons_MMAth/code/swap_queues.cfa
===================================================================
--- doc/theses/colby_parsons_MMAth/code/swap_queues.cfa	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ doc/theses/colby_parsons_MMAth/code/swap_queues.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -1,27 +1,13 @@
-// this is a code stub and will not compile
-
-// tries to atomically swap two queues and returns 0p if the swap failed
-// returns ptr to newly owned queue if swap succeeds
-static inline work_queue * try_swap_queues( worker & this, unsigned int victim_idx, unsigned int my_idx ) with(this) {
+// sequential equivalent swap
+void swap( uint victim_idx, uint my_idx  ) {
+    // Step 0:
     work_queue * my_queue = request_queues[my_idx];
-    work_queue * other_queue = request_queues[victim_idx];
-
-    // if either queue is 0p then they are in the process of being stolen
-    if ( other_queue == 0p || my_queue == 0p ) return 0p;
-
-    // try to set our queue ptr to be 0p. If it fails someone moved our queue so return false
-    if ( !__atomic_compare_exchange_n( &request_queues[my_idx], &my_queue, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) )
-        return 0p;
-
-    // try to set other queue ptr to be our queue ptr. If it fails someone moved the other queue so fix up then return false
-    if ( !__atomic_compare_exchange_n( &request_queues[victim_idx], &other_queue, my_queue, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) ) {
-        /* paranoid */ verify( request_queues[my_idx] == 0p );
-        request_queues[my_idx] = my_queue; // reset my queue ptr back to appropriate val
-        return 0p;
-    }
-
-    // we have successfully swapped and since our queue is 0p no one will touch it so write back new queue ptr non atomically
-    request_queues[my_idx] = other_queue; // last write does not need to be atomic
-    return other_queue;
+    work_queue * vic_queue = request_queues[victim_idx];
+    // Step 2:
+    request_queues[my_idx] = 0p;
+    // Step 3:
+    request_queues[victim_idx] = my_queue;
+    // Step 4:
+    request_queues[my_idx] = vic_queue;
 }
 
@@ -35,18 +21,23 @@
 
 bool try_swap_queues( worker & this, uint victim_idx, uint my_idx ) with(this) {
+    // Step 0:
+    // request_queues is the shared array of all sharded queues
     work_queue * my_queue = request_queues[my_idx];
     work_queue * vic_queue = request_queues[victim_idx];
 
+    // Step 1:
     // If either queue is 0p then they are in the process of being stolen
     // 0p is CForAll's equivalent of C++'s nullptr
-    if ( vic_queue == 0p || my_queue == 0p ) return false;
+    if ( vic_queue == 0p ) return false;
 
-    // Try to set our queue ptr to be 0p.
-    // If this CAS fails someone moved our queue so return false
+    // Step 2:
+    // Try to set thief's queue ptr to be 0p.
+    // If this CAS fails someone stole thief's queue so return false
     if ( !CAS( &request_queues[my_idx], &my_queue, 0p ) )
         return false;
-
-    // Try to set other queue ptr to be our queue ptr.
-    // If it fails someone moved the other queue, so fix up then return false
+    
+    // Step 3:
+    // Try to set victim queue ptr to be thief's queue ptr.
+    // If it fails someone stole the other queue, so fix up then return false
     if ( !CAS( &request_queues[victim_idx], &vic_queue, my_queue ) ) {
         request_queues[my_idx] = my_queue; // reset queue ptr back to prev val
@@ -54,6 +45,8 @@
     }
 
+    // Step 4:
     // Successfully swapped.
-    // Our queue is 0p so no one will touch it so write back without CAS is safe
+    // Thief's ptr is 0p so no one will touch it
+    // Write back without CAS is safe
     request_queues[my_idx] = vic_queue;
     return true;
Index: doc/theses/rob_schluntz_MMath/thesis.bib
===================================================================
--- doc/theses/rob_schluntz_MMath/thesis.bib	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ doc/theses/rob_schluntz_MMath/thesis.bib	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
Index: libcfa/src/bits/weakso_locks.cfa
===================================================================
--- libcfa/src/bits/weakso_locks.cfa	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ libcfa/src/bits/weakso_locks.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -15,7 +15,5 @@
 // Update Count     :
 //
-
 #include "bits/weakso_locks.hfa"
-
 #pragma GCC visibility push(default)
 
@@ -30,2 +28,6 @@
 void on_wakeup( blocking_lock &, size_t ) {}
 size_t wait_count( blocking_lock & ) { return 0; }
+bool register_select( blocking_lock & this, select_node & node ) { return false; }
+bool unregister_select( blocking_lock & this, select_node & node ) { return false; }
+bool on_selected( blocking_lock & this, select_node & node ) { return true; }
+
Index: libcfa/src/bits/weakso_locks.hfa
===================================================================
--- libcfa/src/bits/weakso_locks.hfa	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ libcfa/src/bits/weakso_locks.hfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -23,5 +23,5 @@
 #include "containers/list.hfa"
 
-struct thread$;
+struct select_node;
 
 //-----------------------------------------------------------------------------
@@ -32,5 +32,5 @@
 
 	// List of blocked threads
-	dlist( thread$ ) blocked_threads;
+	dlist( select_node ) blocked_threads;
 
 	// Count of current blocked threads
@@ -60,4 +60,7 @@
 void on_wakeup( blocking_lock & this, size_t ) OPTIONAL_THREAD;
 size_t wait_count( blocking_lock & this ) OPTIONAL_THREAD;
+bool register_select( blocking_lock & this, select_node & node ) OPTIONAL_THREAD;
+bool unregister_select( blocking_lock & this, select_node & node ) OPTIONAL_THREAD;
+bool on_selected( blocking_lock & this, select_node & node ) OPTIONAL_THREAD;
 
 //----------
@@ -75,2 +78,5 @@
 static inline void   on_wakeup( multiple_acquisition_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); }
 static inline void   on_notify( multiple_acquisition_lock & this, struct thread$ * t ){ on_notify( (blocking_lock &)this, t ); }
+static inline bool   register_select( multiple_acquisition_lock & this, select_node & node ) { return register_select( (blocking_lock &)this, node ); }
+static inline bool   unregister_select( multiple_acquisition_lock & this, select_node & node ) { return unregister_select( (blocking_lock &)this, node ); }
+static inline bool   on_selected( multiple_acquisition_lock & this, select_node & node ) { return on_selected( (blocking_lock &)this, node ); }
Index: libcfa/src/concurrency/channel.hfa
===================================================================
--- libcfa/src/concurrency/channel.hfa	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ libcfa/src/concurrency/channel.hfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -4,37 +4,21 @@
 #include <list.hfa>
 #include <mutex_stmt.hfa>
-
-// link field used for threads waiting on channel
-struct wait_link {
-    // used to put wait_link on a dl queue
-    inline dlink(wait_link);
-
-    // waiting thread
-    struct thread$ * t;
-
-    // shadow field
-    void * elem;
-};
-P9_EMBEDDED( wait_link, dlink(wait_link) )
-
-static inline void ?{}( wait_link & this, thread$ * t, void * elem ) {
-    this.t = t;
-    this.elem = elem;
-}
-
-// wake one thread from the list
-static inline void wake_one( dlist( wait_link ) & queue ) {
-    wait_link & popped = try_pop_front( queue );
-    unpark( popped.t );
-}
+#include "select.hfa"
 
 // returns true if woken due to shutdown
 // blocks thread on list and releases passed lock
-static inline bool block( dlist( wait_link ) & queue, void * elem_ptr, go_mutex & lock ) {
-    wait_link w{ active_thread(), elem_ptr };
-    insert_last( queue, w );
+static inline bool block( dlist( select_node ) & queue, void * elem_ptr, go_mutex & lock ) {
+    select_node sn{ active_thread(), elem_ptr };
+    insert_last( queue, sn );
     unlock( lock );
     park();
-    return w.elem == 0p;
+    return sn.extra == 0p;
+}
+
+// Waituntil support (un)register_select helper routine
+// Sets select node avail if not special OR case and then unlocks
+static inline void __set_avail_then_unlock( select_node & node, go_mutex & mutex_lock ) {
+    if ( node.park_counter ) __make_select_node_available( node );
+    unlock( mutex_lock );
 }
 
@@ -59,5 +43,5 @@
     size_t size, front, back, count;
     T * buffer;
-    dlist( wait_link ) prods, cons; // lists of blocked threads
+    dlist( select_node ) prods, cons; // lists of blocked threads
     go_mutex mutex_lock;            // MX lock
     bool closed;                    // indicates channel close/open
@@ -70,5 +54,5 @@
     size = _size;
     front = back = count = 0;
-    buffer = aalloc( size );
+    if ( size != 0 ) buffer = aalloc( size );
     prods{};
     cons{};
@@ -87,5 +71,5 @@
     #endif
     verifyf( cons`isEmpty && prods`isEmpty, "Attempted to delete channel with waiting threads (Deadlock).\n" );
-    delete( buffer );
+    if ( size != 0 ) delete( buffer );
 }
 static inline size_t get_count( channel(T) & chan ) with(chan) { return count; }
@@ -102,9 +86,13 @@
     // flush waiting consumers and producers
     while ( has_waiting_consumers( chan ) ) {
-        cons`first.elem = 0p;
+        if( !__handle_waituntil_OR( cons ) ) // ensure we only signal special OR case threads when they win the race
+            break;  // if __handle_waituntil_OR returns false cons is empty so break
+        cons`first.extra = 0p;
         wake_one( cons );
     }
     while ( has_waiting_producers( chan ) ) {
-        prods`first.elem = 0p;
+        if( !__handle_waituntil_OR( prods ) ) // ensure we only signal special OR case threads when they win the race
+            break;  // if __handle_waituntil_OR returns false prods is empty so break
+        prods`first.extra = 0p;
         wake_one( prods );
     }
@@ -114,9 +102,20 @@
 static inline void is_closed( channel(T) & chan ) with(chan) { return closed; }
 
+// used to hand an element to a blocked consumer and signal it
+static inline void __cons_handoff( channel(T) & chan, T & elem ) with(chan) {
+    memcpy( cons`first.extra, (void *)&elem, sizeof(T) ); // do waiting consumer work
+    wake_one( cons );
+}
+
+// used to hand an element to a blocked producer and signal it
+static inline void __prods_handoff( channel(T) & chan, T & retval ) with(chan) {
+    memcpy( (void *)&retval, prods`first.extra, sizeof(T) );
+    wake_one( prods );
+}
+
 static inline void flush( channel(T) & chan, T elem ) with(chan) {
     lock( mutex_lock );
     while ( count == 0 && !cons`isEmpty ) {
-        memcpy(cons`first.elem, (void *)&elem, sizeof(T)); // do waiting consumer work
-        wake_one( cons );
+        __cons_handoff( chan, elem );
     }
     unlock( mutex_lock );
@@ -125,5 +124,5 @@
 // handles buffer insert
 static inline void __buf_insert( channel(T) & chan, T & elem ) with(chan) {
-    memcpy((void *)&buffer[back], (void *)&elem, sizeof(T));
+    memcpy( (void *)&buffer[back], (void *)&elem, sizeof(T) );
     count += 1;
     back++;
@@ -131,12 +130,4 @@
 }
 
-// does the buffer insert or hands elem directly to consumer if one is waiting
-static inline void __do_insert( channel(T) & chan, T & elem ) with(chan) {
-    if ( count == 0 && !cons`isEmpty ) {
-        memcpy(cons`first.elem, (void *)&elem, sizeof(T)); // do waiting consumer work
-        wake_one( cons );
-    } else __buf_insert( chan, elem );
-}
-
 // needed to avoid an extra copy in closed case
 static inline bool __internal_try_insert( channel(T) & chan, T & elem ) with(chan) {
@@ -145,6 +136,15 @@
     operations++;
     #endif
+
+    ConsEmpty: if ( !cons`isEmpty ) {
+        if ( !__handle_waituntil_OR( cons ) ) break ConsEmpty;
+        __cons_handoff( chan, elem );
+        unlock( mutex_lock );
+        return true;
+    }
+
     if ( count == size ) { unlock( mutex_lock ); return false; }
-    __do_insert( chan, elem );
+
+    __buf_insert( chan, elem );
     unlock( mutex_lock );
     return true;
@@ -157,5 +157,5 @@
 // handles closed case of insert routine
 static inline void __closed_insert( channel(T) & chan, T & elem ) with(chan) {
-    channel_closed except{&channel_closed_vt, &elem, &chan };
+    channel_closed except{ &channel_closed_vt, &elem, &chan };
     throwResume except; // throw closed resumption
     if ( !__internal_try_insert( chan, elem ) ) throw except; // if try to insert fails (would block), throw termination
@@ -182,10 +182,10 @@
     }
 
-    // have to check for the zero size channel case
-    if ( size == 0 && !cons`isEmpty ) {
-        memcpy(cons`first.elem, (void *)&elem, sizeof(T));
-        wake_one( cons );
-        unlock( mutex_lock );
-        return true;
+    // buffer count must be zero if cons are blocked (also handles zero-size case)
+    ConsEmpty: if ( !cons`isEmpty ) {
+        if ( !__handle_waituntil_OR( cons ) ) break ConsEmpty;
+        __cons_handoff( chan, elem );
+        unlock( mutex_lock );
+        return;
     }
 
@@ -202,25 +202,16 @@
     } // if
 
-    if ( count == 0 && !cons`isEmpty ) {
-        memcpy(cons`first.elem, (void *)&elem, sizeof(T)); // do waiting consumer work
-        wake_one( cons );
-    } else __buf_insert( chan, elem );
-    
-    unlock( mutex_lock );
-    return;
-}
-
-// handles buffer remove
-static inline void __buf_remove( channel(T) & chan, T & retval ) with(chan) {
-    memcpy((void *)&retval, (void *)&buffer[front], sizeof(T));
+    __buf_insert( chan, elem );
+    unlock( mutex_lock );
+}
+
+// does the buffer remove and potentially does waiting producer work
+static inline void __do_remove( channel(T) & chan, T & retval ) with(chan) {
+    memcpy( (void *)&retval, (void *)&buffer[front], sizeof(T) );
     count -= 1;
     front = (front + 1) % size;
-}
-
-// does the buffer remove and potentially does waiting producer work
-static inline void __do_remove( channel(T) & chan, T & retval ) with(chan) {
-    __buf_remove( chan, retval );
     if (count == size - 1 && !prods`isEmpty ) {
-        __buf_insert( chan, *(T *)prods`first.elem );  // do waiting producer work
+        if ( !__handle_waituntil_OR( prods ) ) return;
+        __buf_insert( chan, *(T *)prods`first.extra );  // do waiting producer work
         wake_one( prods );
     }
@@ -233,5 +224,14 @@
     operations++;
     #endif
+
+    ZeroSize: if ( size == 0 && !prods`isEmpty ) {
+        if ( !__handle_waituntil_OR( prods ) ) break ZeroSize;
+        __prods_handoff( chan, retval );
+        unlock( mutex_lock );
+        return true;
+    }
+
     if ( count == 0 ) { unlock( mutex_lock ); return false; }
+
     __do_remove( chan, retval );
     unlock( mutex_lock );
@@ -244,8 +244,9 @@
 static inline [T, bool] try_remove( channel(T) & chan ) {
     T retval;
-    return [ retval, __internal_try_remove( chan, retval ) ];
-}
-
-static inline T try_remove( channel(T) & chan, T elem ) {
+    bool success = __internal_try_remove( chan, retval );
+    return [ retval, success ];
+}
+
+static inline T try_remove( channel(T) & chan ) {
     T retval;
     __internal_try_remove( chan, retval );
@@ -255,5 +256,5 @@
 // handles closed case of insert routine
 static inline void __closed_remove( channel(T) & chan, T & retval ) with(chan) {
-    channel_closed except{&channel_closed_vt, 0p, &chan };
+    channel_closed except{ &channel_closed_vt, 0p, &chan };
     throwResume except; // throw resumption
     if ( !__internal_try_remove( chan, retval ) ) throw except; // if try to remove fails (would block), throw termination
@@ -279,7 +280,7 @@
 
     // have to check for the zero size channel case
-    if ( size == 0 && !prods`isEmpty ) {
-        memcpy((void *)&retval, (void *)prods`first.elem, sizeof(T));
-        wake_one( prods );
+    ZeroSize: if ( size == 0 && !prods`isEmpty ) {
+        if ( !__handle_waituntil_OR( prods ) ) break ZeroSize;
+        __prods_handoff( chan, retval );
         unlock( mutex_lock );
         return retval;
@@ -287,5 +288,5 @@
 
     // wait if buffer is empty, work will be completed by someone else
-    if (count == 0) {
+    if ( count == 0 ) {
         #ifdef CHAN_STATS
         blocks++;
@@ -299,7 +300,184 @@
     // Remove from buffer
     __do_remove( chan, retval );
-
     unlock( mutex_lock );
     return retval;
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// The following is support for waituntil (select) statements
+///////////////////////////////////////////////////////////////////////////////////////////
+static inline bool unregister_chan( channel(T) & chan, select_node & node ) with(chan) {
+    if ( !node`isListed && !node.park_counter ) return false; // handle special OR case
+    lock( mutex_lock );
+    if ( node`isListed ) { // op wasn't performed
+        #ifdef CHAN_STATS
+        operations--;
+        #endif
+        remove( node );
+        unlock( mutex_lock );
+        return false;
+    }
+    unlock( mutex_lock );
+
+    // only return true when not special OR case, not exceptional calse and status is SAT
+    return ( node.extra == 0p || !node.park_counter ) ? false : *node.clause_status == __SELECT_SAT;
+}
+
+// type used by select statement to capture a chan read as the selected operation
+struct chan_read {
+    channel(T) & chan;
+    T & ret;
+};
+
+static inline void ?{}( chan_read(T) & cr, channel(T) & chan, T & ret ) {
+    &cr.chan = &chan;
+    &cr.ret = &ret;
+}
+static inline chan_read(T) ?<<?( T & ret, channel(T) & chan ) { chan_read(T) cr{ chan, ret }; return cr; }
+
+static inline void __handle_select_closed_read( chan_read(T) & this, select_node & node ) with(this.chan, this) {
+    __closed_remove( chan, ret );
+    // if we get here then the insert succeeded
+    __make_select_node_available( node );
+}
+
+static inline bool register_select( chan_read(T) & this, select_node & node ) with(this.chan, this) {
+    // mutex(sout) sout | "register_read";
+    lock( mutex_lock );
+    node.extra = &ret; // set .extra so that if it == 0p later in on_selected it is due to channel close
+
+    #ifdef CHAN_STATS
+    if ( !closed ) operations++;
+    #endif
+
+    // check if we can complete operation. If so race to establish winner in special OR case
+    if ( !node.park_counter && ( count != 0 || !prods`isEmpty || unlikely(closed) ) ) {
+        if ( !__make_select_node_available( node ) ) { // we didn't win the race so give up on registering
+           unlock( mutex_lock );
+           return false;
+        }
+    }
+
+    if ( unlikely(closed) ) {
+        unlock( mutex_lock );
+        __handle_select_closed_read( this, node );
+        return true;
+    }
+
+    // have to check for the zero size channel case
+    ZeroSize: if ( size == 0 && !prods`isEmpty ) {
+        if ( !__handle_waituntil_OR( prods ) ) break ZeroSize;
+        __prods_handoff( chan, ret );
+        __set_avail_then_unlock( node, mutex_lock );
+        return true;
+    }
+
+    // wait if buffer is empty, work will be completed by someone else
+    if ( count == 0 ) {
+        #ifdef CHAN_STATS
+        blocks++;
+        #endif
+        
+        insert_last( cons, node );
+        unlock( mutex_lock );
+        return false;
+    }
+
+    // Remove from buffer
+    __do_remove( chan, ret );
+    __set_avail_then_unlock( node, mutex_lock );
+    return true;
+}
+static inline bool unregister_select( chan_read(T) & this, select_node & node ) { return unregister_chan( this.chan, node ); }
+static inline bool on_selected( chan_read(T) & this, select_node & node ) with(this) {
+    if ( node.extra == 0p ) // check if woken up due to closed channel
+        __closed_remove( chan, ret );
+    // This is only reachable if not closed or closed exception was handled
+    return true;
+}
+
+// type used by select statement to capture a chan write as the selected operation
+struct chan_write {
+    channel(T) & chan;
+    T elem;
+};
+
+static inline void ?{}( chan_write(T) & cw, channel(T) & chan, T elem ) {
+    &cw.chan = &chan;
+    memcpy( (void *)&cw.elem, (void *)&elem, sizeof(T) );
+}
+static inline chan_write(T) ?>>?( T elem, channel(T) & chan ) { chan_write(T) cw{ chan, elem }; return cw; }
+
+static inline void __handle_select_closed_write( chan_write(T) & this, select_node & node ) with(this.chan, this) {
+    __closed_insert( chan, elem );
+    // if we get here then the insert succeeded
+    __make_select_node_available( node );
+}
+
+static inline bool register_select( chan_write(T) & this, select_node & node ) with(this.chan, this) {
+    // mutex(sout) sout | "register_write";
+    lock( mutex_lock );
+    node.extra = &elem; // set .extra so that if it == 0p later in on_selected it is due to channel close
+
+    #ifdef CHAN_STATS
+    if ( !closed ) operations++;
+    #endif
+
+    // check if we can complete operation. If so race to establish winner in special OR case
+    if ( !node.park_counter && ( count != size || !cons`isEmpty || unlikely(closed) ) ) {
+        if ( !__make_select_node_available( node ) ) { // we didn't win the race so give up on registering
+           unlock( mutex_lock );
+           return false;
+        }
+    }
+
+    // if closed handle
+    if ( unlikely(closed) ) {
+        unlock( mutex_lock );
+        __handle_select_closed_write( this, node );
+        return true;
+    }
+
+    // handle blocked consumer case via handoff (buffer is implicitly empty)
+    ConsEmpty: if ( !cons`isEmpty ) {
+        if ( !__handle_waituntil_OR( cons ) ) {
+            // mutex(sout) sout | "empty";
+            break ConsEmpty;
+        }
+        // mutex(sout) sout | "signal";
+        __cons_handoff( chan, elem );
+        __set_avail_then_unlock( node, mutex_lock );
+        return true;
+    }
+
+    // insert node in list if buffer is full, work will be completed by someone else
+    if ( count == size ) {
+        #ifdef CHAN_STATS
+        blocks++;
+        #endif
+
+        insert_last( prods, node );
+        unlock( mutex_lock );
+        return false;
+    } // if
+
+    // otherwise carry out write either via normal insert
+    __buf_insert( chan, elem );
+    __set_avail_then_unlock( node, mutex_lock );
+    return true;
+}
+static inline bool unregister_select( chan_write(T) & this, select_node & node ) { return unregister_chan( this.chan, node ); }
+
+static inline bool on_selected( chan_write(T) & this, select_node & node ) with(this) { 
+    if ( node.extra == 0p ) // check if woken up due to closed channel
+        __closed_insert( chan, elem );
+
+    // This is only reachable if not closed or closed exception was handled
+    return true;
+}
+
+
 } // forall( T )
+
+
+
Index: libcfa/src/concurrency/future.hfa
===================================================================
--- libcfa/src/concurrency/future.hfa	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ libcfa/src/concurrency/future.hfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -19,4 +19,5 @@
 #include "monitor.hfa"
 #include "select.hfa"
+#include "locks.hfa"
 
 //----------------------------------------------------------------------------
@@ -26,5 +27,5 @@
 //  future_t is lockfree and uses atomics which aren't needed given we use locks here
 forall( T ) {
-    // enum(int) { FUTURE_EMPTY = 0, FUTURE_FULFILLED = 1 }; // Enums seem to be broken so feel free to add this back afterwards
+    // enum { FUTURE_EMPTY = 0, FUTURE_FULFILLED = 1 }; // Enums seem to be broken so feel free to add this back afterwards
 
     // temporary enum replacement
@@ -44,6 +45,4 @@
     };
 
-    // C_TODO: perhaps allow exceptions to be inserted like uC++?
-
 	static inline {
 
@@ -82,11 +81,12 @@
         void _internal_flush( future(T) & this ) with(this) {
             while( ! waiters`isEmpty ) {
+                if ( !__handle_waituntil_OR( waiters ) ) // handle special waituntil OR case
+                    break; // if handle_OR returns false then waiters is empty so break
                 select_node &s = try_pop_front( waiters );
 
-                if ( s.race_flag == 0p )
+                if ( s.clause_status == 0p )
                     // poke in result so that woken threads do not need to reacquire any locks
-                    // *(((future_node(T) &)s).my_result) = result;
                     copy_T( result, *(((future_node(T) &)s).my_result) );
-                else if ( !install_select_winner( s, &this ) ) continue;
+                else if ( !__make_select_node_available( s ) ) continue;
                 
                 // only unpark if future is not selected
@@ -97,5 +97,5 @@
 
 		// Fulfil the future, returns whether or not someone was unblocked
-		bool fulfil( future(T) & this, T & val ) with(this) {
+		bool fulfil( future(T) & this, T val ) with(this) {
             lock( lock );
             if( state != FUTURE_EMPTY )
@@ -153,31 +153,36 @@
         }
 
-        void * register_select( future(T) & this, select_node & s ) with(this) {
-            lock( lock );
-
-            // future not ready -> insert select node and return 0p
+        bool register_select( future(T) & this, select_node & s ) with(this) {
+            lock( lock );
+
+            // check if we can complete operation. If so race to establish winner in special OR case
+            if ( !s.park_counter && state != FUTURE_EMPTY ) { 
+                if ( !__make_select_node_available( s ) ) { // we didn't win the race so give up on registering
+                    unlock( lock );
+                    return false;
+                }
+            }
+
+            // future not ready -> insert select node and return
             if( state == FUTURE_EMPTY ) {
                 insert_last( waiters, s );
                 unlock( lock );
-                return 0p;
-            }
-
-            // future ready and we won race to install it as the select winner return 1p
-            if ( install_select_winner( s, &this ) ) {
-                unlock( lock );
-                return 1p;
-            }
-
-            unlock( lock );
-            // future ready and we lost race to install it as the select winner
-            return 2p;
-        }
-
-        void unregister_select( future(T) & this, select_node & s ) with(this) {
+                return false;
+            }
+
+            __make_select_node_available( s );
+            unlock( lock );
+            return true;
+        }
+
+        bool unregister_select( future(T) & this, select_node & s ) with(this) {
+            if ( ! s`isListed ) return false;
             lock( lock );
             if ( s`isListed ) remove( s );
             unlock( lock );
+            return false;
         }
 		
+        bool on_selected( future(T) & this, select_node & node ) { return true; }
 	}
 }
@@ -186,5 +191,5 @@
 // These futures below do not support select statements so they may not be as useful as 'future'
 //  however the 'single_future' is cheap and cheerful and is most likely more performant than 'future'
-//  since it uses raw atomics and no locks afaik
+//  since it uses raw atomics and no locks
 //
 // As far as 'multi_future' goes I can't see many use cases as it will be less performant than 'future'
Index: libcfa/src/concurrency/invoke.h
===================================================================
--- libcfa/src/concurrency/invoke.h	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ libcfa/src/concurrency/invoke.h	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -217,8 +217,9 @@
 		struct __thread_user_link cltr_link;
 
-		// used to point to this thd's current clh node
-		volatile bool * clh_node;
-
 		struct processor * last_proc;
+
+        // ptr used during handover between blocking lists to allow for stack allocation of intrusive nodes
+        // main use case is wait-morphing to allow a different node to be used to block on condvar vs lock
+        void * link_node;
 
 		PRNG_STATE_T random_state;						// fast random numbers
Index: libcfa/src/concurrency/locks.cfa
===================================================================
--- libcfa/src/concurrency/locks.cfa	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ libcfa/src/concurrency/locks.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -79,20 +79,17 @@
 	// lock is held by some other thread
 	if ( owner != 0p && owner != thrd ) {
-		insert_last( blocked_threads, *thrd );
+        select_node node;
+		insert_last( blocked_threads, node );
 		wait_count++;
 		unlock( lock );
 		park( );
-	}
-	// multi acquisition lock is held by current thread
-	else if ( owner == thrd && multi_acquisition ) {
+        return;
+	} else if ( owner == thrd && multi_acquisition ) { // multi acquisition lock is held by current thread
 		recursion_count++;
-		unlock( lock );
-	}
-	// lock isn't held
-	else {
+	} else {  // lock isn't held
 		owner = thrd;
 		recursion_count = 1;
-		unlock( lock );
-	}
+	}
+    unlock( lock );
 }
 
@@ -117,10 +114,25 @@
 }
 
-static void pop_and_set_new_owner( blocking_lock & this ) with( this ) {
-	thread$ * t = &try_pop_front( blocked_threads );
-	owner = t;
-	recursion_count = ( t ? 1 : 0 );
-	if ( t ) wait_count--;
-	unpark( t );
+// static void pop_and_set_new_owner( blocking_lock & this ) with( this ) {
+// 	thread$ * t = &try_pop_front( blocked_threads );
+// 	owner = t;
+// 	recursion_count = ( t ? 1 : 0 );
+// 	if ( t ) wait_count--;
+// 	unpark( t );
+// }
+
+static inline void pop_node( blocking_lock & this ) with( this ) {
+    __handle_waituntil_OR( blocked_threads );
+    select_node * node = &try_pop_front( blocked_threads );
+    if ( node ) {
+        wait_count--;
+        owner = node->blocked_thread;
+        recursion_count = 1;
+        // if ( !node->clause_status || __make_select_node_available( *node ) ) unpark( node->blocked_thread );
+        wake_one( blocked_threads, *node );
+    } else {
+        owner = 0p;
+        recursion_count = 0;
+    }
 }
 
@@ -134,5 +146,5 @@
 	recursion_count--;
 	if ( recursion_count == 0 ) {
-		pop_and_set_new_owner( this );
+		pop_node( this );
 	}
 	unlock( lock );
@@ -147,7 +159,6 @@
 	// lock held
 	if ( owner != 0p ) {
-		insert_last( blocked_threads, *t );
+		insert_last( blocked_threads, *(select_node *)t->link_node );
 		wait_count++;
-		unlock( lock );
 	}
 	// lock not held
@@ -156,6 +167,6 @@
 		recursion_count = 1;
 		unpark( t );
-		unlock( lock );
-	}
+	}
+    unlock( lock );
 }
 
@@ -167,6 +178,12 @@
 	size_t ret = recursion_count;
 
-	pop_and_set_new_owner( this );
+	pop_node( this );
+
+    select_node node;
+    active_thread()->link_node = (void *)&node;
 	unlock( lock );
+
+    park();
+
 	return ret;
 }
@@ -175,4 +192,60 @@
 	recursion_count = recursion;
 }
+
+// waituntil() support
+bool register_select( blocking_lock & this, select_node & node ) with(this) {
+    lock( lock __cfaabi_dbg_ctx2 );
+	thread$ * thrd = active_thread();
+
+	// single acquisition lock is held by current thread
+	/* paranoid */ verifyf( owner != thrd || multi_acquisition, "Single acquisition lock holder (%p) attempted to reacquire the lock %p resulting in a deadlock.", owner, &this );
+
+    if ( !node.park_counter && ( (owner == thrd && multi_acquisition) || owner == 0p ) ) { // OR special case
+        if ( !__make_select_node_available( node ) ) { // we didn't win the race so give up on registering
+           unlock( lock );
+           return false;
+        }
+    }
+
+	// lock is held by some other thread
+	if ( owner != 0p && owner != thrd ) {
+		insert_last( blocked_threads, node );
+		wait_count++;
+		unlock( lock );
+        return false;
+	} else if ( owner == thrd && multi_acquisition ) { // multi acquisition lock is held by current thread
+		recursion_count++;
+	} else {  // lock isn't held
+		owner = thrd;
+		recursion_count = 1;
+	}
+
+    if ( node.park_counter ) __make_select_node_available( node );
+    unlock( lock );
+    return true;
+}
+
+bool unregister_select( blocking_lock & this, select_node & node ) with(this) {
+    lock( lock __cfaabi_dbg_ctx2 );
+    if ( node`isListed ) {
+        remove( node );
+        wait_count--;
+        unlock( lock );
+        return false;
+    }
+    
+    if ( owner == active_thread() ) {
+        /* paranoid */ verifyf( recursion_count == 1 || multi_acquisition, "Thread %p attempted to unlock owner lock %p in waituntil unregister, which is not recursive but has a recursive count of %zu", active_thread(), &this, recursion_count );
+        // if recursion count is zero release lock and set new owner if one is waiting
+        recursion_count--;
+        if ( recursion_count == 0 ) {
+            pop_node( this );
+        }
+    }
+	unlock( lock );
+    return false;
+}
+
+bool on_selected( blocking_lock & this, select_node & node ) { return true; }
 
 //-----------------------------------------------------------------------------
@@ -311,27 +384,38 @@
 	int counter( condition_variable(L) & this ) with(this) { return count; }
 
-	static size_t queue_and_get_recursion( condition_variable(L) & this, info_thread(L) * i ) with(this) {
+	static void enqueue_thread( condition_variable(L) & this, info_thread(L) * i ) with(this) {
 		// add info_thread to waiting queue
 		insert_last( blocked_threads, *i );
 		count++;
-		size_t recursion_count = 0;
-		if (i->lock) {
+		// size_t recursion_count = 0;
+		// if (i->lock) {
+		// 	// if lock was passed get recursion count to reset to after waking thread
+		// 	recursion_count = on_wait( *i->lock );
+		// }
+		// return recursion_count;
+	}
+
+    static size_t block_and_get_recursion( info_thread(L) & i ) {
+        size_t recursion_count = 0;
+		if ( i.lock ) {
 			// if lock was passed get recursion count to reset to after waking thread
-			recursion_count = on_wait( *i->lock );
-		}
-		return recursion_count;
-	}
+			recursion_count = on_wait( *i.lock ); // this call blocks
+		} else park( );
+        return recursion_count;
+    }
 
 	// helper for wait()'s' with no timeout
 	static void queue_info_thread( condition_variable(L) & this, info_thread(L) & i ) with(this) {
 		lock( lock __cfaabi_dbg_ctx2 );
-		size_t recursion_count = queue_and_get_recursion(this, &i);
+        enqueue_thread( this, &i );
+		// size_t recursion_count = queue_and_get_recursion( this, &i );
 		unlock( lock );
 
 		// blocks here
-		park( );
+        size_t recursion_count = block_and_get_recursion( i );
+		// park( );
 
 		// resets recursion count here after waking
-		if (i.lock) on_wakeup(*i.lock, recursion_count);
+		if ( i.lock ) on_wakeup( *i.lock, recursion_count );
 	}
 
@@ -343,5 +427,6 @@
 	static void queue_info_thread_timeout( condition_variable(L) & this, info_thread(L) & info, Duration t, Alarm_Callback callback ) with(this) {
 		lock( lock __cfaabi_dbg_ctx2 );
-		size_t recursion_count = queue_and_get_recursion(this, &info);
+        enqueue_thread( this, &info );
+		// size_t recursion_count = queue_and_get_recursion( this, &info );
 		alarm_node_wrap(L) node_wrap = { t, 0`s, callback, &this, &info };
 		unlock( lock );
@@ -351,5 +436,6 @@
 
 		// blocks here
-		park();
+        size_t recursion_count = block_and_get_recursion( info );
+		// park();
 
 		// unregisters alarm so it doesn't go off if this happens first
@@ -357,5 +443,5 @@
 
 		// resets recursion count here after waking
-		if (info.lock) on_wakeup(*info.lock, recursion_count);
+		if ( info.lock ) on_wakeup( *info.lock, recursion_count );
 	}
 
@@ -417,6 +503,6 @@
 		info_thread( L ) i = { active_thread(), info, &l };
 		insert_last( blocked_threads, i );
-		size_t recursion_count = on_wait( *i.lock );
-		park( );
+		size_t recursion_count = on_wait( *i.lock ); // blocks here
+		// park( );
 		on_wakeup(*i.lock, recursion_count);
 	}
@@ -459,15 +545,17 @@
 	bool empty ( pthread_cond_var(L) & this ) with(this) { return blocked_threads`isEmpty; }
 
-	static size_t queue_and_get_recursion( pthread_cond_var(L) & this, info_thread(L) * i ) with(this) {
-		// add info_thread to waiting queue
-		insert_last( blocked_threads, *i );
-		size_t recursion_count = 0;
-		recursion_count = on_wait( *i->lock );
-		return recursion_count;
-	}
+	// static size_t queue_and_get_recursion( pthread_cond_var(L) & this, info_thread(L) * i ) with(this) {
+	// 	// add info_thread to waiting queue
+	// 	insert_last( blocked_threads, *i );
+	// 	size_t recursion_count = 0;
+	// 	recursion_count = on_wait( *i->lock );
+	// 	return recursion_count;
+	// }
+
 	
 	static void queue_info_thread_timeout( pthread_cond_var(L) & this, info_thread(L) & info, Duration t, Alarm_Callback callback ) with(this) {
 		lock( lock __cfaabi_dbg_ctx2 );
-		size_t recursion_count = queue_and_get_recursion(this, &info);
+		// size_t recursion_count = queue_and_get_recursion(this, &info);
+        insert_last( blocked_threads, info );
 		pthread_alarm_node_wrap(L) node_wrap = { t, 0`s, callback, &this, &info };
 		unlock( lock );
@@ -477,5 +565,6 @@
 
 		// blocks here
-		park();
+        size_t recursion_count = block_and_get_recursion( info );
+		// park();
 
 		// unregisters alarm so it doesn't go off if this happens first
@@ -483,5 +572,5 @@
 
 		// resets recursion count here after waking
-		if (info.lock) on_wakeup(*info.lock, recursion_count);
+		if ( info.lock ) on_wakeup( *info.lock, recursion_count );
 	}
 
@@ -493,8 +582,12 @@
 		lock( lock __cfaabi_dbg_ctx2 );
 		info_thread( L ) i = { active_thread(), info, &l };
-		size_t recursion_count = queue_and_get_recursion(this, &i);
-		unlock( lock );
-		park( );
-		on_wakeup(*i.lock, recursion_count);
+        insert_last( blocked_threads, i );
+		// size_t recursion_count = queue_and_get_recursion( this, &i );
+		unlock( lock );
+
+        // blocks here
+		size_t recursion_count = block_and_get_recursion( i );
+		// park();
+		on_wakeup( *i.lock, recursion_count );
 	}
 
Index: libcfa/src/concurrency/locks.hfa
===================================================================
--- libcfa/src/concurrency/locks.hfa	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ libcfa/src/concurrency/locks.hfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -30,4 +30,6 @@
 #include "time.hfa"
 
+#include "select.hfa"
+
 #include <fstream.hfa>
 
@@ -70,4 +72,7 @@
 static inline void   on_wakeup( single_acquisition_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); }
 static inline void   on_notify( single_acquisition_lock & this, struct thread$ * t ) { on_notify( (blocking_lock &)this, t ); }
+static inline bool   register_select( single_acquisition_lock & this, select_node & node ) { return register_select( (blocking_lock &)this, node ); }
+static inline bool   unregister_select( single_acquisition_lock & this, select_node & node ) { return unregister_select( (blocking_lock &)this, node ); }
+static inline bool   on_selected( single_acquisition_lock & this, select_node & node ) { return on_selected( (blocking_lock &)this, node ); }
 
 //----------
@@ -84,4 +89,7 @@
 static inline void   on_wakeup( owner_lock & this, size_t v ) { on_wakeup ( (blocking_lock &)this, v ); }
 static inline void   on_notify( owner_lock & this, struct thread$ * t ) { on_notify( (blocking_lock &)this, t ); }
+static inline bool   register_select( owner_lock & this, select_node & node ) { return register_select( (blocking_lock &)this, node ); }
+static inline bool   unregister_select( owner_lock & this, select_node & node ) { return unregister_select( (blocking_lock &)this, node ); }
+static inline bool   on_selected( owner_lock & this, select_node & node ) { return on_selected( (blocking_lock &)this, node ); }
 
 //-----------------------------------------------------------------------------
@@ -180,5 +188,5 @@
 
 // if this is called recursively IT WILL DEADLOCK!!!!!
-static inline void lock(futex_mutex & this) with(this) {
+static inline void lock( futex_mutex & this ) with(this) {
 	int state;
 
@@ -190,7 +198,4 @@
 		for (int i = 0; i < spin; i++) Pause();
 	}
-
-	// // no contention try to acquire
-	// if (internal_try_lock(this, state)) return;
 	
 	// if not in contended state, set to be in contended state
@@ -213,5 +218,5 @@
 
 static inline void on_notify( futex_mutex & f, thread$ * t){ unpark(t); }
-static inline size_t on_wait( futex_mutex & f ) {unlock(f); return 0;}
+static inline size_t on_wait( futex_mutex & f ) { unlock(f); park(); return 0; }
 
 // to set recursion count after getting signalled;
@@ -244,5 +249,5 @@
 
 // if this is called recursively IT WILL DEADLOCK!!!!!
-static inline void lock(go_mutex & this) with(this) {
+static inline void lock( go_mutex & this ) with( this ) {
 	int state, init_state;
 
@@ -255,5 +260,5 @@
             while( !val ) { // lock unlocked
                 state = 0;
-                if (internal_try_lock(this, state, init_state)) return;
+                if ( internal_try_lock( this, state, init_state ) ) return;
             }
             for (int i = 0; i < 30; i++) Pause();
@@ -262,13 +267,13 @@
         while( !val ) { // lock unlocked
             state = 0;
-            if (internal_try_lock(this, state, init_state)) return;
+            if ( internal_try_lock( this, state, init_state ) ) return;
         }
         sched_yield();
         
         // if not in contended state, set to be in contended state
-        state = internal_exchange(this, 2);
+        state = internal_exchange( this, 2 );
         if ( !state ) return; // state == 0
         init_state = 2;
-        futex((int*)&val, FUTEX_WAIT, 2); // if val is not 2 this returns with EWOULDBLOCK
+        futex( (int*)&val, FUTEX_WAIT, 2 ); // if val is not 2 this returns with EWOULDBLOCK
     }
 }
@@ -276,43 +281,13 @@
 static inline void unlock( go_mutex & this ) with(this) {
 	// if uncontended do atomic unlock and then return
-    if (__atomic_exchange_n(&val, 0, __ATOMIC_RELEASE) == 1) return;
+    if ( __atomic_exchange_n(&val, 0, __ATOMIC_RELEASE) == 1 ) return;
 	
 	// otherwise threads are blocked so we must wake one
-	futex((int *)&val, FUTEX_WAKE, 1);
-}
-
-static inline void on_notify( go_mutex & f, thread$ * t){ unpark(t); }
-static inline size_t on_wait( go_mutex & f ) {unlock(f); return 0;}
+	futex( (int *)&val, FUTEX_WAKE, 1 );
+}
+
+static inline void on_notify( go_mutex & f, thread$ * t){ unpark( t ); }
+static inline size_t on_wait( go_mutex & f ) { unlock( f ); park(); return 0; }
 static inline void on_wakeup( go_mutex & f, size_t recursion ) {}
-
-//-----------------------------------------------------------------------------
-// CLH Spinlock
-// - No recursive acquisition
-// - Needs to be released by owner
-
-struct clh_lock {
-	volatile bool * volatile tail;
-    volatile bool * volatile head;
-};
-
-static inline void  ?{}( clh_lock & this ) { this.tail = malloc(); *this.tail = true; }
-static inline void ^?{}( clh_lock & this ) { free(this.tail); }
-
-static inline void lock(clh_lock & l) {
-	thread$ * curr_thd = active_thread();
-	*(curr_thd->clh_node) = false;
-	volatile bool * prev = __atomic_exchange_n((bool **)(&l.tail), (bool *)(curr_thd->clh_node), __ATOMIC_SEQ_CST);
-	while(!__atomic_load_n(prev, __ATOMIC_SEQ_CST)) Pause();
-    __atomic_store_n((bool **)(&l.head), (bool *)curr_thd->clh_node, __ATOMIC_SEQ_CST);
-    curr_thd->clh_node = prev;
-}
-
-static inline void unlock(clh_lock & l) {
-	__atomic_store_n((bool *)(l.head), true, __ATOMIC_SEQ_CST);
-}
-
-static inline void on_notify(clh_lock & this, struct thread$ * t ) { unpark(t); }
-static inline size_t on_wait(clh_lock & this) { unlock(this); return 0; }
-static inline void on_wakeup(clh_lock & this, size_t recursion ) { lock(this); }
 
 //-----------------------------------------------------------------------------
@@ -337,15 +312,15 @@
 static inline void  ^?{}( exp_backoff_then_block_lock & this ){}
 
-static inline bool internal_try_lock(exp_backoff_then_block_lock & this, size_t & compare_val) with(this) {
+static inline bool internal_try_lock( exp_backoff_then_block_lock & this, size_t & compare_val ) with(this) {
 	return __atomic_compare_exchange_n(&lock_value, &compare_val, 1, false, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
 }
 
-static inline bool try_lock(exp_backoff_then_block_lock & this) { size_t compare_val = 0; return internal_try_lock(this, compare_val); }
-
-static inline bool try_lock_contention(exp_backoff_then_block_lock & this) with(this) {
-	return !__atomic_exchange_n(&lock_value, 2, __ATOMIC_ACQUIRE);
-}
-
-static inline bool block(exp_backoff_then_block_lock & this) with(this) {
+static inline bool try_lock( exp_backoff_then_block_lock & this ) { size_t compare_val = 0; return internal_try_lock( this, compare_val ); }
+
+static inline bool try_lock_contention( exp_backoff_then_block_lock & this ) with(this) {
+	return !__atomic_exchange_n( &lock_value, 2, __ATOMIC_ACQUIRE );
+}
+
+static inline bool block( exp_backoff_then_block_lock & this ) with(this) {
     lock( spinlock __cfaabi_dbg_ctx2 );
     if (__atomic_load_n( &lock_value, __ATOMIC_SEQ_CST) != 2) {
@@ -359,5 +334,5 @@
 }
 
-static inline void lock(exp_backoff_then_block_lock & this) with(this) {
+static inline void lock( exp_backoff_then_block_lock & this ) with(this) {
 	size_t compare_val = 0;
 	int spin = 4;
@@ -378,5 +353,5 @@
 }
 
-static inline void unlock(exp_backoff_then_block_lock & this) with(this) {
+static inline void unlock( exp_backoff_then_block_lock & this ) with(this) {
     if (__atomic_exchange_n(&lock_value, 0, __ATOMIC_RELEASE) == 1) return;
     lock( spinlock __cfaabi_dbg_ctx2 );
@@ -386,7 +361,7 @@
 }
 
-static inline void on_notify(exp_backoff_then_block_lock & this, struct thread$ * t ) { unpark(t); }
-static inline size_t on_wait(exp_backoff_then_block_lock & this) { unlock(this); return 0; }
-static inline void on_wakeup(exp_backoff_then_block_lock & this, size_t recursion ) { lock(this); }
+static inline void on_notify( exp_backoff_then_block_lock & this, struct thread$ * t ) { unpark( t ); }
+static inline size_t on_wait( exp_backoff_then_block_lock & this ) { unlock( this ); park(); return 0; }
+static inline void on_wakeup( exp_backoff_then_block_lock & this, size_t recursion ) { lock( this ); }
 
 //-----------------------------------------------------------------------------
@@ -418,5 +393,5 @@
 
 // if this is called recursively IT WILL DEADLOCK!!!!!
-static inline void lock(fast_block_lock & this) with(this) {
+static inline void lock( fast_block_lock & this ) with(this) {
 	lock( lock __cfaabi_dbg_ctx2 );
 	if ( held ) {
@@ -430,5 +405,5 @@
 }
 
-static inline void unlock(fast_block_lock & this) with(this) {
+static inline void unlock( fast_block_lock & this ) with(this) {
 	lock( lock __cfaabi_dbg_ctx2 );
 	/* paranoid */ verifyf( held != false, "Attempt to release lock %p that isn't held", &this );
@@ -439,11 +414,11 @@
 }
 
-static inline void on_notify(fast_block_lock & this, struct thread$ * t ) with(this) {
+static inline void on_notify( fast_block_lock & this, struct thread$ * t ) with(this) {
     lock( lock __cfaabi_dbg_ctx2 );
     insert_last( blocked_threads, *t );
     unlock( lock );
 }
-static inline size_t on_wait(fast_block_lock & this) { unlock(this); return 0; }
-static inline void on_wakeup(fast_block_lock & this, size_t recursion ) { }
+static inline size_t on_wait( fast_block_lock & this) { unlock(this); park(); return 0; }
+static inline void on_wakeup( fast_block_lock & this, size_t recursion ) { }
 
 //-----------------------------------------------------------------------------
@@ -456,5 +431,5 @@
 struct simple_owner_lock {
 	// List of blocked threads
-	dlist( thread$ ) blocked_threads;
+	dlist( select_node ) blocked_threads;
 
 	// Spin lock used for mutual exclusion
@@ -477,6 +452,6 @@
 static inline void ?=?( simple_owner_lock & this, simple_owner_lock this2 ) = void;
 
-static inline void lock(simple_owner_lock & this) with(this) {
-	if (owner == active_thread()) {
+static inline void lock( simple_owner_lock & this ) with(this) {
+	if ( owner == active_thread() ) {
 		recursion_count++;
 		return;
@@ -484,6 +459,7 @@
 	lock( lock __cfaabi_dbg_ctx2 );
 
-	if (owner != 0p) {
-		insert_last( blocked_threads, *active_thread() );
+	if ( owner != 0p ) {
+        select_node node;
+		insert_last( blocked_threads, node );
 		unlock( lock );
 		park( );
@@ -495,13 +471,19 @@
 }
 
-// TODO: fix duplicate def issue and bring this back
-// void pop_and_set_new_owner( simple_owner_lock & this ) with( this ) {
-	// thread$ * t = &try_pop_front( blocked_threads );
-	// owner = t;
-	// recursion_count = ( t ? 1 : 0 );
-	// unpark( t );
-// }
-
-static inline void unlock(simple_owner_lock & this) with(this) {
+static inline void pop_node( simple_owner_lock & this ) with(this) {
+    __handle_waituntil_OR( blocked_threads );
+    select_node * node = &try_pop_front( blocked_threads );
+    if ( node ) {
+        owner = node->blocked_thread;
+        recursion_count = 1;
+        // if ( !node->clause_status || __make_select_node_available( *node ) ) unpark( node->blocked_thread );
+        wake_one( blocked_threads, *node );
+    } else {
+        owner = 0p;
+        recursion_count = 0;
+    }
+}
+
+static inline void unlock( simple_owner_lock & this ) with(this) {
 	lock( lock __cfaabi_dbg_ctx2 );
 	/* paranoid */ verifyf( owner != 0p, "Attempt to release lock %p that isn't held", &this );
@@ -510,18 +492,14 @@
 	recursion_count--;
 	if ( recursion_count == 0 ) {
-		// pop_and_set_new_owner( this );
-		thread$ * t = &try_pop_front( blocked_threads );
-		owner = t;
-		recursion_count = ( t ? 1 : 0 );
-		unpark( t );
+		pop_node( this );
 	}
 	unlock( lock );
 }
 
-static inline void on_notify(simple_owner_lock & this, struct thread$ * t ) with(this) {
+static inline void on_notify(simple_owner_lock & this, thread$ * t ) with(this) {
 	lock( lock __cfaabi_dbg_ctx2 );
 	// lock held
 	if ( owner != 0p ) {
-		insert_last( blocked_threads, *t );
+		insert_last( blocked_threads, *(select_node *)t->link_node );
 	}
 	// lock not held
@@ -534,5 +512,5 @@
 }
 
-static inline size_t on_wait(simple_owner_lock & this) with(this) {
+static inline size_t on_wait( simple_owner_lock & this ) with(this) {
 	lock( lock __cfaabi_dbg_ctx2 );
 	/* paranoid */ verifyf( owner != 0p, "Attempt to release lock %p that isn't held", &this );
@@ -541,16 +519,69 @@
 	size_t ret = recursion_count;
 
-	// pop_and_set_new_owner( this );
-
-	thread$ * t = &try_pop_front( blocked_threads );
-	owner = t;
-	recursion_count = ( t ? 1 : 0 );
-	unpark( t );
-
+	pop_node( this );
+
+    select_node node;
+    active_thread()->link_node = (void *)&node;
 	unlock( lock );
+    park();
+
 	return ret;
 }
 
-static inline void on_wakeup(simple_owner_lock & this, size_t recursion ) with(this) { recursion_count = recursion; }
+static inline void on_wakeup( simple_owner_lock & this, size_t recursion ) with(this) { recursion_count = recursion; }
+
+// waituntil() support
+static inline bool register_select( simple_owner_lock & this, select_node & node ) with(this) {
+    lock( lock __cfaabi_dbg_ctx2 );
+
+    // check if we can complete operation. If so race to establish winner in special OR case
+    if ( !node.park_counter && ( owner == active_thread() || owner == 0p ) ) {
+        if ( !__make_select_node_available( node ) ) { // we didn't win the race so give up on registering
+           unlock( lock );
+           return false;
+        }
+    }
+
+    if ( owner == active_thread() ) {
+		recursion_count++;
+        if ( node.park_counter ) __make_select_node_available( node );
+        unlock( lock );
+		return true;
+	}
+
+    if ( owner != 0p ) {
+		insert_last( blocked_threads, node );
+		unlock( lock );
+		return false;
+	}
+    
+	owner = active_thread();
+	recursion_count = 1;
+
+    if ( node.park_counter ) __make_select_node_available( node );
+    unlock( lock );
+    return true;
+}
+
+static inline bool unregister_select( simple_owner_lock & this, select_node & node ) with(this) {
+    lock( lock __cfaabi_dbg_ctx2 );
+    if ( node`isListed ) {
+        remove( node );
+        unlock( lock );
+        return false;
+    }
+
+    if ( owner == active_thread() ) {
+        recursion_count--;
+        if ( recursion_count == 0 ) {
+            pop_node( this );
+        }
+    }
+    unlock( lock );
+    return false;
+}
+
+static inline bool on_selected( simple_owner_lock & this, select_node & node ) { return true; }
+
 
 //-----------------------------------------------------------------------------
@@ -578,5 +609,5 @@
 
 // if this is called recursively IT WILL DEADLOCK!
-static inline void lock(spin_queue_lock & this) with(this) {
+static inline void lock( spin_queue_lock & this ) with(this) {
 	mcs_spin_node node;
 	lock( lock, node );
@@ -586,13 +617,13 @@
 }
 
-static inline void unlock(spin_queue_lock & this) with(this) {
+static inline void unlock( spin_queue_lock & this ) with(this) {
 	__atomic_store_n(&held, false, __ATOMIC_RELEASE);
 }
 
-static inline void on_notify(spin_queue_lock & this, struct thread$ * t ) {
+static inline void on_notify( spin_queue_lock & this, struct thread$ * t ) {
 	unpark(t);
 }
-static inline size_t on_wait(spin_queue_lock & this) { unlock(this); return 0; }
-static inline void on_wakeup(spin_queue_lock & this, size_t recursion ) { lock(this); }
+static inline size_t on_wait( spin_queue_lock & this ) { unlock( this ); park(); return 0; }
+static inline void on_wakeup( spin_queue_lock & this, size_t recursion ) { lock( this ); }
 
 
@@ -621,5 +652,5 @@
 
 // if this is called recursively IT WILL DEADLOCK!!!!!
-static inline void lock(mcs_block_spin_lock & this) with(this) {
+static inline void lock( mcs_block_spin_lock & this ) with(this) {
 	mcs_node node;
 	lock( lock, node );
@@ -633,7 +664,7 @@
 }
 
-static inline void on_notify(mcs_block_spin_lock & this, struct thread$ * t ) { unpark(t); }
-static inline size_t on_wait(mcs_block_spin_lock & this) { unlock(this); return 0; }
-static inline void on_wakeup(mcs_block_spin_lock & this, size_t recursion ) {lock(this); }
+static inline void on_notify( mcs_block_spin_lock & this, struct thread$ * t ) { unpark( t ); }
+static inline size_t on_wait( mcs_block_spin_lock & this) { unlock( this ); park(); return 0; }
+static inline void on_wakeup( mcs_block_spin_lock & this, size_t recursion ) {lock( this ); }
 
 //-----------------------------------------------------------------------------
@@ -661,5 +692,5 @@
 
 // if this is called recursively IT WILL DEADLOCK!!!!!
-static inline void lock(block_spin_lock & this) with(this) {
+static inline void lock( block_spin_lock & this ) with(this) {
 	lock( lock );
 	while(__atomic_load_n(&held, __ATOMIC_SEQ_CST)) Pause();
@@ -668,9 +699,9 @@
 }
 
-static inline void unlock(block_spin_lock & this) with(this) {
+static inline void unlock( block_spin_lock & this ) with(this) {
 	__atomic_store_n(&held, false, __ATOMIC_RELEASE);
 }
 
-static inline void on_notify(block_spin_lock & this, struct thread$ * t ) with(this.lock) {
+static inline void on_notify( block_spin_lock & this, struct thread$ * t ) with(this.lock) {
 	// first we acquire internal fast_block_lock
 	lock( lock __cfaabi_dbg_ctx2 );
@@ -686,6 +717,6 @@
 	unpark(t);
 }
-static inline size_t on_wait(block_spin_lock & this) { unlock(this); return 0; }
-static inline void on_wakeup(block_spin_lock & this, size_t recursion ) with(this) {
+static inline size_t on_wait( block_spin_lock & this ) { unlock( this ); park(); return 0; }
+static inline void on_wakeup( block_spin_lock & this, size_t recursion ) with(this) {
 	// now we acquire the entire block_spin_lock upon waking up
 	while(__atomic_load_n(&held, __ATOMIC_SEQ_CST)) Pause();
@@ -714,8 +745,4 @@
 forall(L & | is_blocking_lock(L)) {
 	struct info_thread;
-
-	// // for use by sequence
-	// info_thread(L) *& Back( info_thread(L) * this );
-	// info_thread(L) *& Next( info_thread(L) * this );
 }
 
Index: libcfa/src/concurrency/mutex_stmt.hfa
===================================================================
--- libcfa/src/concurrency/mutex_stmt.hfa	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ libcfa/src/concurrency/mutex_stmt.hfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -15,5 +15,4 @@
 };
 
-
 struct __mutex_stmt_lock_guard {
     void ** lockarr;
@@ -30,24 +29,6 @@
 
 forall(L & | is_lock(L)) {
-
-    struct scoped_lock {
-        L * internal_lock;
-    };
-
-    static inline void ?{}( scoped_lock(L) & this, L & internal_lock ) {
-        this.internal_lock = &internal_lock;
-        lock(internal_lock);
-    }
-    
-    static inline void ^?{}( scoped_lock(L) & this ) with(this) {
-        unlock(*internal_lock);
-    }
-
-    static inline void * __get_mutexstmt_lock_ptr( L & this ) {
-        return &this;
-    }
-
-    static inline L __get_mutexstmt_lock_type( L & this );
-
-    static inline L __get_mutexstmt_lock_type( L * this );
+    static inline void * __get_mutexstmt_lock_ptr( L & this ) { return &this; }
+    static inline L __get_mutexstmt_lock_type( L & this ) {}
+    static inline L __get_mutexstmt_lock_type( L * this ) {}
 }
Index: libcfa/src/concurrency/select.hfa
===================================================================
--- libcfa/src/concurrency/select.hfa	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ libcfa/src/concurrency/select.hfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -2,62 +2,128 @@
 
 #include "containers/list.hfa"
-#include <stdint.h>
-#include <kernel.hfa>
-#include <locks.hfa>
+#include "stdint.h"
+#include "kernel.hfa"
 
+struct select_node;
+
+// node status
+static const unsigned long int __SELECT_UNSAT = 0;
+static const unsigned long int __SELECT_SAT = 1;
+static const unsigned long int __SELECT_RUN = 2;
+
+static inline bool __CFA_has_clause_run( unsigned long int status ) { return status == __SELECT_RUN; }
+static inline void __CFA_maybe_park( int * park_counter ) {
+    if ( __atomic_sub_fetch( park_counter, 1, __ATOMIC_SEQ_CST) < 0 )
+        park();
+}
+
+// node used for coordinating waituntil synchronization
 struct select_node {
+    int * park_counter;                 // If this is 0p then the node is in a special OR case waituntil
+    unsigned long int * clause_status;  // needs to point at ptr sized location, if this is 0p then node is not part of a waituntil
+
+    void * extra;                       // used to store arbitrary data needed by some primitives
+
     thread$ * blocked_thread;
-    void ** race_flag;
     inline dlink(select_node);
 };
 P9_EMBEDDED( select_node, dlink(select_node) )
 
-void ?{}( select_node & this ) {
-    this.blocked_thread = 0p;
-    this.race_flag = 0p;
+static inline void ?{}( select_node & this ) {
+    this.blocked_thread = active_thread();
+    this.clause_status = 0p;
+    this.park_counter = 0p;
+    this.extra = 0p;
 }
 
-void ?{}( select_node & this, thread$ * blocked_thread ) {
+static inline void ?{}( select_node & this, thread$ * blocked_thread ) {
     this.blocked_thread = blocked_thread;
-    this.race_flag = 0p;
+    this.clause_status = 0p;
+    this.park_counter = 0p;
+    this.extra = 0p;
 }
 
-void ?{}( select_node & this, thread$ * blocked_thread, void ** race_flag ) {
+static inline void ?{}( select_node & this, thread$ * blocked_thread, void * extra ) {
     this.blocked_thread = blocked_thread;
-    this.race_flag = race_flag;
+    this.clause_status = 0p;
+    this.park_counter = 0p;
+    this.extra = extra;
 }
 
-void ^?{}( select_node & this ) {}
+static inline void ^?{}( select_node & this ) {}
 
+static inline unsigned long int * __get_clause_status( select_node & s ) { return s.clause_status; }
 
 //-----------------------------------------------------------------------------
 // is_selectable
-trait is_selectable(T & | sized(T)) {
-    // For registering a select on a selectable concurrency primitive
-    // return 0p if primitive not accessible yet
-    // return 1p if primitive gets acquired
-    // return 2p if primitive is accessible but some other primitive won the race
-    // C_TODO: add enum for return values
-    void * register_select( T &, select_node & );
+forall(T & | sized(T))
+trait is_selectable {
+    // For registering a select stmt on a selectable concurrency primitive
+    // Returns bool that indicates if operation is already SAT
+    bool register_select( T &, select_node & );
 
-    void unregister_select( T &, select_node &  );
+    // For unregistering a select stmt on a selectable concurrency primitive
+    // If true is returned then the corresponding code block is run (only in non-special OR case and only if node status is not RUN)
+    bool unregister_select( T &, select_node &  );
+
+    // This routine is run on the selecting thread prior to executing the statement corresponding to the select_node
+    //    passed as an arg to this routine
+    // If on_selected returns false, the statement is not run, if it returns true it is run.
+    bool on_selected( T &, select_node & );
 };
 
-static inline bool install_select_winner( select_node & this, void * primitive_ptr ) with(this) {
-    // temporary needed for atomic instruction
-    void * cmp_flag = 0p;
-    
-    // if we dont win the selector race we need to potentially 
-    //   ignore this node and move to the next one so we return accordingly
-    if ( *race_flag != 0p || 
-        !__atomic_compare_exchange_n(
-            race_flag, 
-            &cmp_flag, 
-            primitive_ptr, 
-            false,
-            __ATOMIC_SEQ_CST,
-            __ATOMIC_SEQ_CST
-        )
-    ) return false; // lost race and some other node triggered select
-    return true; // won race so this node is what the select proceeds with
+// this is used inside the compiler to attempt to establish an else clause as a winner in the OR special case race
+static inline bool __select_node_else_race( select_node & this ) with( this ) {
+    unsigned long int cmp_status = __SELECT_UNSAT;
+    return *clause_status == 0 
+            && __atomic_compare_exchange_n( clause_status, &cmp_status, 1, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST );
 }
+
+// when a primitive becomes available it calls the following routine on it's node to update the select state:
+// return true if we want to unpark the thd
+static inline bool __make_select_node_available( select_node & this ) with( this ) {
+    unsigned long int cmp_status = __SELECT_UNSAT;
+
+    if( !park_counter ) 
+        return *clause_status == 0 
+            && __atomic_compare_exchange_n( clause_status, &cmp_status, (unsigned long int)&this, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ); // OR specific case where race was won
+
+    return *clause_status == 0
+        && __atomic_compare_exchange_n( clause_status, &cmp_status, __SELECT_SAT, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) // can maybe just use atomic write
+        && !__atomic_add_fetch( park_counter, 1, __ATOMIC_SEQ_CST);
+}
+
+// Handles the special OR case of the waituntil statement
+// Since only one select node can win in the OR case, we need to race to set the node available BEFORE
+//    performing the operation since if we lose the race the operation should not be performed as it will be lost
+// Returns true if execution can continue normally and false if the queue has now been drained
+static inline bool __handle_waituntil_OR( dlist( select_node ) & queue ) {
+    if ( queue`isEmpty ) return false;
+    if ( queue`first.clause_status && !queue`first.park_counter ) {
+        while ( !queue`isEmpty ) {
+            // if node not a special OR case or if we win the special OR case race break
+            if ( !queue`first.clause_status || queue`first.park_counter || __make_select_node_available( queue`first ) ) { return true; }
+            // otherwise we lost the special OR race so discard node
+            try_pop_front( queue );
+        }
+        return false;
+    }
+    return true;
+}
+
+// wake one thread from the list
+static inline void wake_one( dlist( select_node ) & queue, select_node & popped ) {
+    if ( !popped.clause_status                              // normal case, node is not a select node
+        || ( popped.clause_status && !popped.park_counter ) // If popped link is special case OR selecting unpark but don't call __make_select_node_available
+        || __make_select_node_available( popped ) )         // check if popped link belongs to a selecting thread
+        unpark( popped.blocked_thread );
+}
+
+static inline void wake_one( dlist( select_node ) & queue ) { wake_one( queue, try_pop_front( queue ) ); }
+
+static inline void setup_clause( select_node & this, unsigned long int * clause_status, int * park_counter ) {
+    this.blocked_thread = active_thread();
+    this.clause_status = clause_status;
+    this.park_counter = park_counter;
+}
+
Index: libcfa/src/concurrency/thread.cfa
===================================================================
--- libcfa/src/concurrency/thread.cfa	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ libcfa/src/concurrency/thread.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -53,4 +53,5 @@
 	preferred = ready_queue_new_preferred();
 	last_proc = 0p;
+    link_node = 0p;
 	PRNG_SET_SEED( random_state, __global_random_mask ? __global_random_prime : __global_random_prime ^ rdtscl() );
 	#if defined( __CFA_WITH_VERIFY__ )
@@ -59,7 +60,4 @@
 	#endif
 
-	clh_node = malloc( );
-	*clh_node = false;
-
 	doregister(curr_cluster, this);
 	monitors{ &self_mon_p, 1, (fptr_t)0 };
@@ -70,5 +68,4 @@
 		canary = 0xDEADDEADDEADDEADp;
 	#endif
-	free(clh_node);
 	unregister(curr_cluster, this);
 	^self_cor{};
Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/AST/Convert.cpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -567,4 +567,10 @@
 	}
 
+    const ast::WhenClause * visit( const ast::WhenClause * node ) override final {
+		// There is no old-AST WhenClause, so this should never be called.
+		assert( !node );
+		return nullptr;
+	}
+
 	const ast::Stmt * visit( const ast::WaitForStmt * node ) override final {
 		if ( inCache( node ) ) return nullptr;
@@ -573,9 +579,9 @@
 		for ( auto clause : node->clauses ) {
 			stmt->clauses.push_back({{
-					get<Expression>().accept1( clause->target_func ),
+					get<Expression>().accept1( clause->target ),
 					get<Expression>().acceptL( clause->target_args ),
 				},
 				get<Statement>().accept1( clause->stmt ),
-				get<Expression>().accept1( clause->cond ),
+				get<Expression>().accept1( clause->when_cond ),
 			});
 		}
@@ -594,4 +600,10 @@
 	const ast::WaitForClause * visit( const ast::WaitForClause * node ) override final {
 		// There is no old-AST WaitForClause, so this should never be called.
+		assert( !node );
+		return nullptr;
+	}
+
+    const ast::Stmt * visit( const ast::WaitUntilStmt * node ) override final {
+        // There is no old-AST WaitUntilStmt, so this should never be called.
 		assert( !node );
 		return nullptr;
@@ -2158,8 +2170,8 @@
 			auto clause = new ast::WaitForClause( old->location );
 
-			clause->target_func = GET_ACCEPT_1(clauses[i].target.function, Expr);
+			clause->target = GET_ACCEPT_1(clauses[i].target.function, Expr);
 			clause->target_args = GET_ACCEPT_V(clauses[i].target.arguments, Expr);
 			clause->stmt = GET_ACCEPT_1(clauses[i].statement, Stmt);
-			clause->cond = GET_ACCEPT_1(clauses[i].condition, Expr);
+			clause->when_cond = GET_ACCEPT_1(clauses[i].condition, Expr);
 
 			stmt->clauses.push_back( clause );
Index: src/AST/Fwd.hpp
===================================================================
--- src/AST/Fwd.hpp	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/AST/Fwd.hpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -58,6 +58,8 @@
 class FinallyClause;
 class SuspendStmt;
+class WhenClause;
 class WaitForStmt;
 class WaitForClause;
+class WaitUntilStmt;
 class WithStmt;
 class DeclStmt;
Index: src/AST/Node.cpp
===================================================================
--- src/AST/Node.cpp	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/AST/Node.cpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -174,8 +174,12 @@
 template class ast::ptr_base< ast::FinallyClause, ast::Node::ref_type::weak >;
 template class ast::ptr_base< ast::FinallyClause, ast::Node::ref_type::strong >;
+template class ast::ptr_base< ast::WhenClause, ast::Node::ref_type::weak >;
+template class ast::ptr_base< ast::WhenClause, ast::Node::ref_type::strong >;
 template class ast::ptr_base< ast::WaitForStmt, ast::Node::ref_type::weak >;
 template class ast::ptr_base< ast::WaitForStmt, ast::Node::ref_type::strong >;
 template class ast::ptr_base< ast::WaitForClause, ast::Node::ref_type::weak >;
 template class ast::ptr_base< ast::WaitForClause, ast::Node::ref_type::strong >;
+template class ast::ptr_base< ast::WaitUntilStmt, ast::Node::ref_type::weak >;
+template class ast::ptr_base< ast::WaitUntilStmt, ast::Node::ref_type::strong >;
 template class ast::ptr_base< ast::WithStmt, ast::Node::ref_type::weak >;
 template class ast::ptr_base< ast::WithStmt, ast::Node::ref_type::strong >;
Index: src/AST/Pass.hpp
===================================================================
--- src/AST/Pass.hpp	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/AST/Pass.hpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -162,6 +162,8 @@
 	const ast::FinallyClause *    visit( const ast::FinallyClause        * ) override final;
 	const ast::Stmt *             visit( const ast::SuspendStmt          * ) override final;
+    const ast::WhenClause *       visit( const ast::WhenClause           * ) override final;
 	const ast::Stmt *             visit( const ast::WaitForStmt          * ) override final;
 	const ast::WaitForClause *    visit( const ast::WaitForClause        * ) override final;
+    const ast::Stmt *             visit( const ast::WaitUntilStmt        * ) override final;
 	const ast::Decl *             visit( const ast::WithStmt             * ) override final;
 	const ast::NullStmt *         visit( const ast::NullStmt             * ) override final;
Index: src/AST/Pass.impl.hpp
===================================================================
--- src/AST/Pass.impl.hpp	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/AST/Pass.impl.hpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -1066,4 +1066,19 @@
 
 //--------------------------------------------------------------------------
+// WhenClause
+template< typename core_t >
+const ast::WhenClause * ast::Pass< core_t >::visit( const ast::WhenClause * node ) {
+	VISIT_START( node );
+
+	if ( __visit_children() ) {
+		maybe_accept( node, &WhenClause::target );
+		maybe_accept( node, &WhenClause::stmt );
+		maybe_accept( node, &WhenClause::when_cond );
+	}
+
+	VISIT_END( WhenClause, node );
+}
+
+//--------------------------------------------------------------------------
 // WaitForStmt
 template< typename core_t >
@@ -1090,11 +1105,29 @@
 
 	if ( __visit_children() ) {
-		maybe_accept( node, &WaitForClause::target_func );
+		maybe_accept( node, &WaitForClause::target );
 		maybe_accept( node, &WaitForClause::target_args );
 		maybe_accept( node, &WaitForClause::stmt );
-		maybe_accept( node, &WaitForClause::cond );
+		maybe_accept( node, &WaitForClause::when_cond );
 	}
 
 	VISIT_END( WaitForClause, node );
+}
+
+//--------------------------------------------------------------------------
+// WaitUntilStmt
+template< typename core_t >
+const ast::Stmt * ast::Pass< core_t >::visit( const ast::WaitUntilStmt * node ) {
+	VISIT_START( node );
+
+	if ( __visit_children() ) {
+		maybe_accept( node, &WaitUntilStmt::clauses );
+		maybe_accept( node, &WaitUntilStmt::timeout_time );
+		maybe_accept( node, &WaitUntilStmt::timeout_stmt );
+		maybe_accept( node, &WaitUntilStmt::timeout_cond );
+		maybe_accept( node, &WaitUntilStmt::else_stmt );
+		maybe_accept( node, &WaitUntilStmt::else_cond );
+	}
+
+	VISIT_END( Stmt, node );
 }
 
Index: src/AST/Print.cpp
===================================================================
--- src/AST/Print.cpp	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/AST/Print.cpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -208,4 +208,31 @@
 	}
 
+    void print( const ast::WaitStmt * node ) {
+		if ( node->timeout_time ) {
+			os << indent-1 << "timeout of:" << endl;
+			node->timeout_time->accept( *this );
+
+			if ( node->timeout_stmt ) {
+				os << indent-1 << "... with statment:" << endl;
+				node->timeout_stmt->accept( *this );
+			}
+
+			if ( node->timeout_cond ) {
+				os << indent-1 << "... with condition:" << endl;
+				node->timeout_cond->accept( *this );
+			}
+		}
+
+		if ( node->else_stmt ) {
+			os << indent-1 << "else:" << endl;
+			node->else_stmt->accept( *this );
+
+			if ( node->else_cond ) {
+				os << indent-1 << "... with condition:" << endl;
+				node->else_cond->accept( *this );
+			}
+		}
+	}
+
 	void preprint( const ast::NamedTypeDecl * node ) {
 		if ( ! node->name.empty() ) {
@@ -756,4 +783,21 @@
 	}
 
+	virtual const ast::WhenClause * visit( const ast::WhenClause * node ) override final {
+		os << indent-1 << "target: ";
+		safe_print( node->target );
+
+		if ( node->stmt ) {
+			os << indent-1 << "... with statment:" << endl;
+			node->stmt->accept( *this );
+		}
+
+		if ( node->when_cond ) {
+			os << indent-1 << "... with when condition:" << endl;
+			node->when_cond->accept( *this );
+		}
+
+		return node;
+	}
+
 	virtual const ast::Stmt * visit( const ast::WaitForStmt * node ) override final {
 		os << "Waitfor Statement" << endl;
@@ -793,5 +837,5 @@
 	virtual const ast::WaitForClause * visit( const ast::WaitForClause * node ) override final {
 		os << indent-1 << "target function: ";
-		safe_print( node->target_func );
+		safe_print( node->target );
 
 		if ( !node->target_args.empty() ) {
@@ -807,9 +851,19 @@
 		}
 
-		if ( node->cond ) {
+		if ( node->when_cond ) {
 			os << indent-1 << "... with condition:" << endl;
-			node->cond->accept( *this );
-		}
-
+			node->when_cond->accept( *this );
+		}
+
+		return node;
+	}
+
+    virtual const ast::Stmt * visit( const ast::WaitUntilStmt * node ) override final {
+		os << "Waituntil Statement" << endl;
+		indent += 2;
+		for( const auto & clause : node->clauses ) {
+			clause->accept( *this );
+		}
+        print(node);    // calls print( const ast::WaitStmt * node )
 		return node;
 	}
Index: src/AST/Stmt.hpp
===================================================================
--- src/AST/Stmt.hpp	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/AST/Stmt.hpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -378,9 +378,9 @@
 };
 
-// Waitfor statement: when (...) waitfor (... , ...) ... timeout(...) ... else ...
-class WaitForStmt final : public Stmt {
-  public:
-	std::vector<ptr<WaitForClause>> clauses;
-	ptr<Expr> timeout_time;
+// Base class of WaitFor/WaitUntil statements
+// form: KEYWORD(...) ... timeout(...) ... else ...
+class WaitStmt : public Stmt { 
+  public:
+    ptr<Expr> timeout_time;
 	ptr<Stmt> timeout_stmt;
 	ptr<Expr> timeout_cond;
@@ -388,6 +388,36 @@
 	ptr<Expr> else_cond;
 
+    WaitStmt( const CodeLocation & loc, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)) {}
+
+  private:
+    WaitStmt * clone() const override = 0;
+	MUTATE_FRIEND
+};
+
+// Base class for WaitFor/WaitUntil clauses
+// form: when( when_cond ) KEYWORD( target ) stmt
+class WhenClause : public StmtClause {
+  public:
+	ptr<Expr> target;
+	ptr<Stmt> stmt;
+	ptr<Expr> when_cond;
+
+	WhenClause( const CodeLocation & loc )
+		: StmtClause( loc ) {}
+
+	const WhenClause * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
+	WhenClause * clone() const override { return new WhenClause{ *this }; }
+	MUTATE_FRIEND
+};
+
+// Waitfor statement: when (...) waitfor (... , ...) ... timeout(...) ... else ...
+class WaitForStmt final : public WaitStmt {
+  public:
+	std::vector<ptr<WaitForClause>> clauses;
+
 	WaitForStmt( const CodeLocation & loc, const std::vector<Label> && labels = {} )
-		: Stmt(loc, std::move(labels)) {}
+		: WaitStmt(loc, std::move(labels)) {}
 
 	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
@@ -398,17 +428,60 @@
 
 // Clause in a waitfor statement: waitfor (..., ...) ...
-class WaitForClause final : public StmtClause {
-  public:
-	ptr<Expr> target_func;
+class WaitForClause final : public WhenClause {
+  public:
 	std::vector<ptr<Expr>> target_args;
-	ptr<Stmt> stmt;
-	ptr<Expr> cond;
 
 	WaitForClause( const CodeLocation & loc )
-		: StmtClause( loc ) {}
+		: WhenClause( loc ) {}
 
 	const WaitForClause * accept( Visitor & v ) const override { return v.visit( this ); }
   private:
 	WaitForClause * clone() const override { return new WaitForClause{ *this }; }
+	MUTATE_FRIEND
+};
+
+// waituntil statement: when (...) waituntil (...) ... timeout(...) ... else ...
+class WaitUntilStmt final : public WaitStmt {
+  public:
+    // Non-ast node used during compilation to store data needed to generate predicates
+    //    and set initial status values for clauses
+    // Used to create a tree corresponding to the structure of the clauses in a WaitUntil
+    struct ClauseNode { 
+        enum Op { AND, OR, LEFT_OR, LEAF, ELSE, TIMEOUT } op; // operation/type tag
+        // LEFT_OR used with TIMEOUT/ELSE to indicate that we ignore right hand side after parsing
+
+        ClauseNode * left;
+        ClauseNode * right;
+        WhenClause * leaf;  // only set if this node is a leaf (points into vector of clauses)
+
+        bool ambiguousWhen; // used to paint nodes of predicate tree based on when() clauses
+        bool whenState;     // used to track if when_cond is toggled on or off for generating init values
+        bool childOfAnd;      // true on leaf nodes that are children of AND, false otherwise
+
+        ClauseNode( Op op, ClauseNode * left, ClauseNode * right )
+            : op(op), left(left), right(right), leaf(nullptr), 
+            ambiguousWhen(false), whenState(true), childOfAnd(false) {}
+        ClauseNode( Op op, WhenClause * leaf )
+            : op(op), left(nullptr), right(nullptr), leaf(leaf),
+            ambiguousWhen(false), whenState(true), childOfAnd(false) {}
+        ClauseNode( WhenClause * leaf ) : ClauseNode(LEAF, leaf) {}
+        
+        ~ClauseNode() {
+            if ( left ) delete left;
+            if ( right ) delete right;
+        }
+    };
+
+	std::vector<ptr<WhenClause>> clauses;
+    ClauseNode * predicateTree;
+
+	WaitUntilStmt( const CodeLocation & loc, const std::vector<Label> && labels = {} )
+		: WaitStmt(loc, std::move(labels)) {}
+
+    ~WaitUntilStmt() { delete predicateTree; }
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
+	WaitUntilStmt * clone() const override { return new WaitUntilStmt{ *this }; }
 	MUTATE_FRIEND
 };
Index: src/AST/Visitor.hpp
===================================================================
--- src/AST/Visitor.hpp	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/AST/Visitor.hpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -50,6 +50,8 @@
     virtual const ast::FinallyClause *    visit( const ast::FinallyClause        * ) = 0;
     virtual const ast::Stmt *             visit( const ast::SuspendStmt          * ) = 0;
+    virtual const ast::WhenClause *       visit( const ast::WhenClause           * ) = 0;
     virtual const ast::Stmt *             visit( const ast::WaitForStmt          * ) = 0;
     virtual const ast::WaitForClause *    visit( const ast::WaitForClause        * ) = 0;
+    virtual const ast::Stmt *             visit( const ast::WaitUntilStmt        * ) = 0;
     virtual const ast::Decl *             visit( const ast::WithStmt             * ) = 0;
     virtual const ast::NullStmt *         visit( const ast::NullStmt             * ) = 0;
Index: src/Common/CodeLocationTools.cpp
===================================================================
--- src/Common/CodeLocationTools.cpp	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/Common/CodeLocationTools.cpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -128,6 +128,8 @@
     macro(FinallyClause, FinallyClause) \
     macro(SuspendStmt, Stmt) \
+    macro(WhenClause, WhenClause) \
     macro(WaitForStmt, Stmt) \
     macro(WaitForClause, WaitForClause) \
+    macro(WaitUntilStmt, Stmt) \
     macro(WithStmt, Decl) \
     macro(NullStmt, NullStmt) \
Index: src/Concurrency/WaitforNew.cpp
===================================================================
--- src/Concurrency/WaitforNew.cpp	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/Concurrency/WaitforNew.cpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -305,10 +305,10 @@
 
 	const ast::VariableExpr * variableExpr =
-		clause->target_func.as<ast::VariableExpr>();
+		clause->target.as<ast::VariableExpr>();
 	ast::Expr * castExpr = new ast::CastExpr(
 		location,
 		new ast::CastExpr(
 			location,
-			clause->target_func,
+			clause->target,
 			ast::deepCopy( variableExpr->result.get() ),
 			ast::GeneratedCast ),
@@ -325,7 +325,7 @@
 
 	ResolveContext context{ symtab, transUnit().global };
-	out->push_back( maybeCond( location, clause->cond.get(), {
+	out->push_back( maybeCond( location, clause->when_cond.get(), {
 		makeAccStmt( location, acceptables, index, "is_dtor",
-			detectIsDtor( location, clause->target_func ), context ),
+			detectIsDtor( location, clause->target ), context ),
 		makeAccStmt( location, acceptables, index, "func",
 			funcExpr, context ),
Index: src/Concurrency/Waituntil.cpp
===================================================================
--- src/Concurrency/Waituntil.cpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
+++ src/Concurrency/Waituntil.cpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -0,0 +1,1413 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// WaitforNew.cpp -- Expand waitfor clauses into code.
+//
+// Author           : Andrew Beach
+// Created On       : Fri May 27 10:31:00 2022
+// Last Modified By : Andrew Beach
+// Last Modified On : Tue Jun 13 13:30:00 2022
+// Update Count     : 0
+//
+
+#include <string>
+
+#include "Waituntil.hpp"
+#include "AST/Expr.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Print.hpp"
+#include "AST/Stmt.hpp"
+#include "AST/Type.hpp"
+#include "Common/UniqueName.h"
+
+using namespace ast;
+using namespace std;
+
+/* So this is what this pass dones:
+{
+    when ( condA ) waituntil( A ){ doA(); } 
+    or when ( condB ) waituntil( B ){ doB(); } 
+    and when ( condC ) waituntil( C ) { doC(); }
+}
+		 ||
+		 ||
+		\||/
+		 \/
+
+Generates these two routines:
+static inline bool is_full_sat_1( int * clause_statuses ) {
+    return clause_statuses[0] 
+        || clause_statuses[1]
+        && clause_statuses[2];
+}
+
+static inline bool is_done_sat_1( int * clause_statuses ) {
+    return has_run(clause_statuses[0])
+        || has_run(clause_statuses[1])
+        && has_run(clause_statuses[2]);
+}
+
+Replaces the waituntil statement above with the following code:
+{
+    // used with atomic_dec/inc to get binary semaphore behaviour
+    int park_counter = 0;
+
+    // status (one for each clause)
+    int clause_statuses[3] = { 0 };
+
+    bool whenA = condA;
+    bool whenB = condB;
+    bool whenC = condC;
+
+    if ( !whenB ) clause_statuses[1] = __SELECT_RUN;
+    if ( !whenC ) clause_statuses[2] = __SELECT_RUN;
+
+    // some other conditional settors for clause_statuses are set here, see genSubtreeAssign and related routines
+
+    // three blocks
+    // for each block, create, setup, then register select_node
+    select_node clause1;
+    select_node clause2;
+    select_node clause3;
+
+    try {
+        if ( whenA ) { register_select(A, clause1); setup_clause( clause1, &clause_statuses[0], &park_counter ); }
+        ... repeat ^ for B and C ... 
+
+        // if else clause is defined a separate branch can occur here to set initial values, see genWhenStateConditions
+
+        // loop & park until done
+        while( !is_full_sat_1( clause_statuses ) ) {
+            
+            // binary sem P();
+            if ( __atomic_sub_fetch( &park_counter, 1, __ATOMIC_SEQ_CST) < 0 )
+                park();
+            
+            // execute any blocks available with status set to 0
+            for ( int i = 0; i < 3; i++ ) {
+                if (clause_statuses[i] == __SELECT_SAT) {
+                    switch (i) {
+                        case 0:
+                            try {
+                                if (on_selected( A, clause1 ))
+                                    doA();
+                            }
+                            finally { clause_statuses[i] = __SELECT_RUN; unregister_select(A, clause1); }
+                            break;
+                        case 1:
+                            ... same gen as A but for B and clause2 ...
+                            break;
+                        case 2:
+                            ... same gen as A but for C and clause3 ...
+                            break;
+                    }
+                }
+            }
+        }
+
+        // ensure that the blocks that triggered is_full_sat_1 are run
+        // by running every un-run block that is SAT from the start until
+        // the predicate is SAT when considering RUN status = true
+        for ( int i = 0; i < 3; i++ ) {
+            if (is_done_sat_1( clause_statuses )) break;
+            if (clause_statuses[i] == __SELECT_SAT)
+                ... Same if body here as in loop above ...
+        }
+    } finally {
+        // the unregister and on_selected calls are needed to support primitives where the acquire has side effects
+        // so the corresponding block MUST be run for those primitives to not lose state (example is channels)
+        if ( ! has_run(clause_statuses[0]) && whenA && unregister_select(A, clause1) && on_selected( A, clause1 ) )
+            doA(); 
+        ... repeat if above for B and C ...
+    }
+}
+
+*/
+
+namespace Concurrency {
+
+class GenerateWaitUntilCore final {
+    vector<FunctionDecl *> & satFns;
+	UniqueName namer_sat = "__is_full_sat_"s;
+    UniqueName namer_run = "__is_run_sat_"s;
+	UniqueName namer_park = "__park_counter_"s;
+	UniqueName namer_status = "__clause_statuses_"s;
+	UniqueName namer_node = "__clause_"s;
+    UniqueName namer_target = "__clause_target_"s;
+    UniqueName namer_when = "__when_cond_"s;
+
+    string idxName = "__CFA_clause_idx_";
+
+    struct ClauseData {
+        string nodeName;
+        string targetName;
+        string whenName;
+        int index;
+        string & statusName;
+        ClauseData( int index, string & statusName ) : index(index), statusName(statusName) {}
+    };
+
+    const StructDecl * selectNodeDecl = nullptr;
+
+    // This first set of routines are all used to do the complicated job of 
+    //    dealing with how to set predicate statuses with certain when_conds T/F
+    //    so that the when_cond == F effectively makes that clause "disappear"
+    void updateAmbiguousWhen( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool andBelow, bool orBelow );
+    void paintWhenTree( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool & andBelow, bool & orBelow );
+    bool paintWhenTree( WaitUntilStmt::ClauseNode * currNode );
+    void collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs, int & index, bool parentAmbig, bool parentAnd );
+    void collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs );
+    void updateWhenState( WaitUntilStmt::ClauseNode * currNode );
+    void genSubtreeAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, bool status, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData );
+    void genStatusAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData );
+    CompoundStmt * getStatusAssignment( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData );
+    Stmt * genWhenStateConditions( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigClauses, vector<pair<int, WaitUntilStmt::ClauseNode *>>::size_type ambigIdx );
+
+    // These routines are just code-gen helpers
+    void addPredicates( const WaitUntilStmt * stmt, string & satName, string & runName );
+    void setUpClause( const WhenClause * clause, ClauseData * data, string & pCountName, CompoundStmt * body );
+    ForStmt * genStatusCheckFor( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, string & predName );
+    Expr * genSelectTraitCall( const WhenClause * clause, const ClauseData * data, string fnName );
+    CompoundStmt * genStmtBlock( const WhenClause * clause, const ClauseData * data );
+    Stmt * genElseClauseBranch( const WaitUntilStmt * stmt, string & runName, string & arrName, vector<ClauseData *> & clauseData );
+    Stmt * genNoElseClauseBranch( const WaitUntilStmt * stmt, string & satName, string & runName, string & arrName, string & pCountName, vector<ClauseData *> & clauseData );
+    void genClauseInits( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, CompoundStmt * body, string & statusName, string & elseWhenName );
+    Stmt * recursiveOrIfGen( const WaitUntilStmt * stmt, vector<ClauseData *> & data, vector<ClauseData*>::size_type idx, string & elseWhenName );
+    Stmt * buildOrCaseSwitch( const WaitUntilStmt * stmt, string & statusName, vector<ClauseData *> & data );
+    Stmt * genAllOr( const WaitUntilStmt * stmt );
+
+  public:
+    void previsit( const StructDecl * decl );
+	Stmt * postvisit( const WaitUntilStmt * stmt );
+    GenerateWaitUntilCore( vector<FunctionDecl *> & satFns ): satFns(satFns) {}
+};
+
+// Finds select_node decl
+void GenerateWaitUntilCore::previsit( const StructDecl * decl ) {
+    if ( !decl->body ) {
+		return;
+	} else if ( "select_node" == decl->name ) {
+		assert( !selectNodeDecl );
+		selectNodeDecl = decl;
+	}
+}
+
+void GenerateWaitUntilCore::updateAmbiguousWhen( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool andBelow, bool orBelow ) {
+    // all children when-ambiguous
+    if ( currNode->left->ambiguousWhen && currNode->right->ambiguousWhen )
+        // true iff an ancestor/descendant has a different operation
+        currNode->ambiguousWhen = (orAbove || orBelow) && (andBelow || andAbove);
+    // ambiguousWhen is initially false so theres no need to set it here
+}
+
+// Traverses ClauseNode tree and paints each AND/OR node as when-ambiguous true or false
+// This tree painting is needed to generate the if statements that set the initial state
+//    of the clause statuses when some clauses are turned off via when_cond
+// An internal AND/OR node is when-ambiguous if it satisfies all of the following:
+// - It has an ancestor or descendant that is a different operation, i.e. (AND has an OR ancestor or vice versa)
+// - All of its descendent clauses are optional, i.e. they have a when_cond defined on the WhenClause
+void GenerateWaitUntilCore::paintWhenTree( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool & andBelow, bool & orBelow ) {
+    bool aBelow = false; // updated by child nodes
+    bool oBelow = false; // updated by child nodes
+    switch (currNode->op) {
+        case WaitUntilStmt::ClauseNode::AND:
+            paintWhenTree( currNode->left, true, orAbove, aBelow, oBelow );
+            paintWhenTree( currNode->right, true, orAbove, aBelow, oBelow );
+
+            // update currNode's when flag based on conditions listed in fn signature comment above
+            updateAmbiguousWhen(currNode, true, orAbove, aBelow, oBelow );
+
+            // set return flags to tell parents which decendant ops have been seen
+            andBelow = true;
+            orBelow = oBelow;
+            return;
+        case WaitUntilStmt::ClauseNode::OR:
+            paintWhenTree( currNode->left, andAbove, true, aBelow, oBelow );
+            paintWhenTree( currNode->right, andAbove, true, aBelow, oBelow );
+
+            // update currNode's when flag based on conditions listed in fn signature comment above
+            updateAmbiguousWhen(currNode, andAbove, true, aBelow, oBelow );
+
+            // set return flags to tell parents which decendant ops have been seen
+            andBelow = aBelow;
+            orBelow = true;
+            return;
+        case WaitUntilStmt::ClauseNode::LEAF:
+            if ( currNode->leaf->when_cond )
+                currNode->ambiguousWhen = true;
+            return;
+        default:
+            assertf(false, "Unreachable waituntil clause node type. How did you get here???");
+    }
+}
+
+// overloaded wrapper for paintWhenTree that sets initial values
+// returns true if entire tree is OR's (special case)
+bool GenerateWaitUntilCore::paintWhenTree( WaitUntilStmt::ClauseNode * currNode ) {
+    bool aBelow = false, oBelow = false; // unused by initial call
+    paintWhenTree( currNode, false, false, aBelow, oBelow );
+    return !aBelow;
+}
+
+// Helper: returns Expr that represents arrName[index]
+Expr * genArrAccessExpr( const CodeLocation & loc, int index, string arrName ) {
+    return new UntypedExpr ( loc, 
+        new NameExpr( loc, "?[?]" ),
+        {
+            new NameExpr( loc, arrName ),
+            ConstantExpr::from_int( loc, index )
+        }
+    );
+}
+
+// After the ClauseNode AND/OR nodes are painted this routine is called to traverses the tree and does the following:
+// - collects a set of indices in the clause arr that refer whenclauses that can have ambiguous status assignments (ambigIdxs)
+// - collects a set of indices in the clause arr that refer whenclauses that have a when() defined and an AND node as a parent (andIdxs)
+// - updates LEAF nodes to be when-ambiguous if their direct parent is when-ambiguous.
+void GenerateWaitUntilCore::collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs, int & index, bool parentAmbig, bool parentAnd ) {
+    switch (currNode->op) {
+        case WaitUntilStmt::ClauseNode::AND:
+            collectWhens( currNode->left, ambigIdxs, andIdxs, index, currNode->ambiguousWhen, true );
+            collectWhens( currNode->right,  ambigIdxs, andIdxs, index, currNode->ambiguousWhen, true );
+            return;
+        case WaitUntilStmt::ClauseNode::OR:
+            collectWhens( currNode->left,  ambigIdxs, andIdxs, index, currNode->ambiguousWhen, false );
+            collectWhens( currNode->right,  ambigIdxs, andIdxs, index, currNode->ambiguousWhen, false );
+            return;
+        case WaitUntilStmt::ClauseNode::LEAF:
+            if ( parentAmbig ) {
+                ambigIdxs.push_back(make_pair(index, currNode));
+            }
+            if ( parentAnd && currNode->leaf->when_cond ) {
+                currNode->childOfAnd = true;
+                andIdxs.push_back(index);
+            }
+            index++;
+            return;
+        default:
+            assertf(false, "Unreachable waituntil clause node type. How did you get here???");
+    }
+}
+
+// overloaded wrapper for collectWhens that sets initial values
+void GenerateWaitUntilCore::collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs ) {
+    int idx = 0;
+    collectWhens( currNode, ambigIdxs, andIdxs, idx, false, false );
+}
+
+// recursively updates ClauseNode whenState on internal nodes so that next pass can see which 
+//    subtrees are "turned off"
+// sets whenState = false iff both children have whenState == false.
+// similar to paintWhenTree except since paintWhenTree also filtered out clauses we don't need to consider based on op
+// since the ambiguous clauses were filtered in paintWhenTree we don't need to worry about that here
+void GenerateWaitUntilCore::updateWhenState( WaitUntilStmt::ClauseNode * currNode ) {
+    if ( currNode->op == WaitUntilStmt::ClauseNode::LEAF ) return;
+    updateWhenState( currNode->left );
+    updateWhenState( currNode->right );
+    if ( !currNode->left->whenState && !currNode->right->whenState )
+        currNode->whenState = false;
+    else 
+        currNode->whenState = true;
+}
+
+// generates the minimal set of status assignments to ensure predicate subtree passed as currNode evaluates to status
+// assumes that this will only be called on subtrees that are entirely whenState == false
+void GenerateWaitUntilCore::genSubtreeAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, bool status, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData ) {
+    if ( ( currNode->op == WaitUntilStmt::ClauseNode::AND && status )
+        || ( currNode->op == WaitUntilStmt::ClauseNode::OR && !status ) ) {
+        // need to recurse on both subtrees if && subtree needs to be true or || subtree needs to be false
+        genSubtreeAssign( stmt, currNode->left, status, idx, retStmt, clauseData );
+        genSubtreeAssign( stmt, currNode->right, status, idx, retStmt, clauseData );
+    } else if ( ( currNode->op == WaitUntilStmt::ClauseNode::OR && status )
+        || ( currNode->op == WaitUntilStmt::ClauseNode::AND && !status ) ) {
+        // only one subtree needs to evaluate to status if && subtree needs to be true or || subtree needs to be false
+        CompoundStmt * leftStmt = new CompoundStmt( stmt->location );
+        CompoundStmt * rightStmt = new CompoundStmt( stmt->location );
+
+        // only one side needs to evaluate to status so we recurse on both subtrees
+        //    but only keep the statements from the subtree with minimal statements
+        genSubtreeAssign( stmt, currNode->left, status, idx, leftStmt, clauseData );
+        genSubtreeAssign( stmt, currNode->right, status, idx, rightStmt, clauseData );
+        
+        // append minimal statements to retStmt
+        if ( leftStmt->kids.size() < rightStmt->kids.size() ) {
+            retStmt->kids.splice( retStmt->kids.end(), leftStmt->kids );
+        } else {
+            retStmt->kids.splice( retStmt->kids.end(), rightStmt->kids );
+        }
+        
+        delete leftStmt;
+        delete rightStmt;
+    } else if ( currNode->op == WaitUntilStmt::ClauseNode::LEAF ) {
+        const CodeLocation & loc = stmt->location;
+        if ( status && !currNode->childOfAnd ) {
+            retStmt->push_back(
+                new ExprStmt( loc, 
+                    UntypedExpr::createAssign( loc,
+                        genArrAccessExpr( loc, idx, clauseData.at(idx)->statusName ),
+                        new NameExpr( loc, "__SELECT_RUN" )
+                    )
+                )
+            );
+        } else if ( !status && currNode->childOfAnd ) {
+            retStmt->push_back(
+                new ExprStmt( loc, 
+                    UntypedExpr::createAssign( loc,
+                        genArrAccessExpr( loc, idx, clauseData.at(idx)->statusName ),
+                        new NameExpr( loc, "__SELECT_UNSAT" )
+                    )
+                )
+            );
+        }
+
+        // No need to generate statements for the following cases since childOfAnd are always set to true
+        //    and !childOfAnd are always false
+        // - status && currNode->childOfAnd
+        // - !status && !currNode->childOfAnd
+        idx++;
+    }
+}
+
+void GenerateWaitUntilCore::genStatusAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData ) {
+    switch (currNode->op) {
+        case WaitUntilStmt::ClauseNode::AND:
+            // check which subtrees have all whenState == false (disabled)
+            if (!currNode->left->whenState && !currNode->right->whenState) {
+                // this case can only occur when whole tree is disabled since otherwise 
+                //    genStatusAssign( ... ) isn't called on nodes with whenState == false
+                assert( !currNode->whenState ); // paranoidWWW
+                // whole tree disabled so pass true so that select is SAT vacuously
+                genSubtreeAssign( stmt, currNode, true, idx, retStmt, clauseData );
+            } else if ( !currNode->left->whenState ) {
+                // pass true since x && true === x
+                genSubtreeAssign( stmt, currNode->left, true, idx, retStmt, clauseData );
+                genStatusAssign( stmt, currNode->right, idx, retStmt, clauseData );
+            } else if ( !currNode->right->whenState ) {
+                genStatusAssign( stmt, currNode->left, idx, retStmt, clauseData );
+                genSubtreeAssign( stmt, currNode->right, true, idx, retStmt, clauseData );
+            } else { 
+                // if no children with whenState == false recurse normally via break
+                break;
+            }
+            return;
+        case WaitUntilStmt::ClauseNode::OR:
+            if (!currNode->left->whenState && !currNode->right->whenState) {
+                assert( !currNode->whenState ); // paranoid
+                genSubtreeAssign( stmt, currNode, true, idx, retStmt, clauseData );
+            } else if ( !currNode->left->whenState ) {
+                // pass false since x || false === x
+                genSubtreeAssign( stmt, currNode->left, false, idx, retStmt, clauseData );
+                genStatusAssign( stmt, currNode->right, idx, retStmt, clauseData );
+            } else if ( !currNode->right->whenState ) {
+                genStatusAssign( stmt, currNode->left, idx, retStmt, clauseData );
+                genSubtreeAssign( stmt, currNode->right, false, idx, retStmt, clauseData );
+            } else { 
+                break;
+            }
+            return;
+        case WaitUntilStmt::ClauseNode::LEAF:
+            idx++;
+            return;
+        default:
+            assertf(false, "Unreachable waituntil clause node type. How did you get here???");
+    }
+    genStatusAssign( stmt, currNode->left, idx, retStmt, clauseData );
+    genStatusAssign( stmt, currNode->right, idx, retStmt, clauseData );
+}
+
+// generates a minimal set of assignments for status arr based on which whens are toggled on/off
+CompoundStmt * GenerateWaitUntilCore::getStatusAssignment( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData ) {
+    updateWhenState( stmt->predicateTree );
+    CompoundStmt * retval = new CompoundStmt( stmt->location );
+    int idx = 0;
+    genStatusAssign( stmt, stmt->predicateTree, idx, retval, clauseData );
+    return retval;
+}
+
+// generates nested if/elses for all possible assignments of ambiguous when_conds
+// exponential size of code gen but linear runtime O(n), where n is number of ambiguous whens()
+Stmt * GenerateWaitUntilCore::genWhenStateConditions( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, 
+    vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigClauses, vector<pair<int, WaitUntilStmt::ClauseNode *>>::size_type ambigIdx ) {
+    // I hate C++ sometimes, using vector<pair<int, WaitUntilStmt::ClauseNode *>>::size_type for size() comparison seems silly.
+    //    Why is size_type parameterized on the type stored in the vector?????
+
+    const CodeLocation & loc = stmt->location;
+    int clauseIdx = ambigClauses.at(ambigIdx).first;
+    WaitUntilStmt::ClauseNode * currNode = ambigClauses.at(ambigIdx).second;
+    Stmt * thenStmt;
+    Stmt * elseStmt;
+    
+    if ( ambigIdx == ambigClauses.size() - 1 ) { // base case
+        currNode->whenState = true;
+        thenStmt = getStatusAssignment( stmt, clauseData );
+        currNode->whenState = false;
+        elseStmt = getStatusAssignment( stmt, clauseData );
+    } else {
+        // recurse both with when enabled and disabled to generate all possible cases
+        currNode->whenState = true;
+        thenStmt = genWhenStateConditions( stmt, clauseData, ambigClauses, ambigIdx + 1 );
+        currNode->whenState = false;
+        elseStmt = genWhenStateConditions( stmt, clauseData, ambigClauses, ambigIdx + 1 );
+    }
+
+    // insert first recursion result in if ( __when_cond_ ) { ... }
+    // insert second recursion result in else { ... }
+    return new CompoundStmt ( loc,
+        {
+            new IfStmt( loc,
+                new NameExpr( loc, clauseData.at(clauseIdx)->whenName ),
+                thenStmt,
+                elseStmt
+            )
+        }
+    );
+}
+
+// typedef a fn ptr so that we can reuse genPredExpr
+// genLeafExpr is used to refer to one of the following two routines
+typedef Expr * (*GenLeafExpr)( const CodeLocation & loc, int & index );
+
+// return Expr that represents clause_statuses[index]
+// mutates index to be index + 1
+Expr * genSatExpr( const CodeLocation & loc, int & index ) {
+    return genArrAccessExpr( loc, index++, "clause_statuses" );
+}
+
+// return Expr that represents has_run(clause_statuses[index])
+Expr * genRunExpr( const CodeLocation & loc, int & index ) {
+    return new UntypedExpr ( loc, 
+        new NameExpr( loc, "__CFA_has_clause_run" ),
+        { genSatExpr( loc, index ) }
+    );
+}
+
+// Takes in the ClauseNode tree and recursively generates
+// the predicate expr used inside the predicate functions
+Expr * genPredExpr( const CodeLocation & loc, WaitUntilStmt::ClauseNode * currNode, int & idx, GenLeafExpr genLeaf ) {
+    switch (currNode->op) {
+        case WaitUntilStmt::ClauseNode::AND:
+            return new LogicalExpr( loc, 
+                new CastExpr( loc, genPredExpr( loc, currNode->left, idx, genLeaf ), new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast ),
+                new CastExpr( loc, genPredExpr( loc, currNode->right, idx, genLeaf ), new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast ), 
+                LogicalFlag::AndExpr 
+            );
+        case WaitUntilStmt::ClauseNode::OR:
+            return new LogicalExpr( loc,
+                new CastExpr( loc, genPredExpr( loc, currNode->left, idx, genLeaf ), new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast ),
+                new CastExpr( loc, genPredExpr( loc, currNode->right, idx, genLeaf ), new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast ), 
+                LogicalFlag::OrExpr );
+        case WaitUntilStmt::ClauseNode::LEAF:
+            return genLeaf( loc, idx );
+        default:
+            assertf(false, "Unreachable waituntil clause node type. How did you get here???");
+    }
+}
+
+
+// Builds the predicate functions used to check the status of the waituntil statement
+/* Ex:
+{
+    waituntil( A ){ doA(); } 
+    or waituntil( B ){ doB(); } 
+    and waituntil( C ) { doC(); }
+}
+generates =>
+static inline bool is_full_sat_1( int * clause_statuses ) {
+    return clause_statuses[0] 
+        || clause_statuses[1]
+        && clause_statuses[2];
+}
+
+static inline bool is_done_sat_1( int * clause_statuses ) {
+    return has_run(clause_statuses[0])
+        || has_run(clause_statuses[1])
+        && has_run(clause_statuses[2]);
+}
+*/
+// Returns a predicate function decl
+// predName and genLeaf determine if this generates an is_done or an is_full predicate
+FunctionDecl * buildPredicate( const WaitUntilStmt * stmt, GenLeafExpr genLeaf, string & predName ) {
+    int arrIdx = 0;
+    const CodeLocation & loc = stmt->location;
+    CompoundStmt * body = new CompoundStmt( loc );
+    body->push_back( new ReturnStmt( loc, genPredExpr( loc,  stmt->predicateTree, arrIdx, genLeaf ) ) );
+
+    return new FunctionDecl( loc,
+        predName,
+        {},                     // forall
+        {
+            new ObjectDecl( loc,
+                "clause_statuses",
+                new PointerType( new BasicType( BasicType::Kind::LongUnsignedInt ) )
+            )
+        },
+        { 
+            new ObjectDecl( loc,
+                "sat_ret",
+                new BasicType( BasicType::Kind::Bool )
+            )
+        },
+        body,               // body
+        { Storage::Static },    // storage
+        Linkage::Cforall,       // linkage
+        {},                     // attributes
+        { Function::Inline }
+    );
+}
+
+// Creates is_done and is_full predicates
+void GenerateWaitUntilCore::addPredicates( const WaitUntilStmt * stmt, string & satName, string & runName ) {
+    if ( !stmt->else_stmt || stmt->else_cond ) // don't need SAT predicate when else variation with no else_cond
+        satFns.push_back( Concurrency::buildPredicate( stmt, genSatExpr, satName ) ); 
+    satFns.push_back( Concurrency::buildPredicate( stmt, genRunExpr, runName ) );
+}
+
+// Adds the following to body:
+// if ( when_cond ) { // this if is omitted if no when() condition
+//      setup_clause( clause1, &clause_statuses[0], &park_counter );
+//      register_select(A, clause1);
+// }
+void GenerateWaitUntilCore::setUpClause( const WhenClause * clause, ClauseData * data, string & pCountName, CompoundStmt * body ) {    
+    CompoundStmt * currBody = body;
+    const CodeLocation & loc = clause->location;
+
+    // If we have a when_cond make the initialization conditional
+    if ( clause->when_cond )
+        currBody = new CompoundStmt( loc );
+
+    // Generates: setup_clause( clause1, &clause_statuses[0], &park_counter );
+    currBody->push_back( new ExprStmt( loc,
+        new UntypedExpr ( loc,
+            new NameExpr( loc, "setup_clause" ),
+            {
+                new NameExpr( loc, data->nodeName ),
+                new AddressExpr( loc, genArrAccessExpr( loc, data->index, data->statusName ) ),
+                new AddressExpr( loc, new NameExpr( loc, pCountName ) )
+            }
+        )
+    ));
+
+    // Generates: register_select(A, clause1);
+    currBody->push_back( new ExprStmt( loc, genSelectTraitCall( clause, data, "register_select" ) ) );
+
+    // generates: if ( when_cond ) { ... currBody ... }
+    if ( clause->when_cond )
+        body->push_back( 
+            new IfStmt( loc,
+                new NameExpr( loc, data->whenName ),
+                currBody
+            )
+        );
+}
+
+// Used to generate a call to one of the select trait routines
+Expr * GenerateWaitUntilCore::genSelectTraitCall( const WhenClause * clause, const ClauseData * data, string fnName ) {
+    const CodeLocation & loc = clause->location;
+    return new UntypedExpr ( loc,
+        new NameExpr( loc, fnName ),
+        {
+            new NameExpr( loc, data->targetName ),
+            new NameExpr( loc, data->nodeName )
+        }
+    );
+}
+
+// Generates:
+/* if ( on_selected( target_1, node_1 )) ... corresponding body of target_1 ... 
+*/
+CompoundStmt * GenerateWaitUntilCore::genStmtBlock( const WhenClause * clause, const ClauseData * data ) {
+    const CodeLocation & cLoc = clause->location;
+    return new CompoundStmt( cLoc,
+        {
+            new IfStmt( cLoc,
+                genSelectTraitCall( clause, data, "on_selected" ),
+                new CompoundStmt( cLoc,
+                    {
+                        ast::deepCopy( clause->stmt )
+                    }
+                )
+            )
+        }
+    );
+}
+
+// this routine generates and returns the following
+/*for ( int i = 0; i < numClauses; i++ ) {
+    if ( predName(clause_statuses) ) break;
+    if (clause_statuses[i] == __SELECT_SAT) {
+        switch (i) {
+            case 0:
+                try {
+                    if (on_selected( target1, clause1 ))
+                        dotarget1stmt();
+                }
+                finally { clause_statuses[i] = __SELECT_RUN; unregister_select(target1, clause1); }
+                break;
+            ...
+            case N:
+                ...
+                break;
+        }
+    }
+}*/
+ForStmt * GenerateWaitUntilCore::genStatusCheckFor( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, string & predName ) {
+    CompoundStmt * ifBody = new CompoundStmt( stmt->location );
+    const CodeLocation & loc = stmt->location;
+
+    /* generates:
+    switch (i) {
+        case 0:
+            try {
+                if (on_selected( target1, clause1 ))
+                    dotarget1stmt();
+            }
+            finally { clause_statuses[i] = __SELECT_RUN; unregister_select(target1, clause1); }
+            break;
+            ...
+        case N:
+            ...
+            break;
+    }*/
+    std::vector<ptr<CaseClause>> switchCases;
+    int idx = 0;
+    for ( const auto & clause: stmt->clauses ) {
+        const CodeLocation & cLoc = clause->location;
+        switchCases.push_back(
+            new CaseClause( cLoc,
+                ConstantExpr::from_int( cLoc, idx ),
+                {
+                    new CompoundStmt( cLoc,
+                        {
+                            new ast::TryStmt( cLoc,
+                                genStmtBlock( clause, clauseData.at(idx) ),
+                                {},
+                                new ast::FinallyClause( cLoc, 
+                                    new CompoundStmt( cLoc,
+                                        {
+                                            new ExprStmt( loc,
+                                                new UntypedExpr ( loc,
+                                                    new NameExpr( loc, "?=?" ),
+                                                    {
+                                                        new UntypedExpr ( loc, 
+                                                            new NameExpr( loc, "?[?]" ),
+                                                            {
+                                                                new NameExpr( loc, clauseData.at(0)->statusName ),
+                                                                new NameExpr( loc, idxName )
+                                                            }
+                                                        ),
+                                                        new NameExpr( loc, "__SELECT_RUN" )
+                                                    }
+                                                )
+                                            ),
+                                            new ExprStmt( loc, genSelectTraitCall( clause, clauseData.at(idx), "unregister_select" ) )
+                                        }
+                                    )
+                                )
+                            ),
+                            new BranchStmt( cLoc, BranchStmt::Kind::Break, Label( cLoc ) )
+                        }
+                    )
+                }
+            )
+        );
+        idx++;
+    }
+
+    ifBody->push_back(
+        new SwitchStmt( loc,
+            new NameExpr( loc, idxName ),
+            std::move( switchCases )
+        )
+    );
+
+    // gens:
+    // if (clause_statuses[i] == __SELECT_SAT) {
+    //      ... ifBody  ...
+    // }
+    IfStmt * ifSwitch = new IfStmt( loc,
+        new UntypedExpr ( loc,
+            new NameExpr( loc, "?==?" ),
+            {
+                new UntypedExpr ( loc, 
+                    new NameExpr( loc, "?[?]" ),
+                    {
+                        new NameExpr( loc, clauseData.at(0)->statusName ),
+                        new NameExpr( loc, idxName )
+                    }
+                ),
+                new NameExpr( loc, "__SELECT_SAT" )
+            }
+        ),      // condition
+        ifBody  // body
+    );
+
+    return new ForStmt( loc,
+        {
+            new DeclStmt( loc,
+                new ObjectDecl( loc,
+                    idxName,
+                    new BasicType( BasicType::Kind::SignedInt ),
+                    new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
+                )
+            )
+        },  // inits
+        new UntypedExpr ( loc,
+            new NameExpr( loc, "?<?" ),
+            {
+                new NameExpr( loc, idxName ),
+                ConstantExpr::from_int( loc, stmt->clauses.size() )
+            }
+        ),  // cond
+        new UntypedExpr ( loc,
+            new NameExpr( loc, "?++" ),
+            { new NameExpr( loc, idxName ) }
+        ),  // inc
+        new CompoundStmt( loc,
+            {
+                new IfStmt( loc,
+                    new UntypedExpr ( loc,
+                        new NameExpr( loc, predName ),
+                        { new NameExpr( loc, clauseData.at(0)->statusName ) }
+                    ),
+                    new BranchStmt( loc, BranchStmt::Kind::Break, Label( loc ) )
+                ),
+                ifSwitch
+            }
+        )   // body
+    );
+}
+
+// Generates: !is_full_sat_n() / !is_run_sat_n()
+Expr * genNotSatExpr( const WaitUntilStmt * stmt, string & satName, string & arrName ) {
+    const CodeLocation & loc = stmt->location;
+    return new UntypedExpr ( loc,
+        new NameExpr( loc, "!?" ),
+        {
+            new UntypedExpr ( loc,
+                new NameExpr( loc, satName ),
+                { new NameExpr( loc, arrName ) }
+            )
+        }
+    );
+}
+
+// Generates the code needed for waituntils with an else ( ... )
+// Checks clauses once after registering for completion and runs them if completes
+// If not enough have run to satisfy predicate after one pass then the else is run
+Stmt * GenerateWaitUntilCore::genElseClauseBranch( const WaitUntilStmt * stmt, string & runName, string & arrName, vector<ClauseData *> & clauseData ) {
+    return new CompoundStmt( stmt->else_stmt->location,
+        {
+            genStatusCheckFor( stmt, clauseData, runName ),
+            new IfStmt( stmt->else_stmt->location,
+                genNotSatExpr( stmt, runName, arrName ),
+                ast::deepCopy( stmt->else_stmt )
+            )
+        }
+    );
+}
+
+Stmt * GenerateWaitUntilCore::genNoElseClauseBranch( const WaitUntilStmt * stmt, string & satName, string & runName, string & arrName, string & pCountName, vector<ClauseData *> & clauseData ) {
+    CompoundStmt * whileBody = new CompoundStmt( stmt->location );
+    const CodeLocation & loc = stmt->location;
+
+    // generates: __CFA_maybe_park( &park_counter );
+    whileBody->push_back(
+        new ExprStmt( loc,
+            new UntypedExpr ( loc,
+                new NameExpr( loc, "__CFA_maybe_park" ),
+                { new AddressExpr( loc, new NameExpr( loc, pCountName ) ) }
+            )
+        )
+    );
+
+    whileBody->push_back( genStatusCheckFor( stmt, clauseData, satName ) );
+
+    return new CompoundStmt( loc,
+        {
+            new WhileDoStmt( loc,
+                genNotSatExpr( stmt, satName, arrName ),
+                whileBody,  // body
+                {}          // no inits
+            ),
+            genStatusCheckFor( stmt, clauseData, runName )
+        }
+    );
+}
+
+// generates the following decls for each clause to ensure the target expr and when_cond is only evaluated once
+// typeof(target) & __clause_target_0 = target;
+// bool __when_cond_0 = when_cond; // only generated if when_cond defined
+// select_node clause1;
+void GenerateWaitUntilCore::genClauseInits( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, CompoundStmt * body, string & statusName, string & elseWhenName ) {
+    ClauseData * currClause;
+    for ( vector<ClauseData*>::size_type i = 0; i < stmt->clauses.size(); i++ ) {
+        currClause = new ClauseData( i, statusName );
+        currClause->nodeName = namer_node.newName();
+        currClause->targetName = namer_target.newName();
+        currClause->whenName = namer_when.newName();
+        clauseData.push_back(currClause);
+        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
+
+        // typeof(target) & __clause_target_0 = target;
+        body->push_back(
+            new DeclStmt( cLoc,
+                new ObjectDecl( cLoc,
+                    currClause->targetName,
+                    new ReferenceType( new TypeofType( ast::deepCopy( stmt->clauses.at(i)->target ) ) ),
+                    new SingleInit( cLoc, ast::deepCopy( stmt->clauses.at(i)->target ) )
+                )
+            )
+        );
+
+        // bool __when_cond_0 = when_cond; // only generated if when_cond defined
+        if ( stmt->clauses.at(i)->when_cond )
+            body->push_back(
+                new DeclStmt( cLoc,
+                    new ObjectDecl( cLoc,
+                        currClause->whenName,
+                        new BasicType( BasicType::Kind::Bool ),
+                        new SingleInit( cLoc, ast::deepCopy( stmt->clauses.at(i)->when_cond ) )
+                    )
+                )
+            );
+        
+        // select_node clause1;
+        body->push_back(
+            new DeclStmt( cLoc,
+                new ObjectDecl( cLoc,
+                    currClause->nodeName,
+                    new StructInstType( selectNodeDecl )
+                )
+            )
+        );
+    }
+
+    if ( stmt->else_stmt && stmt->else_cond ) {
+        body->push_back(
+            new DeclStmt( stmt->else_cond->location,
+                new ObjectDecl( stmt->else_cond->location,
+                    elseWhenName,
+                    new BasicType( BasicType::Kind::Bool ),
+                    new SingleInit( stmt->else_cond->location, ast::deepCopy( stmt->else_cond ) )
+                )
+            )
+        );
+    }
+}
+
+/*
+if ( clause_status == &clause1 ) ... clause 1 body ...
+...
+elif ( clause_status == &clausen ) ... clause n body ...
+*/
+Stmt * GenerateWaitUntilCore::buildOrCaseSwitch( const WaitUntilStmt * stmt, string & statusName, vector<ClauseData *> & data ) {
+    const CodeLocation & loc = stmt->location;
+
+    IfStmt * outerIf = nullptr;
+	IfStmt * lastIf = nullptr;
+
+	//adds an if/elif clause for each select clause address to run the corresponding clause stmt
+	for ( long unsigned int i = 0; i < data.size(); i++ ) {
+        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
+
+		IfStmt * currIf = new IfStmt( cLoc,
+			new UntypedExpr( cLoc, 
+                new NameExpr( cLoc, "?==?" ), 
+                {
+                    new NameExpr( cLoc, statusName ),
+                    new CastExpr( cLoc, 
+                        new AddressExpr( cLoc, new NameExpr( cLoc, data.at(i)->nodeName ) ),
+                        new BasicType( BasicType::Kind::LongUnsignedInt ), GeneratedFlag::ExplicitCast 
+                    )
+                }
+            ),
+            genStmtBlock( stmt->clauses.at(i), data.at(i) )
+		);
+		
+		if ( i == 0 ) {
+			outerIf = currIf;
+		} else {
+			// add ifstmt to else of previous stmt
+			lastIf->else_ = currIf;
+		}
+
+		lastIf = currIf;
+	}
+
+    // C_TODO: will remove this commented code later. Currently it isn't needed but may switch to a modified version of this later if it has better performance
+    // std::vector<ptr<CaseClause>> switchCases;
+
+    // int idx = 0;
+    // for ( const auto & clause: stmt->clauses ) {
+    //     const CodeLocation & cLoc = clause->location;
+    //     switchCases.push_back(
+    //         new CaseClause( cLoc,
+    //             new CastExpr( cLoc, 
+    //                 new AddressExpr( cLoc, new NameExpr( cLoc, data.at(idx)->targetName ) ),
+    //                 new BasicType( BasicType::Kind::LongUnsignedInt ), GeneratedFlag::ExplicitCast 
+    //             ),
+    //             {
+    //                 new CompoundStmt( cLoc,
+    //                     {
+    //                         ast::deepCopy( clause->stmt ),
+    //                         new BranchStmt( cLoc, BranchStmt::Kind::Break, Label( cLoc ) )
+    //                     }
+    //                 )
+    //             }
+    //         )
+    //     );
+    //     idx++;
+    // }
+
+    return new CompoundStmt( loc,
+        {
+            new ExprStmt( loc, new UntypedExpr( loc, new NameExpr( loc, "park" ) ) ),
+            outerIf
+            // new SwitchStmt( loc,
+            //     new NameExpr( loc, statusName ),
+            //     std::move( switchCases )
+            // )
+        }
+    );
+}
+
+Stmt * GenerateWaitUntilCore::recursiveOrIfGen( const WaitUntilStmt * stmt, vector<ClauseData *> & data, vector<ClauseData*>::size_type idx, string & elseWhenName ) {
+    if ( idx == data.size() ) {   // base case, gen last else
+        const CodeLocation & cLoc = stmt->else_stmt->location;
+        if ( !stmt->else_stmt ) // normal non-else gen
+            return buildOrCaseSwitch( stmt, data.at(0)->statusName, data );
+
+        Expr * raceFnCall = new UntypedExpr( stmt->location,
+            new NameExpr( stmt->location, "__select_node_else_race" ),
+            { new NameExpr( stmt->location, data.at(0)->nodeName ) }
+        );
+
+        if ( stmt->else_stmt && stmt->else_cond ) { // return else conditional on both when and race
+            return new IfStmt( cLoc,
+                new LogicalExpr( cLoc,
+                    new CastExpr( cLoc,
+                        new NameExpr( cLoc, elseWhenName ),
+                        new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                    ),
+                    new CastExpr( cLoc,
+                        raceFnCall,
+                        new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                    ),
+                    LogicalFlag::AndExpr
+                ),
+                ast::deepCopy( stmt->else_stmt ),
+                buildOrCaseSwitch( stmt, data.at(0)->statusName, data )
+            );
+        }
+
+        // return else conditional on race
+        return new IfStmt( stmt->else_stmt->location,
+            raceFnCall,
+            ast::deepCopy( stmt->else_stmt ),
+            buildOrCaseSwitch( stmt, data.at(0)->statusName, data )
+        );
+    }
+    const CodeLocation & cLoc = stmt->clauses.at(idx)->location;
+
+    Expr * ifCond;
+
+    // If we have a when_cond make the register call conditional on it
+    if ( stmt->clauses.at(idx)->when_cond ) {
+        ifCond = new LogicalExpr( cLoc,
+            new CastExpr( cLoc,
+                new NameExpr( cLoc, data.at(idx)->whenName ), 
+                new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+            ),
+            new CastExpr( cLoc,
+                genSelectTraitCall( stmt->clauses.at(idx), data.at(idx), "register_select" ),
+                new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+            ),
+            LogicalFlag::AndExpr
+        );
+    } else ifCond = genSelectTraitCall( stmt->clauses.at(idx), data.at(idx), "register_select" );
+
+    return new CompoundStmt( cLoc,
+        {   // gens: setup_clause( clause1, &status, 0p );
+            new ExprStmt( cLoc,
+                new UntypedExpr ( cLoc,
+                    new NameExpr( cLoc, "setup_clause" ),
+                    {
+                        new NameExpr( cLoc, data.at(idx)->nodeName ),
+                        new AddressExpr( cLoc, new NameExpr( cLoc, data.at(idx)->statusName ) ),
+                        ConstantExpr::null( cLoc, new PointerType( new BasicType( BasicType::Kind::SignedInt ) ) )
+                    }
+                )
+            ),
+            // gens: if (__when_cond && register_select()) { clause body } else { ... recursiveOrIfGen ... }
+            new IfStmt( cLoc,
+                ifCond,
+                genStmtBlock( stmt->clauses.at(idx), data.at(idx) ),
+                // ast::deepCopy( stmt->clauses.at(idx)->stmt ),
+                recursiveOrIfGen( stmt, data, idx + 1, elseWhenName )
+            )
+        }
+    );
+}
+
+// This gens the special case of an all OR waituntil:
+/* 
+int status = 0;
+
+typeof(target) & __clause_target_0 = target;
+bool __when_cond_0 = when_cond; // only generated if when_cond defined
+select_node clause1;
+... generate above for rest of clauses ...
+
+try {
+    setup_clause( clause1, &status, 0p );
+    if ( __when_cond_0 && register_select( 1 ) ) {
+        ... clause 1 body ...
+    } else {
+        ... recursively gen for each of n clauses ...
+        setup_clause( clausen, &status, 0p );
+        if ( __when_cond_n-1 && register_select( n ) ) {
+            ... clause n body ...
+        } else {
+            if ( else_when ) ... else clause body ...
+            else {
+                park();
+
+                // after winning the race and before unpark() clause_status is set to be the winning clause index + 1 
+                if ( clause_status == &clause1) ... clause 1 body ...
+                ...
+                elif ( clause_status == &clausen ) ... clause n body ...
+            }
+        }
+    }
+}
+finally { 
+    if ( __when_cond_1 && clause1.status != 0p) unregister_select( 1 ); // if registered unregister
+    ...
+    if ( __when_cond_n && clausen.status != 0p) unregister_select( n );
+}
+*/
+Stmt * GenerateWaitUntilCore::genAllOr( const WaitUntilStmt * stmt ) {
+    const CodeLocation & loc = stmt->location;
+    string statusName = namer_status.newName();
+    string elseWhenName = namer_when.newName();
+    int numClauses = stmt->clauses.size();
+    CompoundStmt * body = new CompoundStmt( stmt->location );
+
+    // Generates: unsigned long int status = 0;
+    body->push_back( new DeclStmt( loc,
+        new ObjectDecl( loc,
+            statusName,
+            new BasicType( BasicType::Kind::LongUnsignedInt ),
+            new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
+        )
+    ));
+
+    vector<ClauseData *> clauseData;
+    genClauseInits( stmt, clauseData, body, statusName, elseWhenName );
+
+    vector<int> whenIndices; // track which clauses have whens
+
+    CompoundStmt * unregisters = new CompoundStmt( loc );
+    Expr * ifCond;
+    for ( int i = 0; i < numClauses; i++ ) {
+        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
+        // Gens: node.status != 0p
+        UntypedExpr * statusPtrCheck = new UntypedExpr( cLoc, 
+            new NameExpr( cLoc, "?!=?" ), 
+            {
+                ConstantExpr::null( cLoc, new PointerType( new BasicType( BasicType::Kind::LongUnsignedInt ) ) ),
+                new UntypedExpr( cLoc, 
+                    new NameExpr( cLoc, "__get_clause_status" ), 
+                    { new NameExpr( cLoc, clauseData.at(i)->nodeName ) } 
+                ) 
+            }
+        );
+
+        // If we have a when_cond make the unregister call conditional on it
+        if ( stmt->clauses.at(i)->when_cond ) {
+            whenIndices.push_back(i);
+            ifCond = new LogicalExpr( cLoc,
+                new CastExpr( cLoc,
+                    new NameExpr( cLoc, clauseData.at(i)->whenName ), 
+                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                ),
+                new CastExpr( cLoc,
+                    statusPtrCheck,
+                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                ),
+                LogicalFlag::AndExpr
+            );
+        } else ifCond = statusPtrCheck;
+        
+        unregisters->push_back(
+            new IfStmt( cLoc,
+                ifCond,
+                new ExprStmt( cLoc, genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "unregister_select" ) ) 
+            )
+        );
+    }
+
+    if ( whenIndices.empty() || whenIndices.size() != stmt->clauses.size() ) {
+        body->push_back(
+                new ast::TryStmt( loc,
+                new CompoundStmt( loc, { recursiveOrIfGen( stmt, clauseData, 0, elseWhenName ) } ),
+                {},
+                new ast::FinallyClause( loc, unregisters )
+            )
+        );
+    } else { // If all clauses have whens, we need to skip the waituntil if they are all false
+        Expr * outerIfCond = new NameExpr( loc, clauseData.at( whenIndices.at(0) )->whenName );
+        Expr * lastExpr = outerIfCond;
+
+        for ( vector<int>::size_type i = 1; i < whenIndices.size(); i++ ) {
+            outerIfCond = new LogicalExpr( loc,
+                new CastExpr( loc,
+                    new NameExpr( loc, clauseData.at( whenIndices.at(i) )->whenName ), 
+                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                ),
+                new CastExpr( loc,
+                    lastExpr,
+                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                ),
+                LogicalFlag::OrExpr
+            );
+            lastExpr = outerIfCond;
+        }
+
+        body->push_back(
+                new ast::TryStmt( loc,
+                new CompoundStmt( loc, 
+                    {
+                        new IfStmt( loc,
+                            outerIfCond,
+                            recursiveOrIfGen( stmt, clauseData, 0, elseWhenName )
+                        )
+                    }
+                ),
+                {},
+                new ast::FinallyClause( loc, unregisters )
+            )
+        );
+    }
+
+    for ( ClauseData * datum : clauseData )
+        delete datum;
+
+    return body;
+}
+
+Stmt * GenerateWaitUntilCore::postvisit( const WaitUntilStmt * stmt ) {
+    if ( !selectNodeDecl )
+        SemanticError( stmt, "waituntil statement requires #include <waituntil.hfa>" );
+
+    // Prep clause tree to figure out how to set initial statuses
+    // setTreeSizes( stmt->predicateTree );
+    if ( paintWhenTree( stmt->predicateTree ) ) // if this returns true we can special case since tree is all OR's
+        return genAllOr( stmt );
+
+    CompoundStmt * tryBody = new CompoundStmt( stmt->location );
+    CompoundStmt * body = new CompoundStmt( stmt->location );
+    string statusArrName = namer_status.newName();
+    string pCountName = namer_park.newName();
+    string satName = namer_sat.newName();
+    string runName = namer_run.newName();
+    string elseWhenName = namer_when.newName();
+    int numClauses = stmt->clauses.size();
+    addPredicates( stmt, satName, runName );
+
+    const CodeLocation & loc = stmt->location;
+
+    // Generates: int park_counter = 0;
+    body->push_back( new DeclStmt( loc,
+        new ObjectDecl( loc,
+            pCountName,
+            new BasicType( BasicType::Kind::SignedInt ),
+            new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
+        )
+    ));
+
+    // Generates: int clause_statuses[3] = { 0 };
+    body->push_back( new DeclStmt( loc,
+        new ObjectDecl( loc,
+            statusArrName,
+            new ArrayType( new BasicType( BasicType::Kind::LongUnsignedInt ), ConstantExpr::from_int( loc, numClauses ), LengthFlag::FixedLen, DimensionFlag::DynamicDim ),
+            new ListInit( loc,
+                {
+                    new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) )
+                }
+            )
+        )
+    ));
+
+    vector<ClauseData *> clauseData;
+    genClauseInits( stmt, clauseData, body, statusArrName, elseWhenName );
+
+    vector<pair<int, WaitUntilStmt::ClauseNode *>> ambiguousClauses;       // list of ambiguous clauses
+    vector<int> andWhenClauses;    // list of clauses that have an AND op as a direct parent and when_cond defined
+
+    collectWhens( stmt->predicateTree, ambiguousClauses, andWhenClauses );
+
+    // This is only needed for clauses that have AND as a parent and a when_cond defined
+    // generates: if ( ! when_cond_0 ) clause_statuses_0 = __SELECT_RUN;
+    for ( int idx : andWhenClauses ) {
+        const CodeLocation & cLoc = stmt->clauses.at(idx)->location;
+        body->push_back( 
+            new IfStmt( cLoc,
+                new UntypedExpr ( cLoc,
+                    new NameExpr( cLoc, "!?" ),
+                    { new NameExpr( cLoc, clauseData.at(idx)->whenName ) }
+                ),  // IfStmt cond
+                new ExprStmt( cLoc,
+                    new UntypedExpr ( cLoc,
+                        new NameExpr( cLoc, "?=?" ),
+                        {
+                            new UntypedExpr ( cLoc, 
+                                new NameExpr( cLoc, "?[?]" ),
+                                {
+                                    new NameExpr( cLoc, statusArrName ),
+                                    ConstantExpr::from_int( cLoc, idx )
+                                }
+                            ),
+                            new NameExpr( cLoc, "__SELECT_RUN" )
+                        }
+                    )
+                )  // IfStmt then
+            )
+        );
+    }
+
+    // Only need to generate conditional initial state setting for ambiguous when clauses
+    if ( !ambiguousClauses.empty() ) {
+        body->push_back( genWhenStateConditions( stmt, clauseData, ambiguousClauses, 0 ) );
+    }
+
+    // generates the following for each clause:
+    // setup_clause( clause1, &clause_statuses[0], &park_counter );
+    // register_select(A, clause1);
+    for ( int i = 0; i < numClauses; i++ ) {
+        setUpClause( stmt->clauses.at(i), clauseData.at(i), pCountName, tryBody );
+    }
+
+    // generate satisfy logic based on if there is an else clause and if it is conditional
+    if ( stmt->else_stmt && stmt->else_cond ) { // gen both else/non else branches
+        tryBody->push_back(
+            new IfStmt( stmt->else_cond->location,
+                new NameExpr( stmt->else_cond->location, elseWhenName ),
+                genElseClauseBranch( stmt, runName, statusArrName, clauseData ),
+                genNoElseClauseBranch( stmt, satName, runName, statusArrName, pCountName, clauseData )
+            )
+        );
+    } else if ( !stmt->else_stmt ) { // normal gen
+        tryBody->push_back( genNoElseClauseBranch( stmt, satName, runName, statusArrName, pCountName, clauseData ) );
+    } else { // generate just else
+        tryBody->push_back( genElseClauseBranch( stmt, runName, statusArrName, clauseData ) );
+    }
+
+    CompoundStmt * unregisters = new CompoundStmt( loc );
+    // generates for each clause: 
+    // if ( !has_run( clause_statuses[i] ) ) 
+    // OR if when_cond defined
+    // if ( when_cond_i && !has_run( clause_statuses[i] ) )
+    // body of if is:
+    // { if (unregister_select(A, clause1) && on_selected(A, clause1)) clause1->stmt; } // this conditionally runs the block unregister_select returns true (needed by some primitives)
+    Expr * ifCond;
+    UntypedExpr * statusExpr; // !clause_statuses[i]
+    for ( int i = 0; i < numClauses; i++ ) {
+        const CodeLocation & cLoc = stmt->clauses.at(i)->location;
+
+        statusExpr = new UntypedExpr ( cLoc,
+            new NameExpr( cLoc, "!?" ),
+            {
+                new UntypedExpr ( cLoc, 
+                    new NameExpr( cLoc, "__CFA_has_clause_run" ),
+                    {
+                        genArrAccessExpr( cLoc, i, statusArrName )
+                    }
+                )
+            }
+        );
+
+        if ( stmt->clauses.at(i)->when_cond ) {
+            // generates: if( when_cond_i && !has_run(clause_statuses[i]) )
+            ifCond = new LogicalExpr( cLoc,
+                new CastExpr( cLoc,
+                    new NameExpr( cLoc, clauseData.at(i)->whenName ), 
+                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                ),
+                new CastExpr( cLoc,
+                    statusExpr,
+                    new BasicType( BasicType::Kind::Bool ), GeneratedFlag::ExplicitCast 
+                ),
+                LogicalFlag::AndExpr
+            );
+        } else // generates: if( !clause_statuses[i] )
+            ifCond = statusExpr;
+        
+        unregisters->push_back( 
+            new IfStmt( cLoc,
+                ifCond,
+                new CompoundStmt( cLoc,
+                    {
+                        new IfStmt( cLoc,
+                            genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "unregister_select" ),
+                            // ast::deepCopy( stmt->clauses.at(i)->stmt )
+                            genStmtBlock( stmt->clauses.at(i), clauseData.at(i) )
+                        )
+                    }
+                )
+                
+            )
+        );
+    }
+
+    body->push_back( 
+        new ast::TryStmt(
+            loc,
+            tryBody,
+            {},
+            new ast::FinallyClause( loc, unregisters )
+        )
+    );
+
+    for ( ClauseData * datum : clauseData )
+        delete datum;
+
+    return body;
+}
+
+// To add the predicates at global scope we need to do it in a second pass
+// Predicates are added after "struct select_node { ... };"
+class AddPredicateDecls final : public WithDeclsToAdd<> {
+    vector<FunctionDecl *> & satFns;
+    const StructDecl * selectNodeDecl = nullptr;
+
+  public:
+    void previsit( const StructDecl * decl ) {
+        if ( !decl->body ) {
+            return;
+        } else if ( "select_node" == decl->name ) {
+            assert( !selectNodeDecl );
+            selectNodeDecl = decl;
+            for ( FunctionDecl * fn : satFns )
+                declsToAddAfter.push_back(fn);            
+        }
+    }
+    AddPredicateDecls( vector<FunctionDecl *> & satFns ): satFns(satFns) {}
+};
+
+void generateWaitUntil( TranslationUnit & translationUnit ) {
+    vector<FunctionDecl *> satFns;
+	Pass<GenerateWaitUntilCore>::run( translationUnit, satFns );
+    Pass<AddPredicateDecls>::run( translationUnit, satFns );
+}
+
+} // namespace Concurrency
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Concurrency/Waituntil.hpp
===================================================================
--- src/Concurrency/Waituntil.hpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
+++ src/Concurrency/Waituntil.hpp	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -0,0 +1,33 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Waitfor.h --
+//
+// Author           : Thierry Delisle
+// Created On       : Mon Aug 28 11:03:31 2017
+// Last Modified By :
+// Last Modified On :
+// Update Count     : 1
+//
+
+#pragma once
+
+#include <list>  // for list
+
+class Declaration;
+namespace ast {
+	class TranslationUnit;
+}
+
+namespace Concurrency {
+    void generateWaitUntil( ast::TranslationUnit & translationUnit );
+};
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Concurrency/module.mk
===================================================================
--- src/Concurrency/module.mk	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/Concurrency/module.mk	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -23,3 +23,5 @@
 	Concurrency/WaitforNew.cpp \
 	Concurrency/Waitfor.cc \
-	Concurrency/Waitfor.h 
+	Concurrency/Waitfor.h \
+	Concurrency/Waituntil.cpp \
+	Concurrency/Waituntil.hpp
Index: src/Parser/StatementNode.cc
===================================================================
--- src/Parser/StatementNode.cc	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/Parser/StatementNode.cc	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -328,7 +328,7 @@
 ast::WaitForStmt * build_waitfor( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
 	auto clause = new ast::WaitForClause( location );
-	clause->target_func = maybeBuild( targetExpr );
+	clause->target = maybeBuild( targetExpr );
 	clause->stmt = maybeMoveBuild( stmt );
-	clause->cond = notZeroExpr( maybeMoveBuild( when ) );
+	clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
 
 	ExpressionNode * next = dynamic_cast<ExpressionNode *>( targetExpr->get_next() );
@@ -359,4 +359,93 @@
 	return existing;
 } // build_waitfor_timeout
+
+ast::WaitUntilStmt::ClauseNode * build_waituntil_clause( const CodeLocation & loc, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
+    ast::WhenClause * clause = new ast::WhenClause( loc );
+    clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
+    clause->stmt = maybeMoveBuild( stmt );
+    clause->target = maybeMoveBuild( targetExpr );
+    return new ast::WaitUntilStmt::ClauseNode( clause );
+}
+ast::WaitUntilStmt::ClauseNode * build_waituntil_else( const CodeLocation & loc, ExpressionNode * when, StatementNode * stmt ) {
+    ast::WhenClause * clause = new ast::WhenClause( loc );
+    clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
+    clause->stmt = maybeMoveBuild( stmt );
+    return new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::ELSE, clause );
+}
+ast::WaitUntilStmt::ClauseNode * build_waituntil_timeout( const CodeLocation & loc, ExpressionNode * when, ExpressionNode * timeout, StatementNode * stmt ) {
+    ast::WhenClause * clause = new ast::WhenClause( loc );
+    clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
+    clause->stmt = maybeMoveBuild( stmt );
+    clause->target = maybeMoveBuild( timeout );
+    return new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::TIMEOUT, clause );
+}
+
+ast::WaitUntilStmt * build_waituntil_stmt( const CodeLocation & loc, ast::WaitUntilStmt::ClauseNode * root ) {
+    ast::WaitUntilStmt * retStmt = new ast::WaitUntilStmt( loc );
+    retStmt->predicateTree = root;
+    
+    // iterative tree traversal
+    std::vector<ast::WaitUntilStmt::ClauseNode *> nodeStack; // stack needed for iterative traversal
+    ast::WaitUntilStmt::ClauseNode * currNode = nullptr;
+    ast::WaitUntilStmt::ClauseNode * lastInternalNode = nullptr;
+    ast::WaitUntilStmt::ClauseNode * cleanup = nullptr; // used to cleanup removed else/timeout
+    nodeStack.push_back(root);
+
+    do {
+        currNode = nodeStack.back();
+        nodeStack.pop_back(); // remove node since it will be processed
+
+        switch (currNode->op) {
+            case ast::WaitUntilStmt::ClauseNode::LEAF:
+                retStmt->clauses.push_back(currNode->leaf);
+                break;
+            case ast::WaitUntilStmt::ClauseNode::ELSE:
+                retStmt->else_stmt = currNode->leaf->stmt 
+                    ? ast::deepCopy( currNode->leaf->stmt )
+                    : nullptr;
+                
+                retStmt->else_cond = currNode->leaf->when_cond
+                    ? ast::deepCopy( currNode->leaf->when_cond )
+                    : nullptr;
+
+                delete currNode->leaf;
+                break;
+            case ast::WaitUntilStmt::ClauseNode::TIMEOUT:
+                retStmt->timeout_time = currNode->leaf->target 
+                    ? ast::deepCopy( currNode->leaf->target )
+                    : nullptr;
+                retStmt->timeout_stmt = currNode->leaf->stmt
+                    ? ast::deepCopy( currNode->leaf->stmt )
+                    : nullptr;
+                retStmt->timeout_cond = currNode->leaf->when_cond
+                    ? ast::deepCopy( currNode->leaf->when_cond )
+                    : nullptr;
+
+                delete currNode->leaf;
+                break;
+            default:
+                nodeStack.push_back( currNode->right ); // process right after left
+                nodeStack.push_back( currNode->left );
+
+                // Cut else/timeout out of the tree
+                if ( currNode->op == ast::WaitUntilStmt::ClauseNode::LEFT_OR ) {
+                    if ( lastInternalNode )
+                        lastInternalNode->right = currNode->left;
+                    else    // if not set then root is LEFT_OR 
+                        retStmt->predicateTree = currNode->left;
+    
+                    currNode->left = nullptr;
+                    cleanup = currNode;
+                }
+                
+                lastInternalNode = currNode;
+                break;
+        }
+    } while ( !nodeStack.empty() );
+
+    if ( cleanup ) delete cleanup;
+
+    return retStmt;
+}
 
 ast::Stmt * build_with( const CodeLocation & location, ExpressionNode * exprs, StatementNode * stmt ) {
Index: src/Parser/StatementNode.h
===================================================================
--- src/Parser/StatementNode.h	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/Parser/StatementNode.h	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -100,4 +100,8 @@
 ast::WaitForStmt * build_waitfor_else( const CodeLocation &, ast::WaitForStmt * existing, ExpressionNode * when, StatementNode * stmt );
 ast::WaitForStmt * build_waitfor_timeout( const CodeLocation &, ast::WaitForStmt * existing, ExpressionNode * when, ExpressionNode * timeout, StatementNode * stmt );
+ast::WaitUntilStmt::ClauseNode * build_waituntil_clause( const CodeLocation &, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt );
+ast::WaitUntilStmt::ClauseNode * build_waituntil_else( const CodeLocation &, ExpressionNode * when, StatementNode * stmt );
+ast::WaitUntilStmt::ClauseNode * build_waituntil_timeout( const CodeLocation &, ExpressionNode * when, ExpressionNode * timeout, StatementNode * stmt );
+ast::WaitUntilStmt * build_waituntil_stmt( const CodeLocation &, ast::WaitUntilStmt::ClauseNode * root );
 ast::Stmt * build_with( const CodeLocation &, ExpressionNode * exprs, StatementNode * stmt );
 ast::Stmt * build_mutex( const CodeLocation &, ExpressionNode * exprs, StatementNode * stmt );
Index: src/Parser/parser.yy
===================================================================
--- src/Parser/parser.yy	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/Parser/parser.yy	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -307,4 +307,5 @@
 	ClauseNode * clause;
 	ast::WaitForStmt * wfs;
+    ast::WaitUntilStmt::ClauseNode * wucn;
 	CondCtl * ifctl;
 	ForCtrl * forctl;
@@ -427,5 +428,6 @@
 %type<expr> when_clause					when_clause_opt				waitfor		waituntil		timeout
 %type<stmt> waitfor_statement				waituntil_statement
-%type<wfs> wor_waitfor_clause			waituntil_clause			wand_waituntil_clause	wor_waituntil_clause
+%type<wfs> wor_waitfor_clause
+%type<wucn> waituntil_clause			wand_waituntil_clause       wor_waituntil_clause
 
 // declarations
@@ -1685,30 +1687,33 @@
 waituntil_clause:
 	when_clause_opt waituntil statement
-		{ printf( "waituntil_clause 1\n" ); $$ = nullptr; }
+		{ $$ = build_waituntil_clause( yylloc, $1, $2, maybe_build_compound( yylloc, $3 ) ); }
 	| '(' wor_waituntil_clause ')'
-		{ printf( "waituntil_clause 2\n" ); $$ = nullptr; }
+		{ $$ = $2; }
 	;
 
 wand_waituntil_clause:
 	waituntil_clause									%prec THEN
-		{ printf( "wand_waituntil_clause 1\n" ); $$ = nullptr; }
+		{ $$ = $1; }
 	| waituntil_clause wand wand_waituntil_clause
-		{ printf( "wand_waituntil_clause 2\n" ); $$ = nullptr; }
+		{ $$ = new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::AND, $1, $3 ); }
 	;
 
 wor_waituntil_clause:
 	wand_waituntil_clause
-		{ printf( "wor_waituntil_clause 1\n" ); $$ = nullptr; }
+		{ $$ = $1; }
 	| wor_waituntil_clause wor wand_waituntil_clause
-		{ printf( "wor_waituntil_clause 2\n" ); $$ = nullptr; }
+		{ $$ = new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::OR, $1, $3 ); }
 	| wor_waituntil_clause wor when_clause_opt ELSE statement
-		{ printf( "wor_waituntil_clause 3\n" ); $$ = nullptr; }
+		{ $$ = new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::LEFT_OR, $1, build_waituntil_else( yylloc, $3, maybe_build_compound( yylloc, $5 ) ) ); }
 	| wor_waituntil_clause wor when_clause_opt timeout statement	%prec THEN
-		{ printf( "wor_waituntil_clause 4\n" ); $$ = nullptr; }
+		{ $$ = new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::LEFT_OR, $1, build_waituntil_timeout( yylloc, $3, $4, maybe_build_compound( yylloc, $5 ) ) ); }
 	// "else" must be conditional after timeout or timeout is never triggered (i.e., it is meaningless)
 	| wor_waituntil_clause wor when_clause_opt timeout statement wor ELSE statement // syntax error
 		{ SemanticError( yylloc, "else clause must be conditional after timeout or timeout never triggered." ); $$ = nullptr; }
 	| wor_waituntil_clause wor when_clause_opt timeout statement wor when_clause ELSE statement
-		{ printf( "wor_waituntil_clause 6\n" ); $$ = nullptr; }
+		{ $$ = new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::LEFT_OR, $1,
+                new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::OR, 
+                    build_waituntil_timeout( yylloc, $3, $4, maybe_build_compound( yylloc, $5 ) ), 
+                    build_waituntil_else( yylloc, $7, maybe_build_compound( yylloc, $9 ) ) ) ); }
 	;
 
@@ -1716,5 +1721,8 @@
 	wor_waituntil_clause								%prec THEN
 		// SKULLDUGGERY: create an empty compound statement to test parsing of waituntil statement.
-		{ $$ = new StatementNode( build_compound( yylloc, nullptr ) ); }
+		{
+            $$ = new StatementNode( build_waituntil_stmt( yylloc, $1 ) );
+            // $$ = new StatementNode( build_compound( yylloc, nullptr ) );
+        }
 	;
 
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/ResolvExpr/Resolver.cc	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -1730,10 +1730,10 @@
 
 			// Find all candidates for a function in canonical form
-			funcFinder.find( clause.target_func, ResolvMode::withAdjustment() );
+			funcFinder.find( clause.target, ResolvMode::withAdjustment() );
 
 			if ( funcFinder.candidates.empty() ) {
 				stringstream ss;
 				ss << "Use of undeclared indentifier '";
-				ss << clause.target_func.strict_as< ast::NameExpr >()->name;
+				ss << clause.target.strict_as< ast::NameExpr >()->name;
 				ss << "' in call to waitfor";
 				SemanticError( stmt->location, ss.str() );
@@ -1922,5 +1922,5 @@
 			auto clause2 = new ast::WaitForClause( clause.location );
 
-			clause2->target_func = funcCandidates.front()->expr;
+			clause2->target = funcCandidates.front()->expr;
 
 			clause2->target_args.reserve( clause.target_args.size() );
@@ -1945,5 +1945,5 @@
 
 			// Resolve the conditions as if it were an IfStmt, statements normally
-			clause2->cond = findSingleExpression( clause.cond, context );
+			clause2->when_cond = findSingleExpression( clause.when_cond, context );
 			clause2->stmt = clause.stmt->accept( *visitor );
 
Index: src/main.cc
===================================================================
--- src/main.cc	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ src/main.cc	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -48,4 +48,5 @@
 #include "Concurrency/Keywords.h"           // for implementMutex, implement...
 #include "Concurrency/Waitfor.h"            // for generateWaitfor
+#include "Concurrency/Waituntil.hpp"        // for generateWaitUntil
 #include "ControlStruct/ExceptDecl.h"       // for translateExcept
 #include "ControlStruct/ExceptTranslate.h"  // for translateThrows, translat...
@@ -340,4 +341,5 @@
 		PASS( "Implement Concurrent Keywords", Concurrency::implementKeywords, transUnit );
 		PASS( "Forall Pointer Decay", Validate::decayForallPointers, transUnit );
+        PASS( "Implement Waituntil", Concurrency::generateWaitUntil, transUnit  );
 		PASS( "Hoist Control Declarations", ControlStruct::hoistControlDecls, transUnit );
 
Index: tests/Makefile.am
===================================================================
--- tests/Makefile.am	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ tests/Makefile.am	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -11,6 +11,6 @@
 ## Created On       : Sun May 31 09:08:15 2015
 ## Last Modified By : Peter A. Buhr
-## Last Modified On : Mon May  1 16:45:07 2023
-## Update Count     : 144
+## Last Modified On : Mon May  1 17:25:24 2023
+## Update Count     : 145
 ###############################################################################
 
@@ -116,5 +116,5 @@
 #----------------------------------------------------------------------------------------------------------------
 
-all-local :
+all-local : # This name is important to automake and implies the default build target.
 	@+$(TEST_PY) --debug=$(debug) --install=$(installed) --archive-errors=$(archiveerrors) $(concurrent) $(timeouts) $(ARCH) --all # '@' => do not echo command (SILENT), '+' => allows recursive make from within python program
 
Index: tests/concurrent/futures/select_future.cfa
===================================================================
--- tests/concurrent/futures/select_future.cfa	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ tests/concurrent/futures/select_future.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -196,5 +196,3 @@
     delete( shared_future );
 	printf( "done 3\n" );
-
-    // C_TODO: add test for select statement once it is implemented
 }
Index: sts/concurrent/mutexstmt/.expect/scoped_lock.txt
===================================================================
--- tests/concurrent/mutexstmt/.expect/scoped_lock.txt	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ 	(revision )
@@ -1,4 +1,0 @@
-Start Test: single scoped lock mutual exclusion
-End Test: single scoped lock mutual exclusion
-Start Test: multi scoped lock deadlock/mutual exclusion
-End Test: multi scoped lock deadlock/mutual exclusion
Index: sts/concurrent/mutexstmt/scoped_lock.cfa
===================================================================
--- tests/concurrent/mutexstmt/scoped_lock.cfa	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ 	(revision )
@@ -1,96 +1,0 @@
-#include <mutex_stmt.hfa>
-#include <locks.hfa>
-
-const unsigned int num_times = 10000;
-
-single_acquisition_lock m1, m2, m3, m4, m5;
-
-bool insideFlag = false;
-int count = 0;
-
-thread T_Mutex_Scoped {};
-
-void main( T_Mutex_Scoped & this ) {
-	for (unsigned int i = 0; i < num_times; i++) {
-		{
-			scoped_lock(single_acquisition_lock) s{m1};
-			count++;
-		}
-		{
-			scoped_lock(single_acquisition_lock) s{m1};
-			assert(!insideFlag);
-			insideFlag = true;
-			assert(insideFlag);
-			insideFlag = false;
-		}
-	}
-}
-
-thread T_Multi_Scoped {};
-
-void main( T_Multi_Scoped & this ) {
-	for (unsigned int i = 0; i < num_times; i++) {
-		{
-			scoped_lock(single_acquisition_lock) s{m1};
-			assert(!insideFlag);
-			insideFlag = true;
-			assert(insideFlag);
-			insideFlag = false;
-		}
-		{
-			scoped_lock(single_acquisition_lock) s1{m1};
-			scoped_lock(single_acquisition_lock) s2{m2};
-			scoped_lock(single_acquisition_lock) s3{m3};
-			scoped_lock(single_acquisition_lock) s4{m4};
-			scoped_lock(single_acquisition_lock) s5{m5};
-			assert(!insideFlag);
-			insideFlag = true;
-			assert(insideFlag);
-			insideFlag = false;
-		}
-		{
-			scoped_lock(single_acquisition_lock) s1{m1};
-			scoped_lock(single_acquisition_lock) s3{m3};
-			assert(!insideFlag);
-			insideFlag = true;
-			assert(insideFlag);
-			insideFlag = false;
-		}
-		{
-			scoped_lock(single_acquisition_lock) s1{m1};
-			scoped_lock(single_acquisition_lock) s2{m2};
-			scoped_lock(single_acquisition_lock) s4{m4};
-			assert(!insideFlag);
-			insideFlag = true;
-			assert(insideFlag);
-			insideFlag = false;
-		}
-		{
-			scoped_lock(single_acquisition_lock) s1{m1};
-			scoped_lock(single_acquisition_lock) s3{m3};
-			scoped_lock(single_acquisition_lock) s4{m4};
-			scoped_lock(single_acquisition_lock) s5{m5};
-			assert(!insideFlag);
-			insideFlag = true;
-			assert(insideFlag);
-			insideFlag = false;
-		}
-	}
-}
-
-int num_tasks = 10;
-int main() {
-	processor p[10];
-
-	printf("Start Test: single scoped lock mutual exclusion\n");
-	{
-		T_Mutex_Scoped t[10];
-	}
-	assert(count == num_tasks * num_times);
-	printf("End Test: single scoped lock mutual exclusion\n");
-	printf("Start Test: multi scoped lock deadlock/mutual exclusion\n");
-	{
-		T_Multi_Scoped t[10];
-	}
-	printf("End Test: multi scoped lock deadlock/mutual exclusion\n");
-}
Index: sts/concurrent/unified_locking/.expect/clh.txt
===================================================================
--- tests/concurrent/unified_locking/.expect/clh.txt	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ 	(revision )
@@ -1,3 +1,0 @@
-Starting
-Done!
-Match!
Index: sts/concurrent/unified_locking/clh.cfa
===================================================================
--- tests/concurrent/unified_locking/clh.cfa	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ 	(revision )
@@ -1,8 +1,0 @@
-#include <locks.hfa>
-
-#define LOCK clh_lock
-#include "mutex_test.hfa"
-
-int main() {
-    test();
-}
Index: tests/concurrent/waituntil/.expect/basic_else.txt
===================================================================
--- tests/concurrent/waituntil/.expect/basic_else.txt	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
+++ tests/concurrent/waituntil/.expect/basic_else.txt	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -0,0 +1,5 @@
+else1
+A2
+else3
+A4
+A5
Index: tests/concurrent/waituntil/.expect/channel_close.txt
===================================================================
--- tests/concurrent/waituntil/.expect/channel_close.txt	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
+++ tests/concurrent/waituntil/.expect/channel_close.txt	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -0,0 +1,10 @@
+start OR
+done sleep
+closing A
+closing B
+done
+start AND
+done sleep
+closing A
+closing B
+done
Index: tests/concurrent/waituntil/.expect/channels.txt
===================================================================
--- tests/concurrent/waituntil/.expect/channels.txt	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
+++ tests/concurrent/waituntil/.expect/channels.txt	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -0,0 +1,6 @@
+start
+terminating churner
+waiting for empty channels
+sending sentinels
+joining servers
+done
Index: tests/concurrent/waituntil/.expect/futures.txt
===================================================================
--- tests/concurrent/waituntil/.expect/futures.txt	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
+++ tests/concurrent/waituntil/.expect/futures.txt	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -0,0 +1,2 @@
+start
+end
Index: tests/concurrent/waituntil/.expect/locks.txt
===================================================================
--- tests/concurrent/waituntil/.expect/locks.txt	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
+++ tests/concurrent/waituntil/.expect/locks.txt	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -0,0 +1,6 @@
+start
+DONE
+DONE
+DONE
+start recursive acq test
+done
Index: tests/concurrent/waituntil/basic_else.cfa
===================================================================
--- tests/concurrent/waituntil/basic_else.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
+++ tests/concurrent/waituntil/basic_else.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -0,0 +1,17 @@
+#include <select.hfa>
+#include <future.hfa>
+
+future(int) A, B, C;
+
+int main() {
+    waituntil( A ) { printf("A1\n"); } or else { printf("else1\n"); }
+    fulfil( A, 1 );
+    waituntil( A ) { printf("A2\n"); } or else { printf("else2\n"); }
+    reset( A );
+    waituntil( A ) { printf("A3\n"); } or when(true) else { printf("else3\n"); }
+    fulfil( A, 1 );
+    waituntil( A ) { printf("A4\n"); } or when(false) else { printf("else4\n"); }
+    reset( A );
+    fulfil( A, 1 );
+    waituntil( A ) { printf("A5\n"); }
+}
Index: tests/concurrent/waituntil/channel_close.cfa
===================================================================
--- tests/concurrent/waituntil/channel_close.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
+++ tests/concurrent/waituntil/channel_close.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -0,0 +1,94 @@
+#include <select.hfa>
+#include <thread.hfa>
+#include <channel.hfa>
+#include <time.hfa>
+
+channel(long long int) A, B;
+
+volatile long long int inserts = 0;
+volatile long long int removes = 0;
+
+thread Producer {};
+void main( Producer & this ) {
+    try {
+        for( long long int i = 0;;i++ ) {
+            waituntil( (i >> A) ) { inserts++; }
+            and waituntil( (i >> B) ) { inserts++; }
+        }
+    } catch ( channel_closed * e ) {} 
+}
+
+bool useAnd = false;
+thread Consumer {}; // ensures that the changing when states of Server1 don't result in a deadlock
+void main( Consumer & this ) {
+    long long int in, in2, A_removes = 0, B_removes = 0;
+    try {
+        for( ;; ) {
+            if ( useAnd ) {
+                waituntil( (in << A) ) { assert( A_removes == in ); A_removes++; removes++; }
+                and waituntil( (in2 << B) ) { assert( B_removes == in2 ); B_removes++; removes++; }
+                continue;
+            }
+            waituntil( (in << A) ) { assert( A_removes == in ); A_removes++; removes++; }
+            or waituntil( (in2 << B) ) { assert( B_removes == in2 ); B_removes++; removes++; }
+        }
+    } catchResume ( channel_closed * e ) {} // continue to remove until would block
+    catch ( channel_closed * e ) {} 
+    try {
+        for( ;; )
+            waituntil( (in << A) ) { assert( A_removes == in ); A_removes++; removes++; }
+    } catchResume ( channel_closed * e ) {} // continue to remove until would block
+    catch ( channel_closed * e ) {} 
+    try {
+        for( ;; )
+            waituntil( (in << B) ) { assert( B_removes == in ); B_removes++; removes++; }
+    } catchResume ( channel_closed * e ) {} // continue to remove until would block
+    catch ( channel_closed * e ) {} 
+}
+
+
+size_t time = 5;
+int main( int argc, char * argv[] ) {
+    if ( argc == 2 )
+        time = atoi( argv[1] );
+
+    processor p[2];
+    A{5};
+    B{5};
+
+    printf("start OR\n");
+    {
+        Producer p;
+        Consumer c;
+        sleep(time`s);
+        printf("done sleep\n");
+        printf("closing A\n");
+        close(A);
+        printf("closing B\n");
+        close(B);
+    }
+    if ( inserts != removes ) 
+        printf("CHECKSUM MISMATCH!! Producer got: %lld, Consumer got: %lld\n", inserts, removes);
+    printf("done\n");
+    ^A{};
+    ^B{};
+
+    inserts = 0;
+    removes = 0;
+    A{5};
+    B{5};
+    printf("start AND\n");
+    {
+        Producer p;
+        Consumer c;
+        sleep(time`s);
+        printf("done sleep\n");
+        printf("closing A\n");
+        close(A);
+        printf("closing B\n");
+        close(B);
+    }
+    if ( inserts != removes ) 
+        printf("CHECKSUM MISMATCH!! Producer got: %lld, Consumer got: %lld\n", inserts, removes);
+    printf("done\n");
+}
Index: tests/concurrent/waituntil/channels.cfa
===================================================================
--- tests/concurrent/waituntil/channels.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
+++ tests/concurrent/waituntil/channels.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -0,0 +1,86 @@
+#include <select.hfa>
+#include <thread.hfa>
+#include <channel.hfa>
+
+channel(long long int) A, B, C;
+
+volatile bool done = false;
+long long int globalTotal = 0;
+
+thread Server1 {};
+void main( Server1 & this ) {
+    long long int a, b, c, i = 0, myTotal = 0;
+    for( ;;i++ ) {
+        when( i % 2 == 0 ) waituntil( (a << A) ) { myTotal += a; }
+        or when( i % 4 < 2 ) waituntil( (b << B) ) { myTotal += b; }
+        or waituntil( (c << C) ) { if ( c == -1 ) break; myTotal += c; }
+        or when( i % 8 < 4 ) else {}
+    }
+    __atomic_fetch_add( &globalTotal, myTotal, __ATOMIC_SEQ_CST );
+}
+
+thread Drainer {}; // ensures that the changing when states of Server1 don't result in a deadlock
+void main( Drainer & this ) {
+    long long int a, b, c, i = 0, myTotal = 0;
+    for( ;;i++ ) {
+        waituntil( (a << A) ) { myTotal += a; }
+        or waituntil( (b << B) ) { myTotal += b; }
+        or waituntil( (c << C) ) { if ( c == -1 ) break; myTotal += c; }
+    }
+    __atomic_fetch_add( &globalTotal, myTotal, __ATOMIC_SEQ_CST );
+}
+
+thread Churner {}; // performs non-waituntil try insert/remove operations to add churn/interference
+void main( Churner & this ) {
+    long long int out, myTotal = 0;
+    bool success;
+    while( !done ) {
+        try_insert( A, 0 );
+        try_insert( B, 0 );
+        try_insert( C, 0 );
+        [out, success] = try_remove(A);
+        if ( success ) myTotal += out;
+        [out, success] = try_remove(B);
+        if ( success ) myTotal += out;
+        [out, success] = try_remove(C);
+        if ( success ) myTotal += out;
+    }
+    __atomic_fetch_add( &globalTotal, myTotal, __ATOMIC_SEQ_CST );
+}
+
+size_t numtimes = 100000;
+size_t numServers = 3;
+int main( int argc, char * argv[] ) {
+    if ( argc == 2 )
+        numtimes = atoi( argv[1] );
+
+    processor p[numServers + 2];
+    A{5};
+    B{5};
+    C{5};
+
+    long long int total = 0;
+    printf("start\n");
+    {
+        Server1 s[numServers];
+        Drainer d;
+        {
+            Churner c;
+            for( long long int j = 0; j < numtimes; j++ ) {
+                when( j % 2 == 0 ) waituntil( (j >> A) ) { total += j; }
+                or when( j % 4 < 2 ) waituntil( (j >> B) ) { total += j; }
+                and when( j % 8 < 4 ) waituntil( (j >> C) ) { total += j; }
+            }
+            done = true;
+            printf("terminating churner\n");
+        }
+        printf("waiting for empty channels\n");
+        while( get_count(A) > 0 || get_count(B) > 0 || get_count(C) > 0 ) { Pause(); }
+        printf("sending sentinels\n");
+        for ( i; numServers + 1 ) insert( C, -1 );
+        printf("joining servers\n");
+    }
+    if ( total != globalTotal ) 
+        printf("CHECKSUM MISMATCH!! Main thread got %lld, server sum is %lld\n", total, globalTotal);
+    printf("done\n");
+}
Index: tests/concurrent/waituntil/futures.cfa
===================================================================
--- tests/concurrent/waituntil/futures.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
+++ tests/concurrent/waituntil/futures.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -0,0 +1,61 @@
+#include <select.hfa>
+#include <future.hfa>
+#include <thread.hfa>
+
+future(int) A, B, C;
+
+semaphore s{0};
+
+thread Server1 {};
+void main( Server1 & this ) {
+    fulfil(B, 3);
+    P( s );
+    fulfil(A, 2);
+    fulfil(C, 4);
+}
+
+thread Server2 {};
+void main( Server2 & this ) {
+    fulfil(B, 6);
+    fulfil(A, 5);
+    fulfil(C, 7);
+}
+
+int main() {
+    processor proc[1];
+    printf("start\n"); // currently not working
+    {
+        Server1 s1;
+        waituntil( A ) { get(A); }
+        or waituntil( B ) { get(B); V( s ); }
+        and waituntil( C ) { get(C); }
+    }
+    reset(A);
+    reset(B);
+    reset(C);
+    for ( int i = 0; i < 8; i++ ) {
+        {
+            Server2 s2;
+            when( i % 2 == 0 ) waituntil( A ) { get(A); }
+            or when( i % 4 < 2 ) waituntil( B ) { get(B); }
+            and when( i < 4 ) waituntil( C ) { get(C); }
+        }
+        reset(A);
+        reset(B);
+        reset(C);
+        {
+            Server2 s2;
+            (
+                when( i % 2 == 0 ) waituntil( A ) { get(A); }
+                or when( i % 4 < 2 ) waituntil( B ) { get(B); }
+            )
+            and when( i < 4 ) waituntil( C ) { get(C); }
+        }
+        reset(A);
+        reset(B);
+        reset(C);
+    }
+
+    printf("end\n");
+    return 0;
+}
Index: tests/concurrent/waituntil/locks.cfa
===================================================================
--- tests/concurrent/waituntil/locks.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
+++ tests/concurrent/waituntil/locks.cfa	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
@@ -0,0 +1,73 @@
+#include <select.hfa>
+#include <thread.hfa>
+#include <locks.hfa>
+#include <mutex_stmt.hfa>
+
+multiple_acquisition_lock A;
+simple_owner_lock B;
+simple_owner_lock C;
+
+volatile bool done = false;
+
+thread Server1 {};
+void main( Server1 & this ) {
+    while( !done ) {
+        lock(A);
+        unlock(A);
+        lock(B);
+        unlock(B);
+        lock(C);
+        unlock(C);
+        mutex(A,B,C) {};
+    }
+    mutex(sout) sout | "DONE";
+}
+
+size_t numtimes = 10000;
+int main() {
+    processor p[3];
+    int a = 0, b = 0, c = 0;
+    printf("start\n");
+    {
+        Server1 s[3];
+        for( j; numtimes ) {
+            for ( int i = 0; i < 8; i++ ) {
+
+                when( i % 2 == 0 ) waituntil( A ) { a++; }
+                or when( i % 4 < 2 ) waituntil( B ) { b++; }
+                and when( i < 4 ) waituntil( C ) { c++; }
+
+                (
+                    when( i % 2 == 0 ) waituntil( A ) { a++; }
+                    or when( i % 4 < 2 ) waituntil( B ) { b++; }
+                )
+                and when( i < 4 ) waituntil( C ) { c++; }
+                
+                when( i % 2 == 0 ) waituntil( A ) { a++; }
+                and when( i % 4 < 2 ) waituntil( B ) { b++; }
+                and when( i < 4 ) waituntil( C ) { c++; }
+
+                when( i % 2 == 0 ) waituntil( A ) { a++; }
+                or when( i % 4 < 2 ) waituntil( B ) { b++; }
+                or when( i < 4 ) waituntil( C ) { c++; }
+            }
+        }
+        done = true;
+    }
+    printf("start recursive acq test\n");
+    {
+        for( j; 10 ) {
+            lock( A );
+            lock( B );
+        }
+        for ( j; 10 ) {
+            waituntil( A ) { a++; } or waituntil( B ) { b++; }
+            waituntil( B ) { b++; } or waituntil( A ) { a++; }
+        }
+        for( j; 10 ) {
+            unlock( A );
+            unlock( B );
+        }
+    }
+    printf("done\n");
+}
Index: tests/zombies/gc_no_raii/bug-repro/deref.c
===================================================================
--- tests/zombies/gc_no_raii/bug-repro/deref.c	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ tests/zombies/gc_no_raii/bug-repro/deref.c	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
Index: tests/zombies/gc_no_raii/bug-repro/malloc.c
===================================================================
--- tests/zombies/gc_no_raii/bug-repro/malloc.c	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ tests/zombies/gc_no_raii/bug-repro/malloc.c	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
Index: tests/zombies/gc_no_raii/bug-repro/zero.c
===================================================================
--- tests/zombies/gc_no_raii/bug-repro/zero.c	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ tests/zombies/gc_no_raii/bug-repro/zero.c	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
Index: tests/zombies/gc_no_raii/test/operators.c
===================================================================
--- tests/zombies/gc_no_raii/test/operators.c	(revision 4daf79f5bebb1c45d59ca6a16f2c2facb6763c7d)
+++ tests/zombies/gc_no_raii/test/operators.c	(revision c083c3d051f3db3793a548d324e8e7d5aa9560d1)
