Skip to content
This repository was archived by the owner on Feb 2, 2025. It is now read-only.

Commit 9f49620

Browse files
author
Alex Belozierov
committed
- improved performance
- fixed bugs and crashes
1 parent 70b6ab2 commit 9f49620

File tree

8 files changed

+198
-223
lines changed

8 files changed

+198
-223
lines changed

Sources/SwiftCoroutine/Coroutine/CoroutineDispatcher/CoroutineDispatcher.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// Copyright © 2020 Alex Belozierov. All rights reserved.
77
//
88

9-
@usableFromInline protocol _CoroutineTaskExecutor: class {
9+
@usableFromInline protocol CoroutineTaskExecutor: class {
1010

1111
func execute(on scheduler: CoroutineScheduler, task: @escaping () -> Void)
1212

@@ -22,7 +22,7 @@
2222
return CoroutineDispatcher(executor: executor)
2323
}
2424

25-
@usableFromInline let executor: _CoroutineTaskExecutor
25+
@usableFromInline let executor: CoroutineTaskExecutor
2626

2727
@inlinable internal func execute(on scheduler: CoroutineScheduler, task: @escaping () -> Void) {
2828
executor.execute(on: scheduler, task: task)

Sources/SwiftCoroutine/Coroutine/Shared/SharedCoroutineQueue.swift

Lines changed: 0 additions & 67 deletions
This file was deleted.

Sources/SwiftCoroutine/Coroutine/Shared/SharedCoroutine.swift renamed to Sources/SwiftCoroutine/Coroutine/SharedCoroutineDispatcher/SharedCoroutine.swift

Lines changed: 49 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,71 +2,90 @@
22
// SharedCoroutine.swift
33
// SwiftCoroutine
44
//
5-
// Created by Alex Belozierov on 09.03.2020.
5+
// Created by Alex Belozierov on 03.04.2020.
66
// Copyright © 2020 Alex Belozierov. All rights reserved.
77
//
88

9-
#if SWIFT_PACKAGE
10-
import CCoroutine
11-
#endif
12-
13-
internal final class SharedCoroutine: CoroutineProtocol {
9+
internal final class SharedCoroutine {
10+
11+
internal typealias CompletionState = SharedCoroutineQueue.CompletionState
1412

1513
private enum State: Int {
16-
case running, suspending, suspended, restarting
14+
case running, suspending, suspended, restarting, finished
1715
}
1816

1917
private struct StackBuffer {
2018
let stack: UnsafeMutableRawPointer, size: Int
2119
}
2220

23-
private let dispatcher: SharedCoroutineDispatcher
21+
internal let dispatcher: SharedCoroutineDispatcher
2422
internal let queue: SharedCoroutineQueue
2523
private(set) var scheduler: CoroutineScheduler
26-
private(set) var environment: UnsafeMutablePointer<CoroutineContext.SuspendData>!
27-
private var stackBuffer: StackBuffer!
2824
private var state = AtomicEnum(value: State.running)
2925

26+
private var environment: UnsafeMutablePointer<CoroutineContext.SuspendData>!
27+
private var stackBuffer: StackBuffer!
28+
3029
internal init(dispatcher: SharedCoroutineDispatcher, queue: SharedCoroutineQueue, scheduler: CoroutineScheduler) {
3130
self.dispatcher = dispatcher
3231
self.queue = queue
3332
self.scheduler = scheduler
3433
}
3534

36-
// MARK: - actions
35+
// MARK: - Actions
36+
37+
internal func start() -> CompletionState {
38+
performAsCurrent { perform(queue.context.start) }
39+
}
40+
41+
internal func resume() -> CompletionState {
42+
performAsCurrent(resumeContext)
43+
}
3744

38-
internal func start(_ task: @escaping () -> Void) {
39-
performAsCurrent { perform { queue.start(task) } }
45+
private func resumeContext() -> CompletionState {
46+
perform { queue.context.resume(from: environment.pointee.env) }
4047
}
4148

42-
internal func resume() {
43-
performAsCurrent { perform { queue.resume(self) } }
49+
private func perform(_ block: () -> Bool) -> CompletionState {
50+
if block() { return .finished }
51+
switch state.update(.suspended) {
52+
case .running: return resumeContext()
53+
case .restarting: return .restarting
54+
default: return .suspended
55+
}
4456
}
4557

4658
private func suspend() {
4759
if environment == nil {
4860
environment = .allocate(capacity: 1)
4961
environment.initialize(to: .init())
5062
}
51-
queue.suspend(self)
63+
queue.context.suspend(to: environment)
5264
}
5365

54-
private func perform(_ block: () -> Bool) {
55-
queue.isFree = true
56-
if block() { return }
57-
switch state.update(.suspended) {
58-
case .running:
59-
state.value = .running
60-
perform { queue.resume(self) }
61-
case .restarting:
62-
state.value = .running
63-
queue.isFree = false
64-
dispatcher.restart(self)
65-
default: break
66-
}
66+
// MARK: - Stack
67+
68+
internal func saveStack() {
69+
let size = environment.pointee.sp.distance(to: queue.context.stackTop)
70+
let stack = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: 16)
71+
stack.copyMemory(from: environment.pointee.sp, byteCount: size)
72+
stackBuffer = .init(stack: stack, size: size)
6773
}
6874

69-
// MARK: - await
75+
internal func restoreStack() {
76+
environment.pointee.sp.copyMemory(from: stackBuffer.stack, byteCount: stackBuffer.size)
77+
stackBuffer.stack.deallocate()
78+
stackBuffer = nil
79+
}
80+
81+
deinit {
82+
environment?.pointee.env.deallocate()
83+
environment?.deallocate()
84+
}
85+
86+
}
87+
88+
extension SharedCoroutine: CoroutineProtocol {
7089

7190
internal func await<T>(_ callback: (@escaping (T) -> Void) -> Void) -> T {
7291
state.value = .suspending
@@ -95,26 +114,4 @@ internal final class SharedCoroutine: CoroutineProtocol {
95114
suspend()
96115
}
97116

98-
deinit {
99-
environment?.pointee.env.deallocate()
100-
environment?.deallocate()
101-
}
102-
103-
}
104-
105-
extension SharedCoroutine {
106-
107-
internal func saveStack() {
108-
let size = environment.pointee.sp.distance(to: queue.context.stackTop)
109-
let stack = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: 16)
110-
stack.copyMemory(from: environment.pointee.sp, byteCount: size)
111-
stackBuffer = .init(stack: stack, size: size)
112-
}
113-
114-
internal func restoreStack() {
115-
environment.pointee.sp.copyMemory(from: stackBuffer.stack, byteCount: stackBuffer.size)
116-
stackBuffer.stack.deallocate()
117-
stackBuffer = nil
118-
}
119-
120117
}

Sources/SwiftCoroutine/Coroutine/Shared/SharedCoroutineDispatcher.swift renamed to Sources/SwiftCoroutine/Coroutine/SharedCoroutineDispatcher/SharedCoroutineDispatcher.swift

Lines changed: 29 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,25 @@
22
// SharedCoroutineDispatcher.swift
33
// SwiftCoroutine
44
//
5-
// Created by Alex Belozierov on 09.03.2020.
5+
// Created by Alex Belozierov on 03.04.2020.
66
// Copyright © 2020 Alex Belozierov. All rights reserved.
77
//
88

99
import Dispatch
1010

11-
internal final class SharedCoroutineDispatcher: _CoroutineTaskExecutor {
11+
internal final class SharedCoroutineDispatcher: CoroutineTaskExecutor {
1212

13-
private struct Task {
13+
internal struct Task {
1414
let scheduler: CoroutineScheduler, task: () -> Void
1515
}
1616

1717
private let mutex = PsxLock()
1818
private let stackSize: Int
19+
private var tasks = FifoQueue<Task>()
20+
1921
private var contextsCount: Int
2022
private var freeQueues = [SharedCoroutineQueue]()
2123
private var suspendedQueues = Set<SharedCoroutineQueue>()
22-
private var tasks = FifoQueue<Task>()
2324
private var freeCount: AtomicInt
2425

2526
internal init(contextsCount: Int, stackSize: Int) {
@@ -31,14 +32,15 @@ internal final class SharedCoroutineDispatcher: _CoroutineTaskExecutor {
3132
startDispatchSource()
3233
}
3334

35+
// MARK: - Start
36+
3437
internal func execute(on scheduler: CoroutineScheduler, task: @escaping () -> Void) {
3538
func perform() {
3639
freeCount.update { max(0, $0 - 1) }
3740
mutex.lock()
3841
if let queue = freeQueue {
3942
mutex.unlock()
40-
SharedCoroutine(dispatcher: self, queue: queue, scheduler: scheduler).start(task)
41-
performNext(for: queue)
43+
queue.start(dispatcher: self, task: .init(scheduler: scheduler, task: task))
4244
} else {
4345
tasks.push(.init(scheduler: scheduler, task: task))
4446
mutex.unlock()
@@ -58,8 +60,7 @@ internal final class SharedCoroutineDispatcher: _CoroutineTaskExecutor {
5860
if let queue = freeQueues.popLast() { return queue }
5961
if contextsCount > 0 {
6062
contextsCount -= 1
61-
let context = CoroutineContext(stackSize: stackSize)
62-
return SharedCoroutineQueue(context: context)
63+
return SharedCoroutineQueue(stackSize: stackSize)
6364
} else if suspendedQueues.count < 2 {
6465
return suspendedQueues.popFirst()
6566
}
@@ -74,28 +75,9 @@ internal final class SharedCoroutineDispatcher: _CoroutineTaskExecutor {
7475
return suspendedQueues.remove(min)
7576
}
7677

78+
// MARK: - Resume
79+
7780
internal func resume(_ coroutine: SharedCoroutine) {
78-
// func perform() {
79-
// mutex.lock()
80-
// if suspendedQueues.remove(coroutine.queue) == nil {
81-
// coroutine.queue.push(coroutine)
82-
// mutex.unlock()
83-
// } else {
84-
// freeCount.decrease()
85-
// mutex.unlock()
86-
// coroutine.resume()
87-
// performNext(for: coroutine.queue)
88-
// }
89-
// }
90-
// mutex.lock()
91-
// if suspendedQueues.contains(coroutine.queue) {
92-
// mutex.unlock()
93-
// coroutine.scheduler.scheduleTask(perform)
94-
// } else {
95-
// coroutine.queue.push(coroutine)
96-
// mutex.unlock()
97-
// }
98-
9981
mutex.lock()
10082
if suspendedQueues.remove(coroutine.queue) == nil {
10183
coroutine.queue.push(coroutine)
@@ -104,54 +86,33 @@ internal final class SharedCoroutineDispatcher: _CoroutineTaskExecutor {
10486
mutex.unlock()
10587
freeCount.decrease()
10688
coroutine.scheduler.scheduleTask {
107-
coroutine.resume()
108-
self.performNext(for: coroutine.queue)
89+
coroutine.queue.resume(coroutine: coroutine)
10990
}
11091
}
11192
}
11293

113-
internal func restart(_ coroutine: SharedCoroutine) {
114-
coroutine.scheduler.scheduleTask {
115-
coroutine.resume()
116-
self.performNext(for: coroutine.queue)
117-
}
118-
}
94+
// MARK: - Next
11995

120-
private enum NextState: Int {
121-
case running, none
122-
}
123-
124-
private func performNext(for queue: SharedCoroutineQueue) {
125-
var state = AtomicEnum(value: NextState.none)
126-
while queue.isFree {
127-
mutex.lock()
128-
if let coroutine = queue.pop() {
129-
mutex.unlock()
130-
state.value = .running
131-
coroutine.scheduler.scheduleTask {
132-
coroutine.resume()
133-
if state.update(.none) == .running { return }
134-
self.performNext(for: queue)
135-
}
136-
} else if let task = tasks.pop() {
137-
mutex.unlock()
138-
state.value = .running
139-
task.scheduler.scheduleTask {
140-
SharedCoroutine(dispatcher: self, queue: queue, scheduler: task.scheduler)
141-
.start(task.task)
142-
if state.update(.none) == .running { return }
143-
self.performNext(for: queue)
144-
}
145-
} else if queue.started == 0 {
96+
internal func performNext(for queue: SharedCoroutineQueue) {
97+
mutex.lock()
98+
if let coroutine = queue.pop() {
99+
mutex.unlock()
100+
coroutine.scheduler.scheduleTask {
101+
queue.resume(coroutine: coroutine)
102+
}
103+
} else if let task = tasks.pop() {
104+
mutex.unlock()
105+
task.scheduler.scheduleTask {
106+
queue.start(dispatcher: self, task: task)
107+
}
108+
} else {
109+
if queue.started == 0 {
146110
freeQueues.append(queue)
147-
freeCount.increase()
148-
return mutex.unlock()
149111
} else {
150112
suspendedQueues.insert(queue)
151-
freeCount.increase()
152-
return mutex.unlock()
153113
}
154-
if state.update(.none) == .running { return }
114+
freeCount.increase()
115+
mutex.unlock()
155116
}
156117
}
157118

0 commit comments

Comments
 (0)