Skip to content

Commit

Permalink
[Feature] Update Update API (#124)
Browse files Browse the repository at this point in the history
* Update Update to RELEASE_2024

* Optimize MovableLock API interface

* Add UpdateTests
  • Loading branch information
Kyle-Ye committed Sep 24, 2024
1 parent 3029608 commit 63c8cba
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 142 deletions.
4 changes: 2 additions & 2 deletions Sources/COpenSwiftUICore/include/MovableLock.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ MovableLock _MovableLockCreate(void) OPENSWIFTUI_SWIFT_NAME(MovableLock.create()
OPENSWIFTUI_EXPORT
OPENSWIFTUI_REFINED_FOR_SWIFT
void _MovableLockDestory(MovableLock lock) OPENSWIFTUI_SWIFT_NAME(MovableLock.destory(self:));
bool _MovableLockIsOwner(MovableLock lock) OPENSWIFTUI_SWIFT_NAME(MovableLock.isOwner(self:));
bool _MovableLockIsOuterMostOwner(MovableLock lock) OPENSWIFTUI_SWIFT_NAME(MovableLock.isOuterMostOwner(self:));
bool _MovableLockIsOwner(MovableLock lock) OPENSWIFTUI_SWIFT_NAME(getter:MovableLock.isOwner(self:));
bool _MovableLockIsOuterMostOwner(MovableLock lock) OPENSWIFTUI_SWIFT_NAME(getter:MovableLock.isOuterMostOwner(self:));
void _MovableLockLock(MovableLock lock) OPENSWIFTUI_SWIFT_NAME(MovableLock.lock(self:));
void _MovableLockUnlock(MovableLock lock) OPENSWIFTUI_SWIFT_NAME(MovableLock.unlock(self:));
void _MovableLockSyncMain(MovableLock lock, const void *context, void (*function)(const void *context)) OPENSWIFTUI_SWIFT_NAME(MovableLock.syncMain(self:_:function:));
Expand Down
2 changes: 1 addition & 1 deletion Sources/OpenSwiftUI/Core/Render/DisplayLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ final class DisplayLink: NSObject {
}

func invalidate() {
Update.lock.withLock {
Update.locked {
// TODO
}
}
Expand Down
126 changes: 0 additions & 126 deletions Sources/OpenSwiftUI/Core/Update/Update.swift

This file was deleted.

6 changes: 3 additions & 3 deletions Sources/OpenSwiftUI/Core/View/ViewRendererHost.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ protocol ViewRendererHost: ViewGraphDelegate {

extension ViewRendererHost {
func updateViewGraph<Value>(body: (ViewGraph) -> Value) -> Value {
Update.perform {
Update.dispatchImmediately {
OGGraph.withoutUpdate {
updateGraph()
return body(viewGraph)
Expand All @@ -37,7 +37,7 @@ extension ViewRendererHost {
}

func invalidateProperties(_ properties: ViewRendererHostProperties, mayDeferUpdate: Bool) {
Update.lock.withLock {
Update.locked {
guard !propertiesNeedingUpdate.contains(properties) else {
return
}
Expand All @@ -56,7 +56,7 @@ extension ViewRendererHost {
}

func render(interval: Double, updateDisplayList: Bool = true) {
Update.perform {
Update.dispatchImmediately {
guard !isRendering else {
return
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/OpenSwiftUI/Integration/UIKit/UIHostingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ open class _UIHostingView<Content>: UIView where Content: View {
guard canAdvanceTimeAutomatically else {
return
}
Update.lock.withLock {
Update.locked {
cancelAsyncRendering()
let interval: Double
if let displayLink, displayLink.willRender {
Expand Down Expand Up @@ -129,7 +129,7 @@ open class _UIHostingView<Content>: UIView where Content: View {
}

func cancelAsyncRendering() {
Update.lock.withLock {
Update.locked {
displayLink?.cancelAsyncRendering()
}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/OpenSwiftUI/View/Toggle/Switch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ private class PlatformSwitchCoordinator: PlatformViewCoordinator {

@objc
func isOnChanged(_ sender: UISwitch) {
Update.perform {
Update.dispatchImmediately {
_isOn.wrappedValue = sender.isOn
}
sender.setOn(_isOn.wrappedValue, animated: true)
Expand Down
179 changes: 179 additions & 0 deletions Sources/OpenSwiftUICore/Data/Update.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
//
// Update.swift
// OpenSwiftUI
//
// Audited for RELEASE_2024
// Status: Blocked by Signpost
// ID: EA173074DA35FA471DC70643259B7E74 (RELEASE_2021)
// ID: 61534957AEEC2EDC447ABDC13B4D426F (RELEASE_2024)

internal import COpenSwiftUICore
internal import OpenGraphShims
import Foundation

package enum Update {
private final class TraceHost {}
static let trackHost: AnyObject = TraceHost()

private static var depth = 0
private static var dispatchDepth = 0
private static let _lock = MovableLock.create()
private static var actions: [() -> Void] = []
private static let lockAssertionsAreEnabled = EnvironmentHelper.bool(for: "OPENSWIFTUI_ASSERT_LOCKS")

@inlinable
package static var isActive: Bool {
depth != 0
}

@inlinable
package static var threadIsUpdating: Bool {
depth < dispatchDepth ? isOwner : false
}

@inlinable
package static func assertIsActive() {
assert(isActive)
}

package static func lock() {
_lock.lock()
}
package static func unlock() {
_lock.unlock()
}

package static var isOwner: Bool {
_lock.isOwner
}

package static func wait() {
_lock.wait()
}

package static func broadcast() {
_lock.broadcast()
}

package static func assertIsLocked() {
guard lockAssertionsAreEnabled else {
return
}
guard isOwner else {
fatalError("OpenSwiftUI is active without having taken its own lock - missing Update.ensure()?")
}
}

package static func begin() {
lock()
depth += 1
if depth == 1 {
// TODO: Signpost.renderUpdate.trace + trackHost
}
}

package static func end() {
if depth == 1 {
dispatchActions()
// TODO: Signpost.renderUpdate.trace + trackHost
}
depth -= 1
unlock()
}

package static func enqueueAction(_ action: @escaping () -> Void) {
begin()
actions.append(action)
end()
}

@inlinable
@inline(__always)
package static func locked<T>(_ body: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try body()
}

package static func syncMain(_ body: () -> Void) {
#if os(WASI)
// FIXME: See #76
body()
#else
if Thread.isMainThread {
body()
} else {
withoutActuallyEscaping(body) { escapableBody in
let context = AnyRuleContext(attribute: AnyOptionalAttribute.current.identifier)
MovableLock.syncMain(lock: _lock) {
#if canImport(Darwin)
context.update(body: escapableBody)
#else
fatalError("See #39")
#endif
}
}
}
#endif
}

package static func ensure<T>(_ callback: () throws -> T) rethrows -> T {
try locked {
begin()
defer { end() }
return try callback()
}
}

package static var canDispatch: Bool {
assertIsLocked()
guard depth == 1 else {
return false
}
return !actions.isEmpty
}

package static func dispatchActions() {
guard canDispatch else { return }
repeat {
let actions = Update.actions
Update.actions = []
onMainThread {
Signpost.postUpdateActions.traceInterval(object: trackHost, nil) {
begin()
let oldDispatchDepth = dispatchDepth
let oldDepth = depth
dispatchDepth = oldDepth
defer {
dispatchDepth = oldDispatchDepth
end()
}
for action in actions {
action()
let newDepth = depth
guard newDepth == oldDepth else {
fatalError("Action caused unbalanced updates.")
}
}
}
}
} while(!Update.actions.isEmpty)
}

package static func dispatchImmediately<T>(_ body: () -> T) -> T {
begin()
let oldDispatchDepth = dispatchDepth
dispatchDepth = depth
defer {
dispatchDepth = oldDispatchDepth
end()
}
return body()
}
}

// FIXME: migrate to use @_extern(c, "xx") in Swift 6
extension MovableLock {
@_silgen_name("_MovableLockSyncMain")
static func syncMain(lock: MovableLock, body: @escaping () -> Void)
}
2 changes: 1 addition & 1 deletion Sources/OpenSwiftUICore/Log/Signpost.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Signpost.swift
// OpenSwiftUI
//
// Audited for RELEASE_2021
// Audited for RELEASE_2024
// Status: WIP
// ID: 34756F646CF7AC3DBE2A8E0B344C962F (RELEASE_2021)
// ID: 59349949219F590F26B6A55CEC9D59A2 (RELEASE_2024)
Expand Down
Loading

0 comments on commit 63c8cba

Please sign in to comment.