/*
 * PingPong2.java
 *
 * Alternating thread pattern using the Lock and Condition classes
 * from the Java concurrent package.  Programs must explicitly manage
 * locking and unlocking, unlike what is implicitly done when using
 * synchronized methods and blocks.
 *
 * % javac PingPong2.java
 * % java PingPong2
 *
 * Geoff Voelker
 * December 2018
 */

import java.util.concurrent.locks.*;

public class PingPong2 implements Runnable {
    final static int ITERS = 10;
    String whoami;

    final static Lock lock = new ReentrantLock();
    final static Condition cv = lock.newCondition();

    public PingPong2 (String name) {
	whoami = name;
    }

    public void run () {
	lock.lock();
	
	try {
	    for (int i = 0; i < ITERS; i++) {
		System.out.println (whoami);

		Thread.currentThread().yield();  // 'arbitrary' context switch
		cv.signal();
		Thread.currentThread().yield();  // 'arbitrary' context switch
		cv.await();
		Thread.currentThread().yield();  // 'arbitrary' context switch
	    }
	    // wake up whichever thread is still waiting on the cv
	    cv.signal();

	} catch (InterruptedException e) {
	    e.printStackTrace();
	} finally {
	    lock.unlock();
	}
    }

    public static void main (String args[]) {
	Thread ping = new Thread (new PingPong2 ("ping"));
	Thread pong = new Thread (new PingPong2 ("pong"));

	ping.start();
	pong.start();
	try {
	    ping.join();
	    pong.join();
	} catch (InterruptedException e) {
	    e.printStackTrace();
	}
    }
}
