Skip to content

Commit

Permalink
strict concurrency checking.
Browse files Browse the repository at this point in the history
  • Loading branch information
hfutrell committed Oct 18, 2024
1 parent 01658fd commit 110bc59
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 30 deletions.
8 changes: 8 additions & 0 deletions BezierKit/BezierKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,7 @@
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTS_MACCATALYST = NO;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
Expand Down Expand Up @@ -1040,6 +1041,7 @@
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTS_MACCATALYST = NO;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
Expand Down Expand Up @@ -1075,6 +1077,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.HolmesFutrell.BezierKit;
PRODUCT_NAME = BezierKit;
SKIP_INSTALL = YES;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
Expand Down Expand Up @@ -1108,6 +1111,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.HolmesFutrell.BezierKit;
PRODUCT_NAME = BezierKit;
SKIP_INSTALL = YES;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
Expand All @@ -1129,6 +1133,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.HolmesFutrell.BezierKit-iOSTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
};
name = Debug;
Expand All @@ -1148,6 +1153,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.HolmesFutrell.BezierKit-iOSTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
VALIDATE_PRODUCT = YES;
};
Expand All @@ -1168,6 +1174,7 @@
MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.HolmesFutrell.BezierKit-MacTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 5.0;
};
Expand All @@ -1188,6 +1195,7 @@
MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.HolmesFutrell.BezierKit-MacTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 5.0;
};
Expand Down
60 changes: 36 additions & 24 deletions BezierKit/BezierKitTests/LockTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,26 @@
// Copyright © 2019 Holmes Futrell. All rights reserved.
//

import XCTest
@preconcurrency import XCTest
@testable import BezierKit

#if !os(WASI)
class LockTests: XCTestCase {
func testPathPropertyAtomicity() {
func testPathPropertyAtomicity() async {

@MainActor class Results: Sendable {
#if canImport(CoreGraphics)
var cgPaths: [Int: CGPath] = [:]
func setPath(at index: Int, _ value: CGPath) {
cgPaths[index] = value
}
#endif
var boundingBoxes: [Int: BoundingBox] = [:]
func setBoundingBox(at index: Int, _ value: BoundingBox) {
boundingBoxes[index] = value
}
}

// ensure that lazy properties of Path are only initialized once
let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
let path = Path(rect: rect)
Expand All @@ -20,38 +34,36 @@ class LockTests: XCTestCase {
let expectation = XCTestExpectation()
expectation.expectedFulfillmentCount = threadCount

#if canImport(CoreGraphics)
var cgPaths: [Int: CGPath] = [:]
#endif
var boundingBoxes: [Int: BoundingBox] = [:]

let results = await Results()
for i in 0..<threadCount {
let index = i
DispatchQueue.global(qos: .default).async {
#if canImport(CoreGraphics)
Task.detached {
#if canImport(CoreGraphics)
let pathValue = path.cgPath
#endif
#endif
let boundingBoxValue = path.boundingBox
DispatchQueue.main.async {
#if canImport(CoreGraphics)
cgPaths[index] = pathValue
#endif
boundingBoxes[index] = boundingBoxValue
await MainActor.run {
#if canImport(CoreGraphics)
results.setPath(at: index, pathValue)
#endif
results.setBoundingBox(at: index, boundingBoxValue)
expectation.fulfill()
}
}
}
wait(for: [expectation], timeout: 10.0)

#if canImport(CoreGraphics)
XCTAssertEqual(cgPaths.values.count, threadCount)
XCTAssertEqual(cgPaths[0], Path(rect: rect).cgPath)
XCTAssertTrue(cgPaths.values.allSatisfy { $0 === cgPaths[0] }, "cgPaths should all refer to the same instance (was it initialized more than once?)")
#endif
await fulfillment(of: [expectation], timeout: 10.0)

let expectedBoundingBox = Path(rect: rect).boundingBox
XCTAssertEqual(boundingBoxes.values.count, threadCount)
XCTAssertTrue(boundingBoxes.values.allSatisfy { $0 == expectedBoundingBox })
await MainActor.run {
#if canImport(CoreGraphics)
XCTAssertEqual(results.cgPaths.values.count, threadCount)
XCTAssertEqual(results.cgPaths[0], Path(rect: rect).cgPath)
XCTAssertTrue(results.cgPaths.values.allSatisfy { $0 === results.cgPaths[0] }, "cgPaths should all refer to the same instance (was it initialized more than once?)")
#endif
let expectedBoundingBox = Path(rect: rect).boundingBox
XCTAssertEqual(results.boundingBoxes.values.count, threadCount)
XCTAssertTrue(results.boundingBoxes.values.allSatisfy { $0 == expectedBoundingBox })
}
}
}
#endif
12 changes: 6 additions & 6 deletions BezierKit/Library/Draw.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public class Draw {
public static let blue = Draw.Color(red: 0.0, green: 0.0, blue: 255.0, alpha: 1.0)
public static let green = Draw.Color(red: 0.0, green: 255.0, blue: 0.0, alpha: 1.0)

private static var randomIndex = 0
@MainActor private static var randomIndex = 0
private static let randomColors: [CGColor] = {
var temp: [CGColor] = []
for i in 0..<360 {
Expand All @@ -97,20 +97,20 @@ public class Draw {

// MARK: -

public static func reset(_ context: CGContext) {
@MainActor public static func reset(_ context: CGContext) {
context.setStrokeColor(black)
randomIndex = 0
}

// MARK: - setting colors

public static func setRandomColor(_ context: CGContext) {
@MainActor public static func setRandomColor(_ context: CGContext) {
randomIndex = (randomIndex+1) % randomColors.count
let c = randomColors[randomIndex]
context.setStrokeColor(c)
}

public static func setRandomFill(_ context: CGContext, alpha a: CGFloat = 1.0) {
@MainActor public static func setRandomFill(_ context: CGContext, alpha a: CGFloat = 1.0) {
randomIndex = (randomIndex+1) % randomColors.count
let c = randomColors[randomIndex]
let c2 = c.copy(alpha: a)
Expand Down Expand Up @@ -264,7 +264,7 @@ public class Draw {

}

public static func drawPathComponent(_ context: CGContext, pathComponent: PathComponent, offset: CGPoint = .zero, includeBoundingVolumeHierarchy: Bool = false) {
@MainActor public static func drawPathComponent(_ context: CGContext, pathComponent: PathComponent, offset: CGPoint = .zero, includeBoundingVolumeHierarchy: Bool = false) {
if includeBoundingVolumeHierarchy {
pathComponent.bvh.visit { node, depth in
setColor(context, color: randomColors[depth])
Expand All @@ -280,7 +280,7 @@ public class Draw {
context.drawPath(using: .fillStroke)
}

public static func drawPath(_ context: CGContext, _ path: Path, offset: CGPoint = .zero) {
@MainActor public static func drawPath(_ context: CGContext, _ path: Path, offset: CGPoint = .zero) {
Draw.setRandomFill(context, alpha: 0.2)
context.addPath(path.cgPath)
context.drawPath(using: .fillStroke)
Expand Down

0 comments on commit 110bc59

Please sign in to comment.