void afterWrite(Runnable task) {
    for (int i = 0; i < WRITE_BUFFER_RETRIES; i++) {
      if (writeBuffer.offer(task)) {
        scheduleAfterWrite();
        return;
      }
      scheduleDrainBuffers();
      Thread.onSpinWait();
    }

    // In scenarios where the writing threads cannot make progress then they attempt to provide
    // assistance by performing the eviction work directly. This can resolve cases where the
    // maintenance task is scheduled but not running. That might occur due to all of the executor's
    // threads being busy (perhaps writing into this cache), the write rate greatly exceeds the
    // consuming rate, priority inversion, or if the executor silently discarded the maintenance
    // task. Unfortunately this cannot resolve when the eviction is blocked waiting on a long-
    // running computation due to an eviction listener, the victim is being computed on by a writer,
    // or the victim residing in the same hash bin as a computing entry. In those cases a warning is
    // logged to encourage the application to decouple these computations from the map operations.
    lock();
    try {
      maintenance(task);
    } catch (RuntimeException e) {
      logger.log(Level.ERROR, "Exception thrown when performing the maintenance task", e);
    } finally {
      evictionLock.unlock();
    }
    rescheduleCleanUpIfIncomplete();
  }
@Test(dataProvider = "caches")
  @CacheSpec(population = Population.EMPTY)
  public void afterWrite_drainFullWriteBuffer(
      BoundedLocalCache<Int, Int> cache, CacheContext context) {
    cache.drainStatus = PROCESSING_TO_IDLE;

    int[] queued = { 0 };
    Runnable pendingTask = () -> queued[0]++;

    for (int i = 0; i < WRITE_BUFFER_MAX; i++) {
      cache.afterWrite(pendingTask);
    }
    assertThat(cache.drainStatus).isEqualTo(PROCESSING_TO_REQUIRED);

    int[] triggered = { 0 };
    Runnable triggerTask = () -> triggered[0] = WRITE_BUFFER_MAX + 1;
    cache.afterWrite(triggerTask);

    assertThat(cache.drainStatus).isEqualTo(IDLE);
    assertThat(cache.evictionLock.isLocked()).isFalse();
    assertThat(queued[0]).isEqualTo(WRITE_BUFFER_MAX);
    assertThat(triggered[0]).isEqualTo(WRITE_BUFFER_MAX + 1);
  }