Schedule/Sources/Schedule/TaskCenter.swift

176 lines
4.4 KiB
Swift

import Foundation
private let _default = TaskCenter()
extension TaskCenter {
private class TaskBox: Hashable {
weak var task: Task?
// Used to find slot in dictionary/set
let hash: Int
init(_ task: Task) {
self.task = task
self.hash = task.hashValue
}
func hash(into hasher: inout Hasher) {
hasher.combine(hash)
}
// Used to find task in a slot in dictionary/set
static func == (lhs: TaskBox, rhs: TaskBox) -> Bool {
return lhs.task == rhs.task
}
}
}
/// A task center that enables batch operation.
open class TaskCenter {
private let lock = NSLock()
private var tags: [String: Set<TaskBox>] = [:]
private var tasks: [TaskBox: Set<String>] = [:]
/// Default task center.
open class var `default`: TaskCenter {
return _default
}
/// Adds the given task to this center.
open func add(_ task: Task) {
task.addToTaskCenter(self)
lock.withLockVoid {
let box = TaskBox(task)
self.tasks[box] = []
}
}
/// Removes the given task from this center.
open func remove(_ task: Task) {
task.removeFromTaskCenter(self)
lock.withLockVoid {
let box = TaskBox(task)
if let tags = self.tasks[box] {
for tag in tags {
self.tags[tag]?.remove(box)
if self.tags[tag]?.count == 0 {
self.tags[tag] = nil
}
}
self.tasks[box] = nil
}
}
}
/// Adds a tag to the task.
///
/// If the task is not in this center, do nothing.
open func addTag(_ tag: String, to task: Task) {
addTags([tag], to: task)
}
/// Adds tags to the task.
///
/// If the task is not in this center, do nothing.
open func addTags(_ tags: [String], to task: Task) {
guard task.taskCenter === self else { return }
lock.withLockVoid {
let box = TaskBox(task)
for tag in tags {
tasks[box]?.insert(tag)
if self.tags[tag] == nil {
self.tags[tag] = []
}
self.tags[tag]?.insert(box)
}
}
}
/// Removes a tag from the task.
///
/// If the task is not in this center, do nothing.
open func removeTag(_ tag: String, from task: Task) {
removeTags([tag], from: task)
}
/// Removes tags from the task.
///
/// If the task is not in this center, do nothing.
open func removeTags(_ tags: [String], from task: Task) {
guard task.taskCenter === self else { return }
lock.withLockVoid {
let box = TaskBox(task)
for tag in tags {
self.tasks[box]?.remove(tag)
self.tags[tag]?.remove(box)
if self.tags[tag]?.count == 0 {
self.tags[tag] = nil
}
}
}
}
/// Returns all tags for the task.
///
/// If the task is not in this center, return an empty array.
open func tags(forTask task: Task) -> [String] {
guard task.taskCenter === self else { return [] }
return lock.withLock {
Array(tasks[TaskBox(task)] ?? [])
}
}
/// Returns all tasks for the tag.
open func tasks(forTag tag: String) -> [Task] {
return lock.withLock {
tags[tag]?.compactMap { $0.task } ?? []
}
}
/// Returns all tasks in this center.
open var allTasks: [Task] {
return lock.withLock {
tasks.compactMap { $0.key.task }
}
}
/// Returns all tags in this center.
open var allTags: [String] {
return lock.withLock {
tags.map { $0.key }
}
}
/// Removes all tasks from this center.
open func removeAll() {
lock.withLockVoid {
tasks = [:]
tags = [:]
}
}
/// Suspends all tasks by tag.
open func suspend(byTag tag: String) {
tasks(forTag: tag).forEach { $0.suspend() }
}
/// Resumes all tasks by tag.
open func resume(byTag tag: String) {
tasks(forTag: tag).forEach { $0.resume() }
}
/// Cancels all tasks by tag.
open func cancel(byTag tag: String) {
tasks(forTag: tag).forEach { $0.cancel() }
}
}