<commit_before>package javaUtilities;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

import javaUtilities.UpgradableLock.Mode;

import org.junit.*;
import org.junit.rules.Timeout;

public class UpgradableLockTest {
  private static int MAX_TEST_LENGTH_MILLIS = 5_000;
  private static long MAX_WAIT_FOR_LOCK_MILLIS = 10;
  
  private UpgradableLock myLock;
  
  @Rule
  public Timeout myTimeout = new Timeout(MAX_TEST_LENGTH_MILLIS);
  
  @Before
  public void setup() {
    myLock = new UpgradableLock();
    Thread.interrupted();
  }

  @Test
  public void testWriteLock() throws Throwable {
    myLock.lock(Mode.WRITE);
    myLock.lock(Mode.READ);
    assertTrue(hasWriter());
    myLock.unlock();
    assertTrue(hasWriter());
    myLock.unlock();
    assertTrue(isUnlocked());
  }

  @Test
  public void testUpgrading() throws Throwable {
    myLock.lock(Mode.UPGRADABLE);
    assertTrue(hasUpgradable());
    myLock.lock(Mode.READ);
    assertTrue(hasUpgradable());
    myLock.upgrade();
    assertTrue(hasWriter());
    myLock.downgrade();
    assertTrue(hasUpgradable());
    myLock.lock(Mode.WRITE);
    assertTrue(hasWriter());
    myLock.unlock();
    assertTrue(hasUpgradable());
    myLock.unlock();
    assertTrue(hasUpgradable());
    myLock.unlock();
    assertTrue(isUnlocked());
  }

  @Test
  public void testReadLock() throws Throwable {
    myLock.lock(Mode.READ);
    assertTrue(hasReaders());
    myLock.lock(Mode.READ);
    assertTrue(hasReaders());
    myLock.unlock();
    assertTrue(hasReaders());
    myLock.unlock();
    assertTrue(isUnlocked());
  }
  
  @Test
  public void clearUpgradesAfterFullUnlock() throws Throwable {
    myLock.lock(Mode.UPGRADABLE);
    myLock.upgrade();
    assertTrue(hasWriter());
    myLock.unlock();
    assertTrue(isUnlocked());
    myLock.lock(Mode.UPGRADABLE);
    assertTrue(hasUpgradable());
  }
  
  @Test
  public void keepUpgradesAfterReleasingWriteLock() throws Throwable {
    myLock.lock(Mode.UPGRADABLE);
    myLock.lock(Mode.WRITE);
    myLock.upgrade();
    assertTrue(hasWriter());
    myLock.unlock();
    assertTrue(hasWriter());
    myLock.downgrade();
    assertTrue(hasUpgradable());
  }
  
  @Test
  public void testTimeout() throws Throwable {
    lockPermanently(Mode.WRITE);
    for (Mode mMode : Mode.values()) {
      boolean mSuccess = myLock.tryLock(mMode, 100, TimeUnit.MICROSECONDS);
      assertFalse(mSuccess);
    }
  }
  
  @Test
  public void testNegativeTimeout() throws Throwable {
    lockPermanently(Mode.UPGRADABLE);
    boolean mSuccess = myLock.tryLock(Mode.UPGRADABLE, -3L, TimeUnit.MICROSECONDS);
    assertFalse(mSuccess);
  }
  
  @Test
  public void testTryLockWhenAvailable() throws Throwable {
    for (Mode mMode : Mode.values()) {
      assertTrue(myLock.tryLock(mMode));
      myLock.unlock();
      assertTrue(isUnlocked());
    }
  }
  
  @Test
  public void testTryLockWhenNotAvailable() throws Throwable {
    lockPermanently(Mode.WRITE);
    for (Mode mMode : Mode.values()) {
      assertFalse(myLock.tryLock(mMode));
    }
  }

  @Test
  public void testInterruption() throws Throwable {
    ResultThread<Boolean> mThread = new ResultThread<>(new Callable<Boolean>() {
      @Override
      public Boolean call() {
        try {
          myLock.lockInterruptibly(Mode.UPGRADABLE);
          return false;
        } catch (InterruptedException e) {
          return true;
        }
      }
    });
    lockPermanently(Mode.WRITE);
    mThread.start();
    Thread.sleep(MAX_WAIT_FOR_LOCK_MILLIS);
    mThread.interrupt();
    assertTrue("Thread not interrupted", mThread.get());
  }
  
  @Test
  public void retainInterruptedStatusWithTryLock() throws Throwable {
    Thread mCurrent = Thread.currentThread();
    mCurrent.interrupt();
    assertTrue(myLock.tryLock(Mode.WRITE));
    myLock.unlock();
    assertTrue(Thread.interrupted());
    lockPermanently(Mode.READ);
    mCurrent.interrupt();
    assertFalse(myLock.tryLock(Mode.WRITE));
    assertTrue(Thread.interrupted());
  }
  
  /*
   * Several threads with read locks and one thread with an upgradable lock
   * wait on a barrier.
   */
  @Test
  public void allowConcurrentReadAndUpgradableAccess() throws Throwable {
    int mNThreads = 4;
    final CyclicBarrier mBarrier = new CyclicBarrier(mNThreads);
    ExecutorService mPool = Executors.newCachedThreadPool();
    Collection<Future<?>> mFutures = new ArrayList<>();
    for (int i = 0; i < mNThreads - 1; i++) {
      mFutures.add(mPool.submit(newBarrierTask(mBarrier, Mode.READ)));
    }<commit_msg>Added test for signaling multiple reader threads with a single release<commit_after>package javaUtilities;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

import javaUtilities.UpgradableLock.Mode;

import org.junit.*;
import org.junit.rules.Timeout;

public class UpgradableLockTest {
  private static int MAX_TEST_LENGTH_MILLIS = 5_000;
  private static long MAX_WAIT_FOR_LOCK_MILLIS = 10;
  
  private UpgradableLock myLock;
  
  @Rule
  public Timeout myTimeout = new Timeout(MAX_TEST_LENGTH_MILLIS);
  
  @Before
  public void setup() {
    myLock = new UpgradableLock();
    Thread.interrupted();
  }

  @Test
  public void testWriteLock() throws Throwable {
    myLock.lock(Mode.WRITE);
    myLock.lock(Mode.READ);
    assertTrue(hasWriter());
    myLock.unlock();
    assertTrue(hasWriter());
    myLock.unlock();
    assertTrue(isUnlocked());
  }

  @Test
  public void testUpgrading() throws Throwable {
    myLock.lock(Mode.UPGRADABLE);
    assertTrue(hasUpgradable());
    myLock.lock(Mode.READ);
    assertTrue(hasUpgradable());
    myLock.upgrade();
    assertTrue(hasWriter());
    myLock.downgrade();
    assertTrue(hasUpgradable());
    myLock.lock(Mode.WRITE);
    assertTrue(hasWriter());
    myLock.unlock();
    assertTrue(hasUpgradable());
    myLock.unlock();
    assertTrue(hasUpgradable());
    myLock.unlock();
    assertTrue(isUnlocked());
  }

  @Test
  public void testReadLock() throws Throwable {
    myLock.lock(Mode.READ);
    assertTrue(hasReaders());
    myLock.lock(Mode.READ);
    assertTrue(hasReaders());
    myLock.unlock();
    assertTrue(hasReaders());
    myLock.unlock();
    assertTrue(isUnlocked());
  }
  
  @Test
  public void clearUpgradesAfterFullUnlock() throws Throwable {
    myLock.lock(Mode.UPGRADABLE);
    myLock.upgrade();
    assertTrue(hasWriter());
    myLock.unlock();
    assertTrue(isUnlocked());
    myLock.lock(Mode.UPGRADABLE);
    assertTrue(hasUpgradable());
  }
  
  @Test
  public void keepUpgradesAfterReleasingWriteLock() throws Throwable {
    myLock.lock(Mode.UPGRADABLE);
    myLock.lock(Mode.WRITE);
    myLock.upgrade();
    assertTrue(hasWriter());
    myLock.unlock();
    assertTrue(hasWriter());
    myLock.downgrade();
    assertTrue(hasUpgradable());
  }
  
  @Test
  public void testTimeout() throws Throwable {
    lockPermanently(Mode.WRITE);
    for (Mode mMode : Mode.values()) {
      boolean mSuccess = myLock.tryLock(mMode, 100, TimeUnit.MICROSECONDS);
      assertFalse(mSuccess);
    }
  }
  
  @Test
  public void testNegativeTimeout() throws Throwable {
    lockPermanently(Mode.UPGRADABLE);
    boolean mSuccess = myLock.tryLock(Mode.UPGRADABLE, -3L, TimeUnit.MICROSECONDS);
    assertFalse(mSuccess);
  }
  
  @Test
  public void testTryLockWhenAvailable() throws Throwable {
    for (Mode mMode : Mode.values()) {
      assertTrue(myLock.tryLock(mMode));
      myLock.unlock();
      assertTrue(isUnlocked());
    }
  }
  
  @Test
  public void testTryLockWhenNotAvailable() throws Throwable {
    lockPermanently(Mode.WRITE);
    for (Mode mMode : Mode.values()) {
      assertFalse(myLock.tryLock(mMode));
    }
  }

  @Test
  public void testInterruption() throws Throwable {
    ResultThread<Boolean> mThread = new ResultThread<>(new Callable<Boolean>() {
      @Override
      public Boolean call() {
        try {
          myLock.lockInterruptibly(Mode.UPGRADABLE);
          return false;
        } catch (InterruptedException e) {
          return true;
        }
      }
    });
    lockPermanently(Mode.WRITE);
    mThread.start();
    Thread.sleep(MAX_WAIT_FOR_LOCK_MILLIS);
    mThread.interrupt();
    assertTrue("Thread not interrupted", mThread.get());
  }
  
  @Test
  public void retainInterruptedStatusWithTryLock() throws Throwable {
    Thread mCurrent = Thread.currentThread();
    mCurrent.interrupt();
    assertTrue(myLock.tryLock(Mode.WRITE));
    myLock.unlock();
    assertTrue(Thread.interrupted());
    lockPermanently(Mode.READ);
    mCurrent.interrupt();
    assertFalse(myLock.tryLock(Mode.WRITE));
    assertTrue(Thread.interrupted());
  }
  
  /*
   * Multiple reader threads wait for a lock held for writing. After the lock is
   * released, the reader threads wait on a barrier while holding the lock. This
   * ensures that all of the reader threads are signaled by only a single
   * release of lock.
   */
  @Test
  public void signalMultipleReaders() throws Throwable {
    myLock.lock(Mode.WRITE);
    int mNThreads = 5;
    final CyclicBarrier mBarrier = new CyclicBarrier(mNThreads);
    ExecutorService mPool = Executors.newCachedThreadPool();
    Collection<Future<?>> mFutures = new ArrayList<>();
    mFutures.add(mPool.submit(newBarrierTask(mBarrier, Mode.UPGRADABLE)));
    for (int i = 0; i < mNThreads - 1; i++) {
      mFutures.add(mPool.submit(newBarrierTask(mBarrier, Mode.READ)));
    }
    Thread.sleep(MAX_WAIT_FOR_LOCK_MILLIS);
    myLock.unlock();
    for (Future<?> mFuture : mFutures) {
      getFromFuture(mFuture);
    }
    assertTrue(isUnlocked());
  }
  
  /*
   * Several threads with read locks and one thread with an upgradable lock
   * wait on a barrier.
   */
  @Test
  public void allowConcurrentReadAndUpgradableAccess() throws Throwable {
    int mNThreads = 4;
    final CyclicBarrier mBarrier = new CyclicBarrier(mNThreads);
    ExecutorService mPool = Executors.newCachedThreadPool();
    Collection<Future<?>> mFutures = new ArrayList<>();
    for (int i = 0; i < mNThreads - 1; i++) {
      mFutures.add(mPool.submit(newBarrierTask(mBarrier, Mode.READ)));
    }
{'fraction_non_alphanumeric': 0.12048076923076922, 'fraction_numerical': 0.0027884615384615383, 'mean_word_length': 3.59205298013245, 'pattern_counts': {'":': 0, '<': 22, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5566141', 'n_tokens_mistral': 3288, 'n_tokens_neox': 3301, 'n_words': 653}