-
-
Notifications
You must be signed in to change notification settings - Fork 51
/
Copy pathJobQueue.swift
91 lines (74 loc) · 2.68 KB
/
JobQueue.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// This file contains the job queue implementation which re-order jobs based on their priority.
// The current implementation is much simple to be easily debugged, but should be re-implemented
// using priority queue ideally.
import _CJavaScriptEventLoop
#if compiler(>=5.5)
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
struct QueueState: Sendable {
fileprivate var headJob: UnownedJob? = nil
fileprivate var isSpinning: Bool = false
}
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
extension JavaScriptEventLoop {
func insertJobQueue(job newJob: UnownedJob) {
withUnsafeMutablePointer(to: &queueState.headJob) { headJobPtr in
var position: UnsafeMutablePointer<UnownedJob?> = headJobPtr
while let cur = position.pointee {
if cur.rawPriority < newJob.rawPriority {
newJob.nextInQueue().pointee = cur
position.pointee = newJob
return
}
position = cur.nextInQueue()
}
newJob.nextInQueue().pointee = nil
position.pointee = newJob
}
// TODO: use CAS when supporting multi-threaded environment
if !queueState.isSpinning {
self.queueState.isSpinning = true
JavaScriptEventLoop.shared.queueMicrotask {
self.runAllJobs()
}
}
}
func runAllJobs() {
assert(queueState.isSpinning)
while let job = self.claimNextFromQueue() {
job._runSynchronously(on: self.asUnownedSerialExecutor())
}
queueState.isSpinning = false
}
func claimNextFromQueue() -> UnownedJob? {
if let job = self.queueState.headJob {
self.queueState.headJob = job.nextInQueue().pointee
return job
}
return nil
}
}
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
fileprivate extension UnownedJob {
private func asImpl() -> UnsafeMutablePointer<_CJavaScriptEventLoop.Job> {
unsafeBitCast(self, to: UnsafeMutablePointer<_CJavaScriptEventLoop.Job>.self)
}
var flags: JobFlags {
JobFlags(bits: asImpl().pointee.Flags)
}
var rawPriority: UInt32 { flags.priority }
func nextInQueue() -> UnsafeMutablePointer<UnownedJob?> {
return withUnsafeMutablePointer(to: &asImpl().pointee.SchedulerPrivate.0) { rawNextJobPtr in
let nextJobPtr = UnsafeMutableRawPointer(rawNextJobPtr).bindMemory(to: UnownedJob?.self, capacity: 1)
return nextJobPtr
}
}
}
fileprivate struct JobFlags {
var bits: UInt32 = 0
var priority: UInt32 {
get {
(bits & 0xFF00) >> 8
}
}
}
#endif