Last active
March 20, 2021 21:11
-
-
Save jwamin/58460ca50ebe42e885b5313ccdbe3b53 to your computer and use it in GitHub Desktop.
Login/Logout TaskManager
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Cocoa | |
//:Requirements | |
//1. Class that does jobs | |
//2. Keeps jobs in queue | |
//3. Perform Jobs in queue | |
//4. Can be torn down | |
//5. Can be switched between modes .anon, .loggedIn | |
//6. Changing modes causes tear down and build up | |
//7. Jobs are resumed | |
class TaskManager { | |
//: Internal Types | |
internal enum User { | |
case anon | |
case user(name: String) | |
} | |
internal class PendingTask: Hashable, CustomStringConvertible { | |
//: CSC confromance | |
var description: String { | |
"task \(id) - user: \(authentication)" | |
} | |
//: Hashable Confromance | |
let id = UUID() | |
static func == (lhs: TaskManager.PendingTask, rhs: TaskManager.PendingTask) -> Bool { | |
lhs.id == rhs.id | |
} | |
func hash(into hasher: inout Hasher) { | |
hasher.combine(id) | |
} | |
let block: (Bool) -> Void | |
let complete: Bool = false | |
let authentication: User | |
init(userSpace: User = .anon, block: @escaping (Bool) -> Void) { | |
self.block = block | |
authentication = userSpace | |
} | |
} | |
public private(set) var user: User = .anon | |
public private(set) var destroyed = true { | |
didSet{ | |
if !destroyed { | |
tasksToComplete += pendingTasks | |
pendingTasks.removeAll() | |
} | |
} | |
} | |
//background queue for task execution | |
private var taskQueue = DispatchQueue(label: "taskQueue",attributes: .concurrent) | |
//track all tasks done | |
private var dispatchGrp = DispatchGroup() | |
private init(){ | |
destroyed = false | |
dispatchGrp.notify(qos: .default, flags: .barrier, queue: taskQueue) { | |
print("done") | |
} | |
} | |
public static let shared = TaskManager() | |
//tasks in this queue will execute when the manager resumes (appending to tasksToComplete) | |
public var pendingTasks = Set<PendingTask>() { | |
didSet { | |
print("pendingTasks now \(pendingTasks )") | |
} | |
} | |
//tasks in this queue will ececute serially | |
public private(set) var tasksToComplete = Array<PendingTask>() { | |
didSet{ | |
print("tasks count now \(tasksToComplete.count)") | |
} | |
} | |
func setup(user: User) { | |
self.destroyed = true | |
self.user = user | |
dispatchGrp.enter() | |
if case .user(let name) = user { | |
print("\n\nreinitilizinge with \(name)\nwaiting 2 second\n") | |
} | |
//test to see if execution of queue stops | |
DispatchQueue.main.asyncAfter(deadline: .now() + 5){ [weak self] in | |
print("\nresuming...\n") | |
self?.destroyed = false | |
self?.continueTasks() | |
self?.taskComplete() | |
} | |
} | |
func continueTasks(){ | |
if self.destroyed { | |
print("we are destroyed, rebuild") | |
return | |
} | |
if self.tasksToComplete.isEmpty { | |
precondition(pendingTasks.isEmpty) | |
print("we are empty, end") | |
return | |
} | |
let pendingTask = self.tasksToComplete.removeFirst() | |
dispatchGrp.enter() | |
taskQueue.async(flags:.barrier){ [weak self] in | |
if self!.destroyed { | |
self?.tasksToComplete.append(pendingTask) | |
return | |
} | |
if case .user(let name) = pendingTask.authentication { | |
print("LOGGED IN - performing \(pendingTask) for logged in user \(name)") | |
pendingTask.block(true) | |
self?.taskComplete() | |
} else { | |
print("LOGGED OUT - performing \(pendingTask) for anonymous user") | |
pendingTask.block(false) | |
self?.taskComplete() | |
} | |
if !self!.tasksToComplete.isEmpty { | |
DispatchQueue.main.async { | |
self!.continueTasks() | |
} | |
} | |
} | |
} | |
func performTask(task: PendingTask) { | |
task.block | |
} | |
func addTask(task: @escaping (Bool) -> Void){ | |
let queuedItem = PendingTask(userSpace: self.user, block: task) | |
if self.destroyed { | |
pendingTasks.insert(queuedItem) | |
print("added \(queuedItem) to pending tasks") | |
return | |
} | |
tasksToComplete.append(queuedItem) | |
continueTasks() | |
} | |
//notify dispatch queue that tasks are complete | |
public func taskComplete(){ | |
dispatchGrp.leave() | |
} | |
deinit{ | |
print("task manager deinitialized") | |
} | |
} | |
//Testing | |
for call in 0...5 { | |
TaskManager.shared.addTask { returnedValue in | |
let timeout = Int.random(in: 0...10) | |
Thread.sleep(until: Date().addingTimeInterval(2)) | |
print("\(call): returnedValue \(returnedValue) timeout was: \(timeout)") | |
} | |
} | |
let blockingQueue = DispatchQueue(label: "blockingQueue", qos: .default, attributes: .concurrent) | |
blockingQueue.asyncAfter(deadline: .now() + Double(5)) { | |
print("\n\nexecution should stop\n\n") | |
TaskManager.shared.setup(user: .user(name: "jossy")) | |
var secondCount = 0 | |
for call in 0...5 { | |
let timeout = Int.random(in: 0...5) | |
blockingQueue.asyncAfter(deadline: .now() + Double(timeout),flags: .barrier ) { | |
TaskManager.shared.addTask { returnedValue in | |
print("\(call): returnedValue \(returnedValue)") | |
} | |
} | |
secondCount += 1 | |
} | |
print("\ntasks registered for logged in: \(secondCount)\n") | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment