Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c439d12

Browse filesBrowse files
authored
Modern concurrency support (#175)
* Add @mainactor and Sendable annotations Enable strict concurrency Get backends to compile and update GTK backends Mac-specific fixes More sendability improvements (please tell me this compiles on Windows) I love waiting 15 minutes for CI to compile a 1-line change * Make Image Sendable use concurrency-annotated version of ImageFormats * Post-rebase fixes * Add more mainactor annotations to windows
1 parent fa6d364 commit c439d12
Copy full SHA for c439d12

File tree

Expand file treeCollapse file tree

74 files changed

+332
-202
lines changed
Open diff view settings
Filter options

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Dismiss banner
Expand file treeCollapse file tree

74 files changed

+332
-202
lines changed
Open diff view settings
Collapse file

‎Package.resolved‎

Copy file name to clipboardExpand all lines: Package.resolved
+3-3Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Collapse file

‎Package.swift‎

Copy file name to clipboardExpand all lines: Package.swift
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ let package = Package(
9696
),
9797
.package(
9898
url: "https://github.com/stackotter/swift-image-formats",
99-
.upToNextMinor(from: "0.3.2")
99+
.upToNextMinor(from: "0.3.3")
100100
),
101101
.package(
102102
url: "https://github.com/stackotter/swift-windowsappsdk",
@@ -138,6 +138,9 @@ let package = Package(
138138
"Views/TupleViewChildren.swift.gyb",
139139
"Views/TableRowContent.swift.gyb",
140140
"Scenes/TupleScene.swift.gyb",
141+
],
142+
swiftSettings: [
143+
.enableUpcomingFeature("StrictConcurrency")
141144
]
142145
),
143146
.testTarget(
Collapse file

‎Sources/AppKitBackend/AppKitBackend.swift‎

Copy file name to clipboardExpand all lines: Sources/AppKitBackend/AppKitBackend.swift
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public final class AppKitBackend: AppBackend {
4444
NSApplication.shared.delegate = appDelegate
4545
}
4646

47-
public func runMainLoop(_ callback: @escaping () -> Void) {
47+
public func runMainLoop(_ callback: @escaping @MainActor () -> Void) {
4848
callback()
4949
NSApplication.shared.activate(ignoringOtherApps: true)
5050
NSApplication.shared.run()
@@ -316,7 +316,7 @@ public final class AppKitBackend: AppBackend {
316316
NSApplication.shared.helpMenu = helpMenu
317317
}
318318

319-
public func runInMainThread(action: @escaping () -> Void) {
319+
public func runInMainThread(action: @escaping @MainActor () -> Void) {
320320
DispatchQueue.main.async {
321321
action()
322322
}
Collapse file

‎Sources/Gtk3Backend/Gtk3Backend.swift‎

Copy file name to clipboardExpand all lines: Sources/Gtk3Backend/Gtk3Backend.swift
+14-10Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public final class Gtk3Backend: AppBackend {
6464
gtkApp.registerSession = true
6565
}
6666

67-
public func runMainLoop(_ callback: @escaping () -> Void) {
67+
public func runMainLoop(_ callback: @escaping @MainActor () -> Void) {
6868
gtkApp.run { window in
6969
self.precreatedWindow = window
7070
callback()
@@ -344,14 +344,14 @@ public final class Gtk3Backend: AppBackend {
344344
}
345345

346346
class ThreadActionContext {
347-
var action: () -> Void
347+
var action: @MainActor () -> Void
348348

349-
init(action: @escaping () -> Void) {
349+
init(action: @escaping @MainActor () -> Void) {
350350
self.action = action
351351
}
352352
}
353353

354-
public func runInMainThread(action: @escaping () -> Void) {
354+
public func runInMainThread(action: @escaping @MainActor () -> Void) {
355355
let action = ThreadActionContext(action: action)
356356
g_idle_add_full(
357357
0,
@@ -360,9 +360,11 @@ public final class Gtk3Backend: AppBackend {
360360
fatalError("Gtk action callback called without context")
361361
}
362362

363-
let action = Unmanaged<ThreadActionContext>.fromOpaque(context)
364-
.takeUnretainedValue()
365-
action.action()
363+
MainActor.assumeIsolated {
364+
let action = Unmanaged<ThreadActionContext>.fromOpaque(context)
365+
.takeUnretainedValue()
366+
action.action()
367+
}
366368

367369
return 0
368370
},
@@ -384,9 +386,11 @@ public final class Gtk3Backend: AppBackend {
384386
fatalError("Gtk action callback called without context")
385387
}
386388

387-
let action = Unmanaged<ThreadActionContext>.fromOpaque(context)
388-
.takeUnretainedValue()
389-
action.action()
389+
MainActor.assumeIsolated {
390+
let action = Unmanaged<ThreadActionContext>.fromOpaque(context)
391+
.takeUnretainedValue()
392+
action.action()
393+
}
390394

391395
// Cancel the recurring timeout after one iteration
392396
return 0
Collapse file

‎Sources/GtkBackend/GtkBackend.swift‎

Copy file name to clipboardExpand all lines: Sources/GtkBackend/GtkBackend.swift
+14-10Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public final class GtkBackend: AppBackend {
6565

6666
var globalCSSProvider: CSSProvider?
6767

68-
public func runMainLoop(_ callback: @escaping () -> Void) {
68+
public func runMainLoop(_ callback: @escaping @MainActor () -> Void) {
6969
gtkApp.run { window in
7070
self.precreatedWindow = window
7171
callback()
@@ -315,14 +315,14 @@ public final class GtkBackend: AppBackend {
315315
}
316316

317317
class ThreadActionContext {
318-
var action: () -> Void
318+
var action: @MainActor () -> Void
319319

320-
init(action: @escaping () -> Void) {
320+
init(action: @escaping @MainActor () -> Void) {
321321
self.action = action
322322
}
323323
}
324324

325-
public func runInMainThread(action: @escaping () -> Void) {
325+
public func runInMainThread(action: @escaping @MainActor () -> Void) {
326326
let action = ThreadActionContext(action: action)
327327
g_idle_add_full(
328328
0,
@@ -331,9 +331,11 @@ public final class GtkBackend: AppBackend {
331331
fatalError("Gtk action callback called without context")
332332
}
333333

334-
let action = Unmanaged<ThreadActionContext>.fromOpaque(context)
335-
.takeUnretainedValue()
336-
action.action()
334+
MainActor.assumeIsolated {
335+
let action = Unmanaged<ThreadActionContext>.fromOpaque(context)
336+
.takeUnretainedValue()
337+
action.action()
338+
}
337339

338340
return 0
339341
},
@@ -355,9 +357,11 @@ public final class GtkBackend: AppBackend {
355357
fatalError("Gtk action callback called without context")
356358
}
357359

358-
let action = Unmanaged<ThreadActionContext>.fromOpaque(context)
359-
.takeUnretainedValue()
360-
action.action()
360+
MainActor.assumeIsolated {
361+
let action = Unmanaged<ThreadActionContext>.fromOpaque(context)
362+
.takeUnretainedValue()
363+
action.action()
364+
}
361365

362366
// Cancel the recurring timeout after one iteration
363367
return 0
Collapse file

‎Sources/HotReloadingMacrosPlugin/HotReloadableAppMacro.swift‎

Copy file name to clipboardExpand all lines: Sources/HotReloadingMacrosPlugin/HotReloadableAppMacro.swift
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ extension HotReloadableAppMacro: MemberMacro {
104104
105105
if !hotReloadingHasConnectedToServer {
106106
hotReloadingHasConnectedToServer = true
107-
Task {
107+
Task { @MainActor
108108
do {
109109
var client = try await HotReloadingClient()
110110
print("Hot reloading: received new dylib")
Collapse file

‎Sources/SwiftCrossUI/App.swift‎

Copy file name to clipboardExpand all lines: Sources/SwiftCrossUI/App.swift
+5-2Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Foundation
22

33
/// An application.
4+
@MainActor
45
public protocol App {
56
/// The backend used to render the app.
67
associatedtype Backend: AppBackend
@@ -24,14 +25,16 @@ public protocol App {
2425

2526
/// Force refresh the entire scene graph. Used by hot reloading. If you need to do
2627
/// this in your own code then something has gone very wrong...
28+
@MainActor
2729
public var _forceRefresh: () -> Void = {}
2830

2931
/// Metadata embedded by Swift Bundler if present. Loaded at app start up.
32+
@MainActor
3033
private var swiftBundlerAppMetadata: AppMetadata?
3134

3235
/// An error encountered when parsing Swift Bundler metadata.
3336
private enum SwiftBundlerMetadataError: LocalizedError {
34-
case jsonNotDictionary(Any)
37+
case jsonNotDictionary(String)
3538
case missingAppIdentifier
3639
case missingAppVersion
3740

@@ -94,7 +97,7 @@ extension App {
9497
// require a lot of boilerplate code to parse with Codable).
9598
let jsonValue = try JSONSerialization.jsonObject(with: jsonData)
9699
guard let json = jsonValue as? [String: Any] else {
97-
throw SwiftBundlerMetadataError.jsonNotDictionary(jsonValue)
100+
throw SwiftBundlerMetadataError.jsonNotDictionary(String(describing: jsonValue))
98101
}
99102
guard let identifier = json["appIdentifier"] as? String else {
100103
throw SwiftBundlerMetadataError.missingAppIdentifier
Collapse file

‎Sources/SwiftCrossUI/Backend/AppBackend.swift‎

Copy file name to clipboardExpand all lines: Sources/SwiftCrossUI/Backend/AppBackend.swift
+5-5Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ import Foundation
4040
/// code between the `create` and `update` methods of the various widgets
4141
/// (since the `update` method is always called between calling `create`
4242
/// and actually displaying the widget anyway).
43-
public protocol AppBackend {
43+
@MainActor
44+
public protocol AppBackend: Sendable {
4445
associatedtype Window
4546
associatedtype Widget
4647
associatedtype Menu
@@ -120,7 +121,7 @@ public protocol AppBackend {
120121
/// setup function is passed to `Gtk` as a callback to run once the main
121122
/// run loop starts.
122123
func runMainLoop(
123-
_ callback: @escaping () -> Void
124+
_ callback: @escaping @MainActor () -> Void
124125
)
125126
/// Creates a new window. For some backends it may make sense for this
126127
/// method to return the application's root window the first time its
@@ -174,7 +175,7 @@ public protocol AppBackend {
174175
/// Runs an action in the app's main thread if required to perform UI updates
175176
/// by the backend. Predominantly used by ``Publisher`` to publish changes to a thread
176177
/// compatible with dispatching UI updates. Can be synchronous or asynchronous (for now).
177-
func runInMainThread(action: @escaping () -> Void)
178+
nonisolated func runInMainThread(action: @escaping @MainActor () -> Void)
178179

179180
/// Computes the root environment for an app (e.g. by checking the system's current
180181
/// theme). May fall back on the provided defaults where reasonable.
@@ -193,7 +194,7 @@ public protocol AppBackend {
193194
/// A default implementation is provided. It uses the backend's reported
194195
/// device class and looks up the text style in a lookup table derived
195196
/// from Apple's typography guidelines. See ``TextStyle/resolve(for:)``.
196-
@Sendable func resolveTextStyle(_ textStyle: Font.TextStyle) -> Font.TextStyle.Resolved
197+
func resolveTextStyle(_ textStyle: Font.TextStyle) -> Font.TextStyle.Resolved
197198

198199
/// Computes a window's environment based off the root environment. This may involve
199200
/// updating ``EnvironmentValues/windowScaleFactor`` etc.
@@ -683,7 +684,6 @@ public protocol AppBackend {
683684
}
684685

685686
extension AppBackend {
686-
@Sendable
687687
public func resolveTextStyle(
688688
_ textStyle: Font.TextStyle
689689
) -> Font.TextStyle.Resolved {
Collapse file

‎Sources/SwiftCrossUI/Backend/ResolvedMenu.swift‎

Copy file name to clipboardExpand all lines: Sources/SwiftCrossUI/Backend/ResolvedMenu.swift
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public struct ResolvedMenu {
1616
/// A menu item.
1717
public enum Item {
1818
/// A button. A `nil` action means that the button is disabled.
19-
case button(_ label: String, _ action: (() -> Void)?)
19+
case button(_ label: String, _ action: (@MainActor () -> Void)?)
2020
/// A named submenu.
2121
case submenu(Submenu)
2222
}
Collapse file

‎Sources/SwiftCrossUI/Environment/Actions/AlertAction.swift‎

Copy file name to clipboardExpand all lines: Sources/SwiftCrossUI/Environment/Actions/AlertAction.swift
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
/// This exists to avoid having to expose internal details of ``Button``, since
55
/// breaking ``Button``'s API would have much more wide-reaching impacts than
66
/// breaking this single-purpose API.
7-
public struct AlertAction {
7+
public struct AlertAction: Sendable {
88
public static let ok = AlertAction(label: "Ok", action: {})
99

1010
public var label: String
11-
public var action: () -> Void
11+
public var action: @MainActor @Sendable () -> Void
1212
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.