Skip to content

Refactor Job Queue Implementation for Improved Priority Handling and Cleanliness #275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 46 additions & 39 deletions Sources/JavaScriptEventLoop/JobQueue.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
// 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 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
struct QueueState: Sendable {
fileprivate var headJob: UnownedJob? = nil
Expand All @@ -14,51 +10,66 @@ struct QueueState: Sendable {

@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *)
extension JavaScriptEventLoop {
private var queueLock: NSLock {
NSLock()
}

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
}
queueLock.lock()
defer { queueLock.unlock() }

insertJob(newJob)

// TODO: use CAS when supporting multi-threaded environment
if !queueState.isSpinning {
self.queueState.isSpinning = true
queueState.isSpinning = true
JavaScriptEventLoop.shared.queueMicrotask {
self.runAllJobs()
}
}
}

private func insertJob(_ newJob: UnownedJob) {
var current = queueState.headJob
var previous: UnownedJob? = nil

while let cur = current, cur.rawPriority >= newJob.rawPriority {
previous = cur
current = cur.nextInQueue().pointee
}

newJob.nextInQueue().pointee = current
if let prev = previous {
prev.nextInQueue().pointee = newJob
} else {
queueState.headJob = newJob
}
}

func runAllJobs() {
assert(queueState.isSpinning)

while let job = self.claimNextFromQueue() {
#if compiler(>=5.9)
job.runSynchronously(on: self.asUnownedSerialExecutor())
#else
job._runSynchronously(on: self.asUnownedSerialExecutor())
#endif
while let job = claimNextFromQueue() {
executeJob(job)
}

queueState.isSpinning = false
}

private func executeJob(_ job: UnownedJob) {
#if compiler(>=5.9)
job.runSynchronously(on: self.asUnownedSerialExecutor())
#else
job._runSynchronously(on: self.asUnownedSerialExecutor())
#endif
}

func claimNextFromQueue() -> UnownedJob? {
if let job = self.queueState.headJob {
self.queueState.headJob = job.nextInQueue().pointee
return job
}
return nil
queueLock.lock()
defer { queueLock.unlock() }

guard let job = queueState.headJob else { return nil }
queueState.headJob = job.nextInQueue().pointee
return job
}
}

Expand All @@ -75,21 +86,17 @@ fileprivate extension UnownedJob {
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
withUnsafeMutablePointer(to: &asImpl().pointee.SchedulerPrivate.0) { rawNextJobPtr in
UnsafeMutableRawPointer(rawNextJobPtr).bindMemory(to: UnownedJob?.self, capacity: 1)
}
}

}

fileprivate struct JobFlags {
var bits: UInt32 = 0

var priority: UInt32 {
get {
(bits & 0xFF00) >> 8
var priority: UInt32 {
(bits & 0xFF00) >> 8
}
}
}
#endif
#endif
Loading