Repository: square/okhttp
File: okhttp-testing-support/src/main/kotlin/okhttp3/internal/concurrent/TaskFaker.kt (Kotlin)

Code before:
    taskRunner.lock.withLock {
      val sleepUntil = nanoTime + durationNanos
      yieldUntil { nanoTime >= sleepUntil }
    }
  }

  /**
   * Artificially stall until manually resumed by the test thread with [runTasks]. Use this to
   * simulate races in tasks that doesn't have a deterministic sequence.
   */
  fun yield() {
    taskRunner.assertThreadDoesntHoldLock()
    taskRunner.lock.withLock {
      yieldUntil()
    }
  }

  /**
   * Process the queue until [condition] returns true.
   *
   * @param resumeEagerly true to prioritize the current task over other queued tasks.
   * @param yieldUntilExhausted true to keep yielding until there's no other runnable tasks.
   */
  private tailrec fun yieldUntil(
    resumeEagerly: Boolean = false,
    yieldUntilExhausted: Boolean = false,
    condition: () -> Boolean = { true },
  ) {
    taskRunner.assertThreadHoldsLock()
    val self = currentTask

    val yieldCompleteTask =
      object : SerialTask {
        override fun isReady() = condition()

        override fun start() {
          taskRunner.assertThreadHoldsLock()
          currentTask = self
          taskRunner.condition.signalAll()
        }
      }

    if (resumeEagerly) {
      serialTaskQueue.addFirst(yieldCompleteTask)
    } else {
      serialTaskQueue.addLast(yieldCompleteTask)
    }

    val startedTask = startNextTask()
    val otherTasksStarted = startedTask != yieldCompleteTask


Review comment (suggestion):
It feels like these two parameters should be mutually exclusive; it doesn't make sense to use them both. If a caller _does_ set both to true then I believe `yieldUntilExhausted` will have no effect.

Maybe replace these two params with a single enum parameter?

```
enum class YieldStrategy {
  Eager,          // Prioritizes this task
  Lazy,           // Prioritizes all other currently queued tasks
  UntilExhausted, // Prioritizes all other queued tasks, including new tasks enqueued while waiting
}
```

(wordsmith as needed)

Code after:
  /** Sleep until [durationNanos] elapses. For use by the task threads. */
  fun sleep(durationNanos: Long) {
    taskRunner.lock.withLock {
      val sleepUntil = nanoTime + durationNanos
      yieldUntil { nanoTime >= sleepUntil }
    }
  }

  /**
   * Artificially stall until manually resumed by the test thread with [runTasks]. Use this to
   * simulate races in tasks that doesn't have a deterministic sequence.
   */
  fun yield() {
    taskRunner.assertThreadDoesntHoldLock()
    taskRunner.lock.withLock {
      yieldUntil()
    }
  }

  /** Process the queue until [condition] returns true. */
  private tailrec fun yieldUntil(
    strategy: ResumePriority = ResumePriority.AfterEnqueuedTasks,
    condition: () -> Boolean = { true },
  ) {
    taskRunner.assertThreadHoldsLock()
    val self = currentTask

    val yieldCompleteTask =
      object : SerialTask {
        override fun isReady() = condition()

        override fun start() {
          taskRunner.assertThreadHoldsLock()
          currentTask = self
          taskRunner.condition.signalAll()
        }
      }

    if (strategy == ResumePriority.BeforeOtherTasks) {
      serialTaskQueue.addFirst(yieldCompleteTask)
    } else {
      serialTaskQueue.addLast(yieldCompleteTask)
    }

    val startedTask = startNextTask()
    val otherTasksStarted = startedTask != yieldCompleteTask

    try {
      while (currentTask != self) {
        taskRunner.condition.await()
      }