Skip to content

Commit ef4b5ec

Browse files
committed
Change TaskRunner to limit context switches.
Now we don't have to alternate between the coordinator thread and the task thread between task runs if the task returns 0. Instead the task thread can stay resident. This implementation works by having task runnables that can switch from the coordinator role (sleeping until the next task starts) and the executor role. #5512
1 parent f40fa6d commit ef4b5ec

File tree

10 files changed

+275
-210
lines changed

10 files changed

+275
-210
lines changed

mockwebserver/src/main/java/okhttp3/mockwebserver/MockWebServer.kt

+3
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,9 @@ class MockWebServer : ExternalResource(), Closeable {
969969
val request = readRequest(stream)
970970
atomicRequestCount.incrementAndGet()
971971
requestQueue.add(request)
972+
if (request.failure != null) {
973+
return // Nothing to respond to.
974+
}
972975

973976
val response: MockResponse = dispatcher.dispatch(request)
974977

okhttp/src/main/java/okhttp3/internal/concurrent/Task.kt

-16
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ abstract class Task(
5656
/** Undefined unless this is in [TaskQueue.futureTasks]. */
5757
internal var nextExecuteNanoTime = -1L
5858

59-
internal var runRunnable: Runnable? = null
60-
6159
/** Returns the delay in nanoseconds until the next execution, or -1L to not reschedule. */
6260
abstract fun runOnce(): Long
6361

@@ -66,19 +64,5 @@ abstract class Task(
6664

6765
check(this.queue === null) { "task is in multiple queues" }
6866
this.queue = queue
69-
70-
this.runRunnable = Runnable {
71-
val currentThread = Thread.currentThread()
72-
val oldName = currentThread.name
73-
currentThread.name = name
74-
75-
var delayNanos = -1L
76-
try {
77-
delayNanos = runOnce()
78-
} finally {
79-
queue.runCompleted(this, delayNanos)
80-
currentThread.name = oldName
81-
}
82-
}
8367
}
8468
}

okhttp/src/main/java/okhttp3/internal/concurrent/TaskQueue.kt

+11-56
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,16 @@ import java.util.concurrent.TimeUnit
2828
class TaskQueue internal constructor(
2929
private val taskRunner: TaskRunner
3030
) {
31-
private var shutdown = false
31+
internal var shutdown = false
3232

3333
/** This queue's currently-executing task, or null if none is currently executing. */
34-
private var activeTask: Task? = null
35-
36-
/** True if the [activeTask] should not recur when it completes. */
37-
private var cancelActiveTask = false
34+
internal var activeTask: Task? = null
3835

3936
/** Scheduled tasks ordered by [Task.nextExecuteNanoTime]. */
40-
private val futureTasks = mutableListOf<Task>()
41-
42-
internal fun isActive(): Boolean {
43-
check(Thread.holdsLock(taskRunner))
37+
internal val futureTasks = mutableListOf<Task>()
4438

45-
return activeTask != null || futureTasks.isNotEmpty()
46-
}
39+
/** True if the [activeTask] should be canceled when it completes. */
40+
internal var cancelActiveTask = false
4741

4842
/**
4943
* Returns a snapshot of tasks currently scheduled for execution. Does not include the
@@ -87,7 +81,7 @@ class TaskQueue internal constructor(
8781
fun awaitIdle(delayNanos: Long): Boolean {
8882
val latch = CountDownLatch(1)
8983

90-
val task = object : Task("awaitIdle") {
84+
val task = object : Task("awaitIdle", cancelable = false) {
9185
override fun runOnce(): Long {
9286
latch.countDown()
9387
return -1L
@@ -104,8 +98,8 @@ class TaskQueue internal constructor(
10498
return latch.await(delayNanos, TimeUnit.NANOSECONDS)
10599
}
106100

107-
/** Adds [task] to run in [delayNanos]. Returns true if the coordinator should run. */
108-
private fun scheduleAndDecide(task: Task, delayNanos: Long): Boolean {
101+
/** Adds [task] to run in [delayNanos]. Returns true if the coordinator is impacted. */
102+
internal fun scheduleAndDecide(task: Task, delayNanos: Long): Boolean {
109103
task.initQueue(this)
110104

111105
val now = taskRunner.backend.nanoTime()
@@ -124,7 +118,7 @@ class TaskQueue internal constructor(
124118
if (insertAt == -1) insertAt = futureTasks.size
125119
futureTasks.add(insertAt, task)
126120

127-
// Run the coordinator if we inserted at the front.
121+
// Impact the coordinator if we inserted at the front.
128122
return insertAt == 0
129123
}
130124

@@ -154,8 +148,8 @@ class TaskQueue internal constructor(
154148
}
155149
}
156150

157-
/** Returns true if the coordinator should run. */
158-
private fun cancelAllAndDecide(): Boolean {
151+
/** Returns true if the coordinator is impacted. */
152+
internal fun cancelAllAndDecide(): Boolean {
159153
if (activeTask != null && activeTask!!.cancelable) {
160154
cancelActiveTask = true
161155
}
@@ -169,43 +163,4 @@ class TaskQueue internal constructor(
169163
}
170164
return tasksCanceled
171165
}
172-
173-
/**
174-
* Posts the next available task to an executor for immediate execution.
175-
*
176-
* Returns the delay until the next call to this method, -1L for no further calls, or
177-
* [Long.MAX_VALUE] to wait indefinitely.
178-
*/
179-
internal fun executeReadyTask(now: Long): Long {
180-
check(Thread.holdsLock(taskRunner))
181-
182-
if (activeTask != null) return Long.MAX_VALUE // This queue is busy.
183-
184-
// Check if a task is immediately ready.
185-
val runTask = futureTasks.firstOrNull() ?: return -1L
186-
val delayNanos = runTask.nextExecuteNanoTime - now
187-
if (delayNanos <= 0) {
188-
activeTask = runTask
189-
futureTasks.removeAt(0)
190-
taskRunner.backend.executeTask(runTask.runRunnable!!)
191-
return Long.MAX_VALUE // This queue is busy until the run completes.
192-
}
193-
194-
// Wait until the next task is ready.
195-
return delayNanos
196-
}
197-
198-
internal fun runCompleted(task: Task, delayNanos: Long) {
199-
synchronized(taskRunner) {
200-
check(activeTask === task)
201-
202-
if (delayNanos != -1L && !cancelActiveTask && !shutdown) {
203-
scheduleAndDecide(task, delayNanos)
204-
}
205-
206-
activeTask = null
207-
cancelActiveTask = false
208-
taskRunner.kickCoordinator(this)
209-
}
210-
}
211166
}

0 commit comments

Comments
 (0)