GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — JavaScriptKit/Sources/JavaScriptEventLoop/JobQueue.swift at main · swiftwasm/JavaScriptKit

Expand file tree

/

JobQueue.swift

Copy path
More file actions
More file actions

Latest commit

 

History

History
History
139 lines (120 loc) · 5.01 KB
/

JobQueue.swift

Copy path

File metadata and controls

139 lines (120 loc) · 5.01 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// This file contains the job queue implementation which re-order jobs based on their priority.
// Jobs form runs, one per priority in descending order, FIFO within a run. The first job of each
// run points to its last job, so an insert steps over whole runs.
import _Concurrency
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
fileprivate var isSpinning: Bool = false
}
@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *)
extension JavaScriptEventLoop {
func insertJobQueue(job newJob: UnownedJob) {
withUnsafeMutablePointer(to: &queueState.headJob) { headJobPtr in
var position: UnsafeMutablePointer<UnownedJob?> = headJobPtr
while let runHead = position.pointee, runHead.rawPriority >= newJob.rawPriority {
let runTail = runHead.lastInRun().pointee!
if runHead.rawPriority == newJob.rawPriority {
newJob.nextInQueue().pointee = runTail.nextInQueue().pointee
runTail.nextInQueue().pointee = newJob
runHead.lastInRun().pointee = newJob
return
}
position = runTail.nextInQueue()
}
newJob.nextInQueue().pointee = position.pointee
newJob.lastInRun().pointee = newJob
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)
// The clock is only read while `yieldInterval` is set.
var start: DrainInstant? = yieldInterval == nil ? nil : drainClockNow()
// A clock read costs more than a small job. Read it every `clockStride` jobs, and double
// the stride (up to 256) while the drain is far from the interval.
var clockStride = 1
var jobsUntilClockRead = 1
while let job = self.claimNextFromQueue() {
#if compiler(>=5.9)
job.runSynchronously(on: self.asUnownedSerialExecutor())
#else
job._runSynchronously(on: self.asUnownedSerialExecutor())
#endif
if let yieldInterval, queueState.headJob != nil {
if let start {
jobsUntilClockRead -= 1
if jobsUntilClockRead > 0 { continue }
let elapsed = drainElapsed(since: start)
if elapsed >= yieldInterval {
// Keep `isSpinning` set so new jobs wait for the continuation.
queueHostTask { self.runAllJobs() }
return
}
if elapsed * 4 < yieldInterval {
clockStride = min(clockStride * 2, 256)
}
jobsUntilClockRead = clockStride
} else {
start = drainClockNow()
}
}
}
queueState.isSpinning = false
}
func claimNextFromQueue() -> UnownedJob? {
if let job = self.queueState.headJob {
let nextJob = job.nextInQueue().pointee
if let nextJob {
let runTail = job.lastInRun().pointee!
if !runTail.isSameJob(as: job) {
nextJob.lastInRun().pointee = runTail
}
}
self.queueState.headJob = nextJob
return job
}
return nil
}
}
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
extension UnownedJob {
private func asImpl() -> UnsafeMutablePointer<_CJavaScriptEventLoop.Job> {
unsafeBitCast(self, to: UnsafeMutablePointer<_CJavaScriptEventLoop.Job>.self)
}
fileprivate var flags: JobFlags {
JobFlags(bits: asImpl().pointee.Flags)
}
fileprivate var rawPriority: UInt32 { flags.priority }
fileprivate 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
}
}
/// The last job of the run. Valid only on the first job of a run.
fileprivate func lastInRun() -> UnsafeMutablePointer<UnownedJob?> {
return withUnsafeMutablePointer(to: &asImpl().pointee.SchedulerPrivate.1) { rawLastJobPtr in
UnsafeMutableRawPointer(rawLastJobPtr).bindMemory(to: UnownedJob?.self, capacity: 1)
}
}
fileprivate func isSameJob(as other: UnownedJob) -> Bool {
asImpl() == other.asImpl()
}
}
private struct JobFlags {
var bits: UInt32 = 0
var priority: UInt32 {
(bits & 0xFF00) >> 8
}
}
#endif
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.