Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (C) 2019 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.concurrent
import java.util.concurrent.CountDownLatch
import java.util.concurrent.RejectedExecutionException
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
import okhttp3.internal.assertNotHeld
import okhttp3.internal.okHttpName
/**
* A set of tasks that are executed in sequential order.
*
* Work within queues is not concurrent. This is equivalent to each queue having a dedicated thread
* for its work; in practice a set of queues may share a set of threads to save resources.
*/
class TaskQueue internal constructor(
internal val taskRunner: TaskRunner,
internal val name: String,
) {
val lock: ReentrantLock = ReentrantLock()
internal var shutdown = false
/** This queue's currently-executing task, or null if none is currently executing. */
internal var activeTask: Task? = null
/** Scheduled tasks ordered by [Task.nextExecuteNanoTime]. */
internal val futureTasks = mutableListOf()
/** True if the [activeTask] should be canceled when it completes. */
internal var cancelActiveTask = false
/**
* Returns a snapshot of tasks currently scheduled for execution. Does not include the
* currently-executing task unless it is also scheduled for future execution.
*/
val scheduledTasks: List
get() = taskRunner.lock.withLock { futureTasks.toList() }
/**
* Schedules [task] for execution in [delayNanos]. A task may only have one future execution
* scheduled. If the task is already in the queue, the earliest execution time is used.
*
* The target execution time is implemented on a best-effort basis. If another task in this queue
* is running when that time is reached, that task is allowed to complete before this task is
* started. Similarly the task will be delayed if the host lacks compute resources.
*
* @throws RejectedExecutionException if the queue is shut down and the task is not cancelable.
*/
fun schedule(
task: Task,
delayNanos: Long = 0L,
) {
taskRunner.lock.withLock {
if (shutdown) {
if (task.cancelable) {
taskRunner.logger.taskLog(task, this) { "schedule canceled (queue is shutdown)" }
return
}
taskRunner.logger.taskLog(task, this) { "schedule failed (queue is shutdown)" }
throw RejectedExecutionException()
}
if (scheduleAndDecide(task, delayNanos, recurrence = false)) {
taskRunner.kickCoordinator(this)
}
}
}
/**
* Overload of [schedule] that uses a lambda for a repeating task.
*
* TODO: make this inline once this is fixed: https://github.com/oracle/graal/issues/3466
*/
fun schedule(
name: String,
delayNanos: Long = 0L,
block: () -> Long,
) {
schedule(
object : Task(name) {
override fun runOnce(): Long {
return block()
}
},
delayNanos,
)
}
/**
* Executes [block] once on a task runner thread.
*
* TODO: make this inline once this is fixed: https://github.com/oracle/graal/issues/3466
*/
fun execute(
name: String,
delayNanos: Long = 0L,
cancelable: Boolean = true,
block: () -> Unit,
) {
schedule(
object : Task(name, cancelable) {
override fun runOnce(): Long {
block()
return -1L
}
},
delayNanos,
)
}
/** Returns a latch that reaches 0 when the queue is next idle. */
fun idleLatch(): CountDownLatch {
taskRunner.lock.withLock {
// If the queue is already idle, that's easy.
if (activeTask == null && futureTasks.isEmpty()) {
return CountDownLatch(0)
}
// If there's an existing AwaitIdleTask, use it. This is necessary when the executor is
// shutdown but still busy as we can't enqueue in that case.
val existingTask = activeTask
if (existingTask is AwaitIdleTask) {
return existingTask.latch
}
for (futureTask in futureTasks) {
if (futureTask is AwaitIdleTask) {
return futureTask.latch
}
}
// Don't delegate to schedule() because that enforces shutdown rules.
val newTask = AwaitIdleTask()
if (scheduleAndDecide(newTask, 0L, recurrence = false)) {
taskRunner.kickCoordinator(this)
}
return newTask.latch
}
}
private class AwaitIdleTask : Task("$okHttpName awaitIdle", cancelable = false) {
val latch = CountDownLatch(1)
override fun runOnce(): Long {
latch.countDown()
return -1L
}
}
/** Adds [task] to run in [delayNanos]. Returns true if the coordinator is impacted. */
internal fun scheduleAndDecide(
task: Task,
delayNanos: Long,
recurrence: Boolean,
): Boolean {
task.initQueue(this)
val now = taskRunner.backend.nanoTime()
val executeNanoTime = now + delayNanos
// If the task is already scheduled, take the earlier of the two times.
val existingIndex = futureTasks.indexOf(task)
if (existingIndex != -1) {
if (task.nextExecuteNanoTime <= executeNanoTime) {
taskRunner.logger.taskLog(task, this) { "already scheduled" }
return false
}
futureTasks.removeAt(existingIndex) // Already scheduled later: reschedule below!
}
task.nextExecuteNanoTime = executeNanoTime
taskRunner.logger.taskLog(task, this) {
if (recurrence) {
"run again after ${formatDuration(executeNanoTime - now)}"
} else {
"scheduled after ${formatDuration(executeNanoTime - now)}"
}
}
// Insert in chronological order. Always compare deltas because nanoTime() is permitted to wrap.
var insertAt = futureTasks.indexOfFirst { it.nextExecuteNanoTime - now > delayNanos }
if (insertAt == -1) insertAt = futureTasks.size
futureTasks.add(insertAt, task)
// Impact the coordinator if we inserted at the front.
return insertAt == 0
}
/**
* Schedules immediate execution of [Task.tryCancel] on all currently-enqueued tasks. These calls
* will not be made until any currently-executing task has completed. Tasks that return true will
* be removed from the execution schedule.
*/
fun cancelAll() {
lock.assertNotHeld()
taskRunner.lock.withLock {
if (cancelAllAndDecide()) {
taskRunner.kickCoordinator(this)
}
}
}
fun shutdown() {
lock.assertNotHeld()
taskRunner.lock.withLock {
shutdown = true
if (cancelAllAndDecide()) {
taskRunner.kickCoordinator(this)
}
}
}
/** Returns true if the coordinator is impacted. */
internal fun cancelAllAndDecide(): Boolean {
if (activeTask != null && activeTask!!.cancelable) {
cancelActiveTask = true
}
var tasksCanceled = false
for (i in futureTasks.size - 1 downTo 0) {
if (futureTasks[i].cancelable) {
taskRunner.logger.taskLog(futureTasks[i], this) { "canceled" }
tasksCanceled = true
futureTasks.removeAt(i)
}
}
return tasksCanceled
}
override fun toString(): String = name
}