11// This file contains the job queue implementation which re-order jobs based on their priority.
2- // The current implementation is much simple to be easily debugged, but should be re-implemented
3- // using priority queue ideally.
42
53import _CJavaScriptEventLoop
64
7- #if compiler(>=5.5)
8-
95@available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
106struct QueueState : Sendable {
117 fileprivate var headJob : UnownedJob ? = nil
@@ -14,51 +10,66 @@ struct QueueState: Sendable {
1410
1511@available ( macOS 14 . 0 , iOS 17 . 0 , watchOS 10 . 0 , tvOS 17 . 0 , * )
1612extension JavaScriptEventLoop {
13+ private var queueLock : NSLock {
14+ NSLock ( )
15+ }
1716
1817 func insertJobQueue( job newJob: UnownedJob ) {
19- withUnsafeMutablePointer ( to: & queueState. headJob) { headJobPtr in
20- var position : UnsafeMutablePointer < UnownedJob ? > = headJobPtr
21- while let cur = position. pointee {
22- if cur. rawPriority < newJob. rawPriority {
23- newJob. nextInQueue ( ) . pointee = cur
24- position. pointee = newJob
25- return
26- }
27- position = cur. nextInQueue ( )
28- }
29- newJob. nextInQueue ( ) . pointee = nil
30- position. pointee = newJob
31- }
18+ queueLock. lock ( )
19+ defer { queueLock. unlock ( ) }
20+
21+ insertJob ( newJob)
3222
33- // TODO: use CAS when supporting multi-threaded environment
3423 if !queueState. isSpinning {
35- self . queueState. isSpinning = true
24+ queueState. isSpinning = true
3625 JavaScriptEventLoop . shared. queueMicrotask {
3726 self . runAllJobs ( )
3827 }
3928 }
4029 }
4130
31+ private func insertJob( _ newJob: UnownedJob ) {
32+ var current = queueState. headJob
33+ var previous : UnownedJob ? = nil
34+
35+ while let cur = current, cur. rawPriority >= newJob. rawPriority {
36+ previous = cur
37+ current = cur. nextInQueue ( ) . pointee
38+ }
39+
40+ newJob. nextInQueue ( ) . pointee = current
41+ if let prev = previous {
42+ prev. nextInQueue ( ) . pointee = newJob
43+ } else {
44+ queueState. headJob = newJob
45+ }
46+ }
47+
4248 func runAllJobs( ) {
4349 assert ( queueState. isSpinning)
4450
45- while let job = self . claimNextFromQueue ( ) {
46- #if compiler(>=5.9)
47- job. runSynchronously ( on: self . asUnownedSerialExecutor ( ) )
48- #else
49- job. _runSynchronously ( on: self . asUnownedSerialExecutor ( ) )
50- #endif
51+ while let job = claimNextFromQueue ( ) {
52+ executeJob ( job)
5153 }
5254
5355 queueState. isSpinning = false
5456 }
5557
58+ private func executeJob( _ job: UnownedJob ) {
59+ #if compiler(>=5.9)
60+ job. runSynchronously ( on: self . asUnownedSerialExecutor ( ) )
61+ #else
62+ job. _runSynchronously ( on: self . asUnownedSerialExecutor ( ) )
63+ #endif
64+ }
65+
5666 func claimNextFromQueue( ) -> UnownedJob ? {
57- if let job = self . queueState. headJob {
58- self . queueState. headJob = job. nextInQueue ( ) . pointee
59- return job
60- }
61- return nil
67+ queueLock. lock ( )
68+ defer { queueLock. unlock ( ) }
69+
70+ guard let job = queueState. headJob else { return nil }
71+ queueState. headJob = job. nextInQueue ( ) . pointee
72+ return job
6273 }
6374}
6475
@@ -75,21 +86,17 @@ fileprivate extension UnownedJob {
7586 var rawPriority : UInt32 { flags. priority }
7687
7788 func nextInQueue( ) -> UnsafeMutablePointer < UnownedJob ? > {
78- return withUnsafeMutablePointer ( to: & asImpl( ) . pointee. SchedulerPrivate. 0 ) { rawNextJobPtr in
79- let nextJobPtr = UnsafeMutableRawPointer ( rawNextJobPtr) . bindMemory ( to: UnownedJob ? . self, capacity: 1 )
80- return nextJobPtr
89+ withUnsafeMutablePointer ( to: & asImpl( ) . pointee. SchedulerPrivate. 0 ) { rawNextJobPtr in
90+ UnsafeMutableRawPointer ( rawNextJobPtr) . bindMemory ( to: UnownedJob ? . self, capacity: 1 )
8191 }
8292 }
83-
8493}
8594
8695fileprivate struct JobFlags {
8796 var bits : UInt32 = 0
8897
89- var priority : UInt32 {
90- get {
91- ( bits & 0xFF00 ) >> 8
98+ var priority : UInt32 {
99+ ( bits & 0xFF00 ) >> 8
92100 }
93- }
94101}
95- #endif
102+ #endif
0 commit comments