// TestTransferWithoutSync.java: Demonstrate resource conflict

public class TestTransferWithoutSync
{
  // Main method
  public static void main(String[] args)
  {
    // Determine if all threads are finished
    boolean done = false;

    // Create a savings account with ID 1 and balance 10000
    Account saving = new Account(1, 10000);

    // Create a checking account with ID 2 and balance 0
    Account checking = new Account(2, 0);

    // Create 100 threads in t1 to transfer money from
    // savings to checking
    Thread t1[] = new Thread[100];

    // Create a thread group g1 for grouping t1's
    ThreadGroup g1 = new ThreadGroup("from savings to checking");

    // Create 100 threads in t2 to transfer money from
    // checking to savings
    Thread t2[] = new Thread[100];

    // Create a thread group g2 for grouping t2's
    ThreadGroup g2 = new ThreadGroup("from checking to savings");

    // Add t1[i] to g1, and start t1[i]
    for (int i=0; i<100; i++)
    {
      t1[i] = new Thread(g1,
        new TransferThread(saving, checking, 1), "t1");
      t1[i].start();
    }

    // Add t2[i] to g2, and start t2[i]
    for (int i=0; i<100; i++)
    {
      t2[i] = new Thread(g2,
        new TransferThread(checking, saving, 1),"t2");
      t2[i].start();
    }

    // Exit the loop when all threads finished
    while (!done)
      if ((g1.activeCount() == 0) && (g2.activeCount() == 0))
        done = true;

    // Show the balance in the savings and checking accounts
    System.out.println("Savings account balance "+
      saving.getBalance());
    System.out.println("Checking account balance "+
      checking.getBalance());
  }
}

// Define the thread to transfer money between accounts
class TransferThread extends Thread
{
  private Account fromAccount, toAccount;
  private double amount;

  // Construct a thread transferring amount
  // from account s to account c
  public TransferThread(Account s, Account c, double amount)
  {
    fromAccount = s;
    toAccount = c;
    this.amount = amount;
  }

  // Override the run method
  public void run()
  {
    transfer(fromAccount, toAccount, amount);
  }

  // Transfer amount from fromAccount to toAccount
  public void transfer(Account fromAccount,
                       Account toAccount,
                       double amount)
  {
    // Record the balance before transaction for use in recovery
    double fromAccountPriorBalance = fromAccount.getBalance();
    double toAccountPriorBalance = toAccount.getBalance();

    try
    {
      toAccount.deposit(amount);
      sleep(10);
      fromAccount.withdraw(amount);
    }
    catch (NegativeAmountException ex)
    {
      // Reset the balance to the value prior to the exception
      fromAccount.setBalance(fromAccountPriorBalance);
      toAccount.setBalance(toAccountPriorBalance);
    }
    catch (InsufficientFundException ex)
    {
      // Reset the balance to the value prior to the exception
      fromAccount.setBalance(fromAccountPriorBalance);
      toAccount.setBalance(toAccountPriorBalance);
    }
    catch (InterruptedException ex)
    {
    }
  }
}
