From 1693abf764b9918cbabc3f7fbd2b8beb9e1b974a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 17 Nov 2022 11:41:12 +0000 Subject: [PATCH 1/4] - Fixes some issues related to locking. - Bump min OS versions to iOS 10, macOS 10.12, tvOS 10, watchOS 3 --- Package.swift | 2 +- ReactiveSwift.podspec | 10 +-- Sources/Atomic.swift | 83 ++++++++----------- Sources/Scheduler.swift | 22 ++--- .../DateSchedulerAsyncTestCase.swift | 14 +--- 5 files changed, 53 insertions(+), 78 deletions(-) diff --git a/Package.swift b/Package.swift index 0eef252e6..99d47d23b 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ import PackageDescription let package = Package( name: "ReactiveSwift", platforms: [ - .macOS(.v10_10), .iOS(.v9), .tvOS(.v9), .watchOS(.v2) + .macOS(.v10_12), .iOS(.v10), .tvOS(.v10), .watchOS(.v3) ], products: [ .library(name: "ReactiveSwift", targets: ["ReactiveSwift"]), diff --git a/ReactiveSwift.podspec b/ReactiveSwift.podspec index e9c546295..a61c6d133 100644 --- a/ReactiveSwift.podspec +++ b/ReactiveSwift.podspec @@ -10,10 +10,10 @@ Pod::Spec.new do |s| s.license = { :type => "MIT", :file => "LICENSE.md" } s.author = "ReactiveCocoa" - s.ios.deployment_target = "9.0" - s.osx.deployment_target = "10.9" - s.watchos.deployment_target = "2.0" - s.tvos.deployment_target = "9.0" + s.ios.deployment_target = "10.0" + s.osx.deployment_target = "10.12" + s.watchos.deployment_target = "3.0" + s.tvos.deployment_target = "10.0" s.source = { :git => "https://github.com/ReactiveCocoa/ReactiveSwift.git", :tag => "#{s.version}" } # Directory glob for all Swift files s.source_files = ["Sources/*.{swift}", "Sources/**/*.{swift}"] @@ -21,5 +21,5 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = {"OTHER_SWIFT_FLAGS[config=Release]" => "$(inherited) -suppress-warnings" } s.cocoapods_version = ">= 1.7.0" - s.swift_versions = ["5.1", "5.2"] + s.swift_versions = ["5.2", "5.3" "5.4", "5.5", "5.6", "5.7"] end diff --git a/Sources/Atomic.swift b/Sources/Atomic.swift index cafd01dd3..9a36f24a6 100644 --- a/Sources/Atomic.swift +++ b/Sources/Atomic.swift @@ -108,10 +108,6 @@ internal struct UnsafeAtomicState where State.RawValue /// fallback. internal class Lock: LockProtocol { #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - @available(iOS 10.0, *) - @available(macOS 10.12, *) - @available(tvOS 10.0, *) - @available(watchOS 3.0, *) internal final class UnfairLock: Lock { private let _lock: os_unfair_lock_t @@ -140,34 +136,31 @@ internal class Lock: LockProtocol { } #endif - #if compiler(>=5.7) - #if !targetEnvironment(macCatalyst) - #if os(iOS) || os(tvOS) || os(watchOS) - @available(iOS 16.0, *) - @available(tvOS 16.0, *) - @available(watchOS 9.0, *) - internal final class AllocatedUnfairLock: Lock { - private let _lock = OSAllocatedUnfairLock() - - override init() { - super.init() - } - - override func lock() { - _lock.lock() - } - - override func unlock() { - _lock.unlock() - } - - override func `try`() -> Bool { - _lock.lockIfAvailable() - } - } - #endif - #endif - #endif + #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) + @available(iOS 16.0, *) + @available(macOS 13.0, *) + @available(tvOS 16.0, *) + @available(watchOS 9.0, *) + internal final class AllocatedUnfairLock: Lock { + private let _lock = OSAllocatedUnfairLock() + + override init() { + super.init() + } + + override func lock() { + _lock.lock() + } + + override func unlock() { + _lock.unlock() + } + + override func `try`() -> Bool { + _lock.lockIfAvailable() + } + } + #endif internal final class PthreadLock: Lock { private let _lock: UnsafeMutablePointer @@ -227,23 +220,15 @@ internal class Lock: LockProtocol { } static func make() -> Self { - #if compiler(>=5.7) - #if !targetEnvironment(macCatalyst) - #if os(iOS) || os(tvOS) || os(watchOS) - if #available(*, iOS 16.0, tvOS 16.0, watchOS 9.0) { - return AllocatedUnfairLock() as! Self - } - #endif - #endif - #endif - - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - if #available(*, macOS 11.12, iOS 10.0, tvOS 10.0, watchOS 3.0) { - return UnfairLock() as! Self - } - #endif - - return PthreadLock() as! Self + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + guard #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) else { + return UnfairLock() as! Self + } + + return AllocatedUnfairLock() as! Self + #else + return PthreadLock() as! Self + #endif } private init() {} diff --git a/Sources/Scheduler.swift b/Sources/Scheduler.swift index e0d5117f2..a82b8d9a3 100644 --- a/Sources/Scheduler.swift +++ b/Sources/Scheduler.swift @@ -742,7 +742,7 @@ public final class TestScheduler: DateScheduler { @MainActor @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, *) public func advance(by interval: DispatchTimeInterval) async { - await advance(to: lock.withLock({ currentDate.addingTimeInterval(interval) })) + await advance(to: lock.sync({ currentDate.addingTimeInterval(interval) })) } /// Advances the virtualized clock by the given interval, dequeuing and @@ -753,7 +753,7 @@ public final class TestScheduler: DateScheduler { @MainActor @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, *) public func advance(by interval: TimeInterval) async { - await advance(to: lock.withLock({ currentDate.addingTimeInterval(interval) })) + await advance(to: lock.sync({ currentDate.addingTimeInterval(interval) })) } /// Advances the virtualized clock to the given future date, dequeuing and @@ -764,12 +764,12 @@ public final class TestScheduler: DateScheduler { @MainActor @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, *) public func advance(to newDate: Date) async { - assert(lock.withLock { _currentDate <= newDate }) + assert(lock.sync { _currentDate <= newDate }) - while lock.withLock({ _currentDate }) <= newDate { + while lock.sync({ _currentDate }) <= newDate { await Task.megaYield() - let `return` = lock.withLock { + let `return` = lock.sync { guard let next = scheduledActions.first, newDate >= next.date @@ -799,15 +799,11 @@ public final class TestScheduler: DateScheduler { #endif } -@available(macOS, obsoleted: 13, message: "`NSLocking` now provides `withLocking`, so this is no longer needed") -@available(iOS, obsoleted: 13, message: "`NSLocking` now provides `withLocking`, so this is no longer needed") -@available(watchOS, obsoleted: 9, message: "`NSLocking` now provides `withLocking`, so this is no longer needed") -@available(macCatalyst, obsoleted: 16, message: "`NSLocking` now provides `withLocking`, so this is no longer needed") extension NSRecursiveLock { - fileprivate func withLock(_ body: () throws -> T) rethrows -> T { - self.lock() - defer { self.unlock() } - return try body() + fileprivate func sync(_ operation: () -> T) -> T { + self.lock() + defer { self.unlock() } + return operation() } } diff --git a/Tests/ReactiveSwiftTests/DateSchedulerAsyncTestCase.swift b/Tests/ReactiveSwiftTests/DateSchedulerAsyncTestCase.swift index 8734ad1ea..f84439c5d 100644 --- a/Tests/ReactiveSwiftTests/DateSchedulerAsyncTestCase.swift +++ b/Tests/ReactiveSwiftTests/DateSchedulerAsyncTestCase.swift @@ -25,35 +25,29 @@ final class DateSchedulerAsyncTestCase: XCTestCase { } func test_sleepFor_shouldSleepForTheDefinedIntervalBeforeReturning() async throws { - let expectation = self.expectation(description: "sleep") - - Task { + let task = Task { try await scheduler.sleep(for: .seconds(5)) XCTAssertEqual(scheduler.currentDate, startDate.addingTimeInterval(5)) - expectation.fulfill() } XCTAssertEqual(scheduler.currentDate, startDate) await scheduler.advance(by: .seconds(5)) - await waitForExpectations(timeout: 0.1) + let _ = await task.result } func test_sleepUntil_shouldSleepForTheDefinedIntervalBeforeReturning() async throws { - let expectation = self.expectation(description: "sleep") - let sleepDate = startDate.addingTimeInterval(5) - Task { + let task = Task { try await scheduler.sleep(until: sleepDate) XCTAssertEqual(scheduler.currentDate, sleepDate) - expectation.fulfill() } XCTAssertEqual(scheduler.currentDate, startDate) await scheduler.advance(by: .seconds(5)) - await waitForExpectations(timeout: 0.1) + let _ = await task.result } func test_timer_shouldSendTheCurrentDateAtTheGivenInterval() async throws { From 5574886cbed701b2af44c2493794fc163556854a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 17 Nov 2022 11:43:37 +0000 Subject: [PATCH 2/4] Updated CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 186ba827f..ec23c4e58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ # master *Please add new entries at the top.* +1. Fix some issues related to locking, bumped min OS versions to iOS 10, macOS 10.12, tvOS 10, watchOS 3 (#858, kudos to @mluisbrown) 1. Add `async` helpers to Schedulers (#857, kudos to @p4checo) 1. Add primary associated types to SignalProducerConvertible & SignalProducerProtocol (#855, kudos to @braker1nine) 2. Refactor Github Actions to cover more swift versions (#858, kudos to @braker1nine) -1.Use `OSAllocatedUnfairLock` instead of `os_unfair_lock` on supported Apple platforms (#856, kudos to @mluisbrown) +1. Use `OSAllocatedUnfairLock` instead of `os_unfair_lock` on supported Apple platforms (#856, kudos to @mluisbrown) # 7.0.0 1. The UnidirectionalBinding operator `<~` returns non optional values. (#834, kudos to @NicholasTD07) From 10024560b6692e042c31ec9170282f256231a35c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 17 Nov 2022 11:57:13 +0000 Subject: [PATCH 3/4] Update deployment targets in project file. --- CHANGELOG.md | 2 +- ReactiveSwift.xcodeproj/project.pbxproj | 44 ++++++++++++------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec23c4e58..db0771f76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # master *Please add new entries at the top.* -1. Fix some issues related to locking, bumped min OS versions to iOS 10, macOS 10.12, tvOS 10, watchOS 3 (#858, kudos to @mluisbrown) +1. Fix some issues related to locking, bumped min OS versions to iOS 10, macOS 10.12, tvOS 10, watchOS 3 (#859, kudos to @mluisbrown) 1. Add `async` helpers to Schedulers (#857, kudos to @p4checo) 1. Add primary associated types to SignalProducerConvertible & SignalProducerProtocol (#855, kudos to @braker1nine) 2. Refactor Github Actions to cover more swift versions (#858, kudos to @braker1nine) diff --git a/ReactiveSwift.xcodeproj/project.pbxproj b/ReactiveSwift.xcodeproj/project.pbxproj index 60a03c25d..fb6b0eb69 100644 --- a/ReactiveSwift.xcodeproj/project.pbxproj +++ b/ReactiveSwift.xcodeproj/project.pbxproj @@ -1516,16 +1516,16 @@ CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; ENABLE_TESTABILITY = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; - SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 9.0; + SWIFT_VERSION = 5.2; + TVOS_DEPLOYMENT_TARGET = 10.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Debug; }; @@ -1536,16 +1536,16 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; - SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 9.0; + SWIFT_VERSION = 5.2; + TVOS_DEPLOYMENT_TARGET = 10.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Release; }; @@ -1578,7 +1578,7 @@ baseConfigurationReference = E6124BA9267DF505005A3490 /* macOS-XCTest.xcconfig */; buildSettings = { INFOPLIST_FILE = Tests/ReactiveSwiftTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Debug; @@ -1588,7 +1588,7 @@ baseConfigurationReference = E6124BA9267DF505005A3490 /* macOS-XCTest.xcconfig */; buildSettings = { INFOPLIST_FILE = Tests/ReactiveSwiftTests/Info.plist; - MACOSX_DEPLOYMENT_TARGET = 10.10; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_NAME = "$(PROJECT_NAME)Tests"; }; name = Release; @@ -1638,16 +1638,16 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; - SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 9.0; + SWIFT_VERSION = 5.2; + TVOS_DEPLOYMENT_TARGET = 10.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Profile; }; @@ -1699,16 +1699,16 @@ BITCODE_GENERATION_MODE = bitcode; CODE_SIGNING_REQUIRED = NO; CURRENT_PROJECT_VERSION = 1; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MACOSX_DEPLOYMENT_TARGET = 10.12; PRODUCT_BUNDLE_IDENTIFIER = "org.reactivecocoa.$(PRODUCT_NAME:rfc1034identifier)-Tests"; PRODUCT_NAME = "$(PROJECT_NAME)"; SWIFT_TREAT_WARNINGS_AS_ERRORS = NO; - SWIFT_VERSION = 5.0; - TVOS_DEPLOYMENT_TARGET = 9.0; + SWIFT_VERSION = 5.2; + TVOS_DEPLOYMENT_TARGET = 10.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Test; }; From 4ccea252de32a6735c8af327e47f48449ba7f9c8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 17 Nov 2022 12:10:45 +0000 Subject: [PATCH 4/4] =?UTF-8?q?Change=20indentation=20to=20tabs=20?= =?UTF-8?q?=F0=9F=99=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/Atomic.swift | 64 +++++++++---------- Sources/Scheduler.swift | 6 +- .../DateSchedulerAsyncTestCase.swift | 4 +- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/Sources/Atomic.swift b/Sources/Atomic.swift index 9a36f24a6..449315596 100644 --- a/Sources/Atomic.swift +++ b/Sources/Atomic.swift @@ -56,8 +56,8 @@ internal struct UnsafeAtomicState where State.RawValue /// - returns: `true` if the transition succeeds. `false` otherwise. internal func tryTransition(from expected: State, to next: State) -> Bool { return OSAtomicCompareAndSwap32Barrier(expected.rawValue, - next.rawValue, - value) + next.rawValue, + value) } #else private let value: Atomic @@ -136,31 +136,31 @@ internal class Lock: LockProtocol { } #endif - #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) - @available(iOS 16.0, *) - @available(macOS 13.0, *) - @available(tvOS 16.0, *) - @available(watchOS 9.0, *) - internal final class AllocatedUnfairLock: Lock { - private let _lock = OSAllocatedUnfairLock() + #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) + @available(iOS 16.0, *) + @available(macOS 13.0, *) + @available(tvOS 16.0, *) + @available(watchOS 9.0, *) + internal final class AllocatedUnfairLock: Lock { + private let _lock = OSAllocatedUnfairLock() - override init() { - super.init() - } + override init() { + super.init() + } - override func lock() { - _lock.lock() - } + override func lock() { + _lock.lock() + } - override func unlock() { - _lock.unlock() - } + override func unlock() { + _lock.unlock() + } - override func `try`() -> Bool { - _lock.lockIfAvailable() - } - } - #endif + override func `try`() -> Bool { + _lock.lockIfAvailable() + } + } + #endif internal final class PthreadLock: Lock { private let _lock: UnsafeMutablePointer @@ -220,15 +220,15 @@ internal class Lock: LockProtocol { } static func make() -> Self { - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - guard #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) else { - return UnfairLock() as! Self - } - - return AllocatedUnfairLock() as! Self - #else - return PthreadLock() as! Self - #endif + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + guard #available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) else { + return UnfairLock() as! Self + } + + return AllocatedUnfairLock() as! Self + #else + return PthreadLock() as! Self + #endif } private init() {} diff --git a/Sources/Scheduler.swift b/Sources/Scheduler.swift index a82b8d9a3..3b0722279 100644 --- a/Sources/Scheduler.swift +++ b/Sources/Scheduler.swift @@ -801,9 +801,9 @@ public final class TestScheduler: DateScheduler { extension NSRecursiveLock { fileprivate func sync(_ operation: () -> T) -> T { - self.lock() - defer { self.unlock() } - return operation() + self.lock() + defer { self.unlock() } + return operation() } } diff --git a/Tests/ReactiveSwiftTests/DateSchedulerAsyncTestCase.swift b/Tests/ReactiveSwiftTests/DateSchedulerAsyncTestCase.swift index f84439c5d..7c02cf271 100644 --- a/Tests/ReactiveSwiftTests/DateSchedulerAsyncTestCase.swift +++ b/Tests/ReactiveSwiftTests/DateSchedulerAsyncTestCase.swift @@ -33,7 +33,7 @@ final class DateSchedulerAsyncTestCase: XCTestCase { XCTAssertEqual(scheduler.currentDate, startDate) await scheduler.advance(by: .seconds(5)) - let _ = await task.result + let _ = await task.result } func test_sleepUntil_shouldSleepForTheDefinedIntervalBeforeReturning() async throws { @@ -47,7 +47,7 @@ final class DateSchedulerAsyncTestCase: XCTestCase { XCTAssertEqual(scheduler.currentDate, startDate) await scheduler.advance(by: .seconds(5)) - let _ = await task.result + let _ = await task.result } func test_timer_shouldSendTheCurrentDateAtTheGivenInterval() async throws {