@Test(timeout = 1000)
  public void readers_and_writers_must_block_on_evicting_page() throws Exception {
    // If we have a loaded page ...
    PageSwapper io = new BufferPageSwapper(ByteBuffer.allocate(TEST_PAGE_SIZE));
    long pageId = 12;
    PinnablePage page = table.load(io, pageId, PageLock.EXCLUSIVE);
    monitor.observe(Fault.class);

    // ... a page that will take a long time to evict
    CountDownLatch latch = monitor.trap(is(new Evict(io, pageId)));

    // ... and a page that is soon up for eviction
    page.unpin(PageLock.EXCLUSIVE);

    // ... then when we observe the eviction taking place
    monitor.observe(Evict.class);

    // ... other threads should not be able to pin that page
    Thread pinForShared = fork($pinUnpin(page, io, pageId, PageLock.SHARED));
    Thread pinForExclusive = fork($pinUnpin(page, io, pageId, PageLock.EXCLUSIVE));
    awaitThreadState(pinForShared, Thread.State.WAITING);
    awaitThreadState(pinForExclusive, Thread.State.WAITING);

    // ... until the eviction finishes
    latch.countDown();
    pinForShared.join();
    pinForExclusive.join();
  }
  @Test
  public void must_notify_monitor_of_evicted_pages() throws Exception {
    // If we load a page ...
    PageSwapper io = new BufferPageSwapper(ByteBuffer.allocate(TEST_PAGE_SIZE));
    long pageId = 12;
    PinnablePage page = table.load(io, pageId, PageLock.EXCLUSIVE);
    page.unpin(PageLock.EXCLUSIVE);

    // ... then we should observe its page fault
    assertThat(monitor.observe(Fault.class), is(new Fault(io, pageId)));

    // ... and when it sits idle for long enough, we should observe its eviction
    assertThat(monitor.observe(Evict.class), is(new Evict(io, pageId)));
  }
  @Test
  public void flushing_pages_with_specific_pageio_must_not_race_with_eviction() throws Exception {
    // The idea is that we repeatedly load a page with an EXCLUSIVE lock, and unpin it so it can
    // be evicted. As soon as we have unpinned, we repeatedly try to flush it with our given
    // PageIO. If this causes an exception to be thrown, then we've raced with the eviction
    // where we shouldn't.
    PageSwapper io = new BufferPageSwapper(ByteBuffer.allocate(TEST_PAGE_SIZE));
    long pageId = 12;

    PinnablePage page = table.load(io, pageId, PageLock.EXCLUSIVE);
    monitor.observe(Fault.class);
    page.unpin(PageLock.EXCLUSIVE); // eviction is now possible
    LockSupport.unpark(sweeperThread);

    while (monitor.tryObserve(Evict.class) == null) {
      table.flush(io);
    }
  }