From 146a7befed2c4032c35ab9930dadd0eec16b7f6e Mon Sep 17 00:00:00 2001 From: Yi Wang Date: Tue, 19 Feb 2019 17:54:17 -0800 Subject: [PATCH] Add AutoReleasingSemaphore Similar to `DispatchSemaphore`, `AutoReleasingSemaphore` is a synchronization mechanism that ensures only a set number of threads can concurrently access a protected resource. Unlike `DispatchSemaphore`, `AutoReleasingSemaphore` auto-releases all blocked threads when the semaphore itself is deallocated. --- Concurrency.xcodeproj/project.pbxproj | 442 +++++++++--------- .../Concurrency/AutoReleasingSemaphore.swift | 83 ++++ .../AutoReleasingSemaphoreTests.swift | 67 +++ 3 files changed, 382 insertions(+), 210 deletions(-) create mode 100644 Sources/Concurrency/AutoReleasingSemaphore.swift create mode 100644 Tests/ConcurrencyTests/AutoReleasingSemaphoreTests.swift diff --git a/Concurrency.xcodeproj/project.pbxproj b/Concurrency.xcodeproj/project.pbxproj index 70936a0..0109e86 100644 --- a/Concurrency.xcodeproj/project.pbxproj +++ b/Concurrency.xcodeproj/project.pbxproj @@ -5,13 +5,13 @@ objects = { "Concurrency::Concurrency" = { isa = "PBXNativeTarget"; - buildConfigurationList = "OBJ_36"; + buildConfigurationList = "OBJ_38"; buildPhases = ( - "OBJ_39", - "OBJ_48" + "OBJ_41", + "OBJ_51" ); dependencies = ( - "OBJ_50" + "OBJ_53" ); name = "Concurrency"; productName = "Concurrency"; @@ -25,25 +25,25 @@ }; "Concurrency::ConcurrencyPackageTests::ProductTarget" = { isa = "PBXAggregateTarget"; - buildConfigurationList = "OBJ_59"; + buildConfigurationList = "OBJ_62"; buildPhases = ( ); dependencies = ( - "OBJ_62" + "OBJ_65" ); name = "ConcurrencyPackageTests"; productName = "ConcurrencyPackageTests"; }; "Concurrency::ConcurrencyTests" = { isa = "PBXNativeTarget"; - buildConfigurationList = "OBJ_64"; + buildConfigurationList = "OBJ_67"; buildPhases = ( - "OBJ_67", - "OBJ_73" + "OBJ_70", + "OBJ_77" ); dependencies = ( - "OBJ_76", - "OBJ_77" + "OBJ_80", + "OBJ_81" ); name = "ConcurrencyTests"; productName = "ConcurrencyTests"; @@ -57,10 +57,10 @@ }; "Concurrency::ObjCBridges" = { isa = "PBXNativeTarget"; - buildConfigurationList = "OBJ_78"; + buildConfigurationList = "OBJ_82"; buildPhases = ( - "OBJ_81", - "OBJ_83" + "OBJ_85", + "OBJ_87" ); dependencies = ( ); @@ -76,9 +76,9 @@ }; "Concurrency::SwiftPMPackageDescription" = { isa = "PBXNativeTarget"; - buildConfigurationList = "OBJ_53"; + buildConfigurationList = "OBJ_56"; buildPhases = ( - "OBJ_56" + "OBJ_59" ); dependencies = ( ); @@ -99,7 +99,7 @@ "en" ); mainGroup = "OBJ_5"; - productRefGroup = "OBJ_31"; + productRefGroup = "OBJ_33"; projectDirPath = "."; targets = ( "Concurrency::Concurrency", @@ -110,75 +110,66 @@ ); }; "OBJ_10" = { - isa = "PBXGroup"; - children = ( - "OBJ_11", - "OBJ_12" - ); - name = "include"; - path = "include"; + isa = "PBXFileReference"; + path = "AtomicInt.swift"; sourceTree = ""; }; "OBJ_11" = { isa = "PBXFileReference"; - path = "AtomicBridges.h"; + path = "AtomicReference.swift"; sourceTree = ""; }; "OBJ_12" = { isa = "PBXFileReference"; - name = "module.modulemap"; - path = "/Users/yiw/Uber/GitHub/swift-concurrency/Concurrency.xcodeproj/GeneratedModuleMap/ObjCBridges/module.modulemap"; + path = "AutoReleasingSemaphore.swift"; sourceTree = ""; }; "OBJ_13" = { + isa = "PBXFileReference"; + path = "CountDownLatch.swift"; + sourceTree = ""; + }; + "OBJ_14" = { isa = "PBXGroup"; children = ( - "OBJ_14", "OBJ_15", "OBJ_16", "OBJ_17", "OBJ_18" ); - name = "Concurrency"; - path = "Sources/Concurrency"; - sourceTree = "SOURCE_ROOT"; - }; - "OBJ_14" = { - isa = "PBXFileReference"; - path = "AtomicBool.swift"; + name = "Executor"; + path = "Executor"; sourceTree = ""; }; "OBJ_15" = { isa = "PBXFileReference"; - path = "AtomicInt.swift"; + path = "ConcurrentSequenceExecutor.swift"; sourceTree = ""; }; "OBJ_16" = { isa = "PBXFileReference"; - path = "AtomicReference.swift"; + path = "ImmediateSerialSequenceExecutor.swift"; sourceTree = ""; }; "OBJ_17" = { isa = "PBXFileReference"; - path = "CountDownLatch.swift"; + path = "SequenceExecutor.swift"; sourceTree = ""; }; "OBJ_18" = { + isa = "PBXFileReference"; + path = "Task.swift"; + sourceTree = ""; + }; + "OBJ_19" = { isa = "PBXGroup"; children = ( - "OBJ_19", "OBJ_20", - "OBJ_21", - "OBJ_22" + "OBJ_21" ); - name = "Executor"; - path = "Executor"; - sourceTree = ""; - }; - "OBJ_19" = { - isa = "PBXFileReference"; - path = "ConcurrentSequenceExecutor.swift"; - sourceTree = ""; + name = "ObjCBridges"; + path = "Sources/ObjCBridges"; + sourceTree = "SOURCE_ROOT"; }; "OBJ_2" = { isa = "XCConfigurationList"; @@ -191,68 +182,71 @@ }; "OBJ_20" = { isa = "PBXFileReference"; - path = "ImmediateSerialSequenceExecutor.swift"; + path = "AtomicBridges.m"; sourceTree = ""; }; "OBJ_21" = { - isa = "PBXFileReference"; - path = "SequenceExecutor.swift"; + isa = "PBXGroup"; + children = ( + "OBJ_22", + "OBJ_23" + ); + name = "include"; + path = "include"; sourceTree = ""; }; "OBJ_22" = { isa = "PBXFileReference"; - path = "Task.swift"; + path = "AtomicBridges.h"; sourceTree = ""; }; "OBJ_23" = { + isa = "PBXFileReference"; + name = "module.modulemap"; + path = "/Users/yiw/Uber/GitHub/swift-concurrency/Concurrency.xcodeproj/GeneratedModuleMap/ObjCBridges/module.modulemap"; + sourceTree = ""; + }; + "OBJ_24" = { isa = "PBXGroup"; children = ( - "OBJ_24" + "OBJ_25" ); name = "Tests"; path = ""; sourceTree = "SOURCE_ROOT"; }; - "OBJ_24" = { + "OBJ_25" = { isa = "PBXGroup"; children = ( - "OBJ_25", "OBJ_26", "OBJ_27", "OBJ_28", - "OBJ_29" + "OBJ_29", + "OBJ_30", + "OBJ_31" ); name = "ConcurrencyTests"; path = "Tests/ConcurrencyTests"; sourceTree = "SOURCE_ROOT"; }; - "OBJ_25" = { - isa = "PBXFileReference"; - path = "AtomicBoolTests.swift"; - sourceTree = ""; - }; "OBJ_26" = { isa = "PBXFileReference"; - path = "AtomicIntTests.swift"; + path = "AtomicBoolTests.swift"; sourceTree = ""; }; "OBJ_27" = { isa = "PBXFileReference"; - path = "AtomicReferenceTests.swift"; + path = "AtomicIntTests.swift"; sourceTree = ""; }; "OBJ_28" = { isa = "PBXFileReference"; - path = "CountDownLatchTests.swift"; + path = "AtomicReferenceTests.swift"; sourceTree = ""; }; "OBJ_29" = { - isa = "PBXGroup"; - children = ( - "OBJ_30" - ); - name = "Executor"; - path = "Executor"; + isa = "PBXFileReference"; + path = "AutoReleasingSemaphoreTests.swift"; sourceTree = ""; }; "OBJ_3" = { @@ -296,30 +290,44 @@ }; "OBJ_30" = { isa = "PBXFileReference"; - path = "ConcurrentSequenceExecutorTests.swift"; + path = "CountDownLatchTests.swift"; sourceTree = ""; }; "OBJ_31" = { isa = "PBXGroup"; children = ( + "OBJ_32" + ); + name = "Executor"; + path = "Executor"; + sourceTree = ""; + }; + "OBJ_32" = { + isa = "PBXFileReference"; + path = "ConcurrentSequenceExecutorTests.swift"; + sourceTree = ""; + }; + "OBJ_33" = { + isa = "PBXGroup"; + children = ( + "Concurrency::ConcurrencyTests::Product", "Concurrency::Concurrency::Product", - "Concurrency::ObjCBridges::Product", - "Concurrency::ConcurrencyTests::Product" + "Concurrency::ObjCBridges::Product" ); name = "Products"; path = ""; sourceTree = "BUILT_PRODUCTS_DIR"; }; - "OBJ_36" = { + "OBJ_38" = { isa = "XCConfigurationList"; buildConfigurations = ( - "OBJ_37", - "OBJ_38" + "OBJ_39", + "OBJ_40" ); defaultConfigurationIsVisible = "0"; defaultConfigurationName = "Release"; }; - "OBJ_37" = { + "OBJ_39" = { isa = "XCBuildConfiguration"; buildSettings = { ENABLE_TESTABILITY = "YES"; @@ -360,7 +368,39 @@ }; name = "Debug"; }; - "OBJ_38" = { + "OBJ_4" = { + isa = "XCBuildConfiguration"; + buildSettings = { + CLANG_ENABLE_OBJC_ARC = "YES"; + COMBINE_HIDPI_IMAGES = "YES"; + COPY_PHASE_STRIP = "YES"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_OPTIMIZATION_LEVEL = "s"; + MACOSX_DEPLOYMENT_TARGET = "10.10"; + OTHER_SWIFT_FLAGS = ( + "-DXcode" + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = "macosx"; + SUPPORTED_PLATFORMS = ( + "macosx", + "iphoneos", + "iphonesimulator", + "appletvos", + "appletvsimulator", + "watchos", + "watchsimulator" + ); + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( + "SWIFT_PACKAGE" + ); + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + USE_HEADERMAP = "NO"; + }; + name = "Release"; + }; + "OBJ_40" = { isa = "XCBuildConfiguration"; buildSettings = { ENABLE_TESTABILITY = "YES"; @@ -401,118 +441,91 @@ }; name = "Release"; }; - "OBJ_39" = { + "OBJ_41" = { isa = "PBXSourcesBuildPhase"; files = ( - "OBJ_40", - "OBJ_41", "OBJ_42", "OBJ_43", "OBJ_44", "OBJ_45", "OBJ_46", - "OBJ_47" + "OBJ_47", + "OBJ_48", + "OBJ_49", + "OBJ_50" ); }; - "OBJ_4" = { - isa = "XCBuildConfiguration"; - buildSettings = { - CLANG_ENABLE_OBJC_ARC = "YES"; - COMBINE_HIDPI_IMAGES = "YES"; - COPY_PHASE_STRIP = "YES"; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - GCC_OPTIMIZATION_LEVEL = "s"; - MACOSX_DEPLOYMENT_TARGET = "10.10"; - OTHER_SWIFT_FLAGS = ( - "-DXcode" - ); - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = "macosx"; - SUPPORTED_PLATFORMS = ( - "macosx", - "iphoneos", - "iphonesimulator", - "appletvos", - "appletvsimulator", - "watchos", - "watchsimulator" - ); - SWIFT_ACTIVE_COMPILATION_CONDITIONS = ( - "SWIFT_PACKAGE" - ); - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - USE_HEADERMAP = "NO"; - }; - name = "Release"; - }; - "OBJ_40" = { - isa = "PBXBuildFile"; - fileRef = "OBJ_14"; - }; - "OBJ_41" = { - isa = "PBXBuildFile"; - fileRef = "OBJ_15"; - }; "OBJ_42" = { isa = "PBXBuildFile"; - fileRef = "OBJ_16"; + fileRef = "OBJ_9"; }; "OBJ_43" = { isa = "PBXBuildFile"; - fileRef = "OBJ_17"; + fileRef = "OBJ_10"; }; "OBJ_44" = { isa = "PBXBuildFile"; - fileRef = "OBJ_19"; + fileRef = "OBJ_11"; }; "OBJ_45" = { isa = "PBXBuildFile"; - fileRef = "OBJ_20"; + fileRef = "OBJ_12"; }; "OBJ_46" = { isa = "PBXBuildFile"; - fileRef = "OBJ_21"; + fileRef = "OBJ_13"; }; "OBJ_47" = { isa = "PBXBuildFile"; - fileRef = "OBJ_22"; + fileRef = "OBJ_15"; }; "OBJ_48" = { - isa = "PBXFrameworksBuildPhase"; - files = ( - "OBJ_49" - ); + isa = "PBXBuildFile"; + fileRef = "OBJ_16"; }; "OBJ_49" = { isa = "PBXBuildFile"; - fileRef = "Concurrency::ObjCBridges::Product"; + fileRef = "OBJ_17"; }; "OBJ_5" = { isa = "PBXGroup"; children = ( "OBJ_6", "OBJ_7", - "OBJ_23", - "OBJ_31" + "OBJ_24", + "OBJ_33" ); path = ""; sourceTree = ""; }; "OBJ_50" = { + isa = "PBXBuildFile"; + fileRef = "OBJ_18"; + }; + "OBJ_51" = { + isa = "PBXFrameworksBuildPhase"; + files = ( + "OBJ_52" + ); + }; + "OBJ_52" = { + isa = "PBXBuildFile"; + fileRef = "Concurrency::ObjCBridges::Product"; + }; + "OBJ_53" = { isa = "PBXTargetDependency"; target = "Concurrency::ObjCBridges"; }; - "OBJ_53" = { + "OBJ_56" = { isa = "XCConfigurationList"; buildConfigurations = ( - "OBJ_54", - "OBJ_55" + "OBJ_57", + "OBJ_58" ); defaultConfigurationIsVisible = "0"; defaultConfigurationName = "Release"; }; - "OBJ_54" = { + "OBJ_57" = { isa = "XCBuildConfiguration"; buildSettings = { LD = "/usr/bin/true"; @@ -530,7 +543,7 @@ }; name = "Debug"; }; - "OBJ_55" = { + "OBJ_58" = { isa = "XCBuildConfiguration"; buildSettings = { LD = "/usr/bin/true"; @@ -548,57 +561,57 @@ }; name = "Release"; }; - "OBJ_56" = { + "OBJ_59" = { isa = "PBXSourcesBuildPhase"; files = ( - "OBJ_57" + "OBJ_60" ); }; - "OBJ_57" = { + "OBJ_6" = { + isa = "PBXFileReference"; + explicitFileType = "sourcecode.swift"; + path = "Package.swift"; + sourceTree = ""; + }; + "OBJ_60" = { isa = "PBXBuildFile"; fileRef = "OBJ_6"; }; - "OBJ_59" = { + "OBJ_62" = { isa = "XCConfigurationList"; buildConfigurations = ( - "OBJ_60", - "OBJ_61" + "OBJ_63", + "OBJ_64" ); defaultConfigurationIsVisible = "0"; defaultConfigurationName = "Release"; }; - "OBJ_6" = { - isa = "PBXFileReference"; - explicitFileType = "sourcecode.swift"; - path = "Package.swift"; - sourceTree = ""; - }; - "OBJ_60" = { + "OBJ_63" = { isa = "XCBuildConfiguration"; buildSettings = { }; name = "Debug"; }; - "OBJ_61" = { + "OBJ_64" = { isa = "XCBuildConfiguration"; buildSettings = { }; name = "Release"; }; - "OBJ_62" = { + "OBJ_65" = { isa = "PBXTargetDependency"; target = "Concurrency::ConcurrencyTests"; }; - "OBJ_64" = { + "OBJ_67" = { isa = "XCConfigurationList"; buildConfigurations = ( - "OBJ_65", - "OBJ_66" + "OBJ_68", + "OBJ_69" ); defaultConfigurationIsVisible = "0"; defaultConfigurationName = "Release"; }; - "OBJ_65" = { + "OBJ_68" = { isa = "XCBuildConfiguration"; buildSettings = { CLANG_ENABLE_MODULES = "YES"; @@ -637,7 +650,7 @@ }; name = "Debug"; }; - "OBJ_66" = { + "OBJ_69" = { isa = "XCBuildConfiguration"; buildSettings = { CLANG_ENABLE_MODULES = "YES"; @@ -676,79 +689,98 @@ }; name = "Release"; }; - "OBJ_67" = { - isa = "PBXSourcesBuildPhase"; - files = ( - "OBJ_68", - "OBJ_69", - "OBJ_70", - "OBJ_71", - "OBJ_72" - ); - }; - "OBJ_68" = { - isa = "PBXBuildFile"; - fileRef = "OBJ_25"; - }; - "OBJ_69" = { - isa = "PBXBuildFile"; - fileRef = "OBJ_26"; - }; "OBJ_7" = { isa = "PBXGroup"; children = ( "OBJ_8", - "OBJ_13" + "OBJ_19" ); name = "Sources"; path = ""; sourceTree = "SOURCE_ROOT"; }; "OBJ_70" = { + isa = "PBXSourcesBuildPhase"; + files = ( + "OBJ_71", + "OBJ_72", + "OBJ_73", + "OBJ_74", + "OBJ_75", + "OBJ_76" + ); + }; + "OBJ_71" = { + isa = "PBXBuildFile"; + fileRef = "OBJ_26"; + }; + "OBJ_72" = { isa = "PBXBuildFile"; fileRef = "OBJ_27"; }; - "OBJ_71" = { + "OBJ_73" = { isa = "PBXBuildFile"; fileRef = "OBJ_28"; }; - "OBJ_72" = { + "OBJ_74" = { + isa = "PBXBuildFile"; + fileRef = "OBJ_29"; + }; + "OBJ_75" = { isa = "PBXBuildFile"; fileRef = "OBJ_30"; }; - "OBJ_73" = { + "OBJ_76" = { + isa = "PBXBuildFile"; + fileRef = "OBJ_32"; + }; + "OBJ_77" = { isa = "PBXFrameworksBuildPhase"; files = ( - "OBJ_74", - "OBJ_75" + "OBJ_78", + "OBJ_79" ); }; - "OBJ_74" = { + "OBJ_78" = { isa = "PBXBuildFile"; fileRef = "Concurrency::Concurrency::Product"; }; - "OBJ_75" = { + "OBJ_79" = { isa = "PBXBuildFile"; fileRef = "Concurrency::ObjCBridges::Product"; }; - "OBJ_76" = { + "OBJ_8" = { + isa = "PBXGroup"; + children = ( + "OBJ_9", + "OBJ_10", + "OBJ_11", + "OBJ_12", + "OBJ_13", + "OBJ_14" + ); + name = "Concurrency"; + path = "Sources/Concurrency"; + sourceTree = "SOURCE_ROOT"; + }; + "OBJ_80" = { isa = "PBXTargetDependency"; target = "Concurrency::Concurrency"; }; - "OBJ_77" = { + "OBJ_81" = { isa = "PBXTargetDependency"; target = "Concurrency::ObjCBridges"; }; - "OBJ_78" = { + "OBJ_82" = { isa = "XCConfigurationList"; buildConfigurations = ( - "OBJ_79", - "OBJ_80" + "OBJ_83", + "OBJ_84" ); defaultConfigurationIsVisible = "0"; defaultConfigurationName = "Release"; }; - "OBJ_79" = { + "OBJ_83" = { isa = "XCBuildConfiguration"; buildSettings = { DEFINES_MODULE = "NO"; @@ -786,17 +818,7 @@ }; name = "Debug"; }; - "OBJ_8" = { - isa = "PBXGroup"; - children = ( - "OBJ_9", - "OBJ_10" - ); - name = "ObjCBridges"; - path = "Sources/ObjCBridges"; - sourceTree = "SOURCE_ROOT"; - }; - "OBJ_80" = { + "OBJ_84" = { isa = "XCBuildConfiguration"; buildSettings = { DEFINES_MODULE = "NO"; @@ -834,24 +856,24 @@ }; name = "Release"; }; - "OBJ_81" = { + "OBJ_85" = { isa = "PBXSourcesBuildPhase"; files = ( - "OBJ_82" + "OBJ_86" ); }; - "OBJ_82" = { + "OBJ_86" = { isa = "PBXBuildFile"; - fileRef = "OBJ_9"; + fileRef = "OBJ_20"; }; - "OBJ_83" = { + "OBJ_87" = { isa = "PBXFrameworksBuildPhase"; files = ( ); }; "OBJ_9" = { isa = "PBXFileReference"; - path = "AtomicBridges.m"; + path = "AtomicBool.swift"; sourceTree = ""; }; }; diff --git a/Sources/Concurrency/AutoReleasingSemaphore.swift b/Sources/Concurrency/AutoReleasingSemaphore.swift new file mode 100644 index 0000000..67b7db6 --- /dev/null +++ b/Sources/Concurrency/AutoReleasingSemaphore.swift @@ -0,0 +1,83 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// Similar to `DispatchSemaphore`, `AutoReleasingSemaphore` is a +/// synchronization mechanism that ensures only a set number of threads can +/// concurrently access a protected resource. Unlike `DispatchSemaphore`, +/// `AutoReleasingSemaphore` auto-releases all blocked threads when the +/// semaphore itself is deallocated. +public class AutoReleasingSemaphore { + + /// Initializer. + /// + /// - parameter value: The starting value for the semaphore. Do not + /// pass a value less than zero. + public init(value: Int) { + semaphore = DispatchSemaphore(value: value) + } + + /// Signals, or increments, a semaphore. + /// + /// - note: Increment the counting semaphore. If the previous value + /// was less than zero, this function wakes a thread currently waiting + /// in dispatch_semaphore_wait(_:_:). + /// - returns: This function returns non-zero if a thread is woken. + /// Otherwise, zero is returned. + public func signal() -> Int { + let newValue = waitingCount.decrementAndGet() + if newValue < 0 { + waitingCount.value = 0 + } + return semaphore.signal() + } + + /// Waits for, or decrements, a semaphore. + /// + /// - note: Decrement the counting semaphore. If the resulting value + /// is less than zero, this function waits for a signal to occur + /// before returning. + public func wait() { + waitingCount.incrementAndGet() + semaphore.wait() + } + + /// Waits for, or decrements, a semaphore for up to the specified + /// time. + /// + /// - note: Decrement the counting semaphore. If the resulting value + /// is less than zero, this function waits for a signal to occur + /// before returning. + /// - parameter timeout: The amount of time in seconds to wait + /// before returning with failure. + /// - returns: The waiting result. + public func wait(timeout: TimeInterval) -> DispatchTimeoutResult { + waitingCount.incrementAndGet() + return semaphore.wait(timeout: DispatchTime.now() + timeout) + } + + deinit { + for _ in 0 ..< waitingCount.value { + semaphore.signal() + } + } + + // MARK: - Private + + private let semaphore: DispatchSemaphore + private let waitingCount = AtomicInt(initialValue: 0) +} diff --git a/Tests/ConcurrencyTests/AutoReleasingSemaphoreTests.swift b/Tests/ConcurrencyTests/AutoReleasingSemaphoreTests.swift new file mode 100644 index 0000000..23991d6 --- /dev/null +++ b/Tests/ConcurrencyTests/AutoReleasingSemaphoreTests.swift @@ -0,0 +1,67 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +@testable import Concurrency + +class AutoReleasingSemaphoreTests: XCTestCase { + + func test_wait_releaseAfterWait_verifyAutoRelease() { + var semaphore: AutoReleasingSemaphore? = AutoReleasingSemaphore(value: 1) + + let autoreleaseExpectation = expectation(description: "autoreleaseExpectation") + DispatchQueue.global(qos: .background).async { + semaphore!.wait() + autoreleaseExpectation.fulfill() + } + + Thread.sleep(forTimeInterval: 1) + semaphore = nil + + waitForExpectations(timeout: 5, handler: nil) + } + + func test_waitWithTimeout_verifyTimeout() { + let semaphore = AutoReleasingSemaphore(value: 0) + + let waitExpectation = expectation(description: "waitExpectation") + DispatchQueue.global(qos: .background).async { + let result = semaphore.wait(timeout: 1) + XCTAssertEqual(result, DispatchTimeoutResult.timedOut) + waitExpectation.fulfill() + } + + waitForExpectations(timeout: 5, handler: nil) + } + + func test_wait_releaseAfterWait_overSignaling_verifyAutoRelease() { + var semaphore: AutoReleasingSemaphore? = AutoReleasingSemaphore(value: 1) + for _ in 0 ..< 1000 { + _ = semaphore!.signal() + } + + let autoreleaseExpectation = expectation(description: "autoreleaseExpectation") + DispatchQueue.global(qos: .background).async { + semaphore!.wait() + autoreleaseExpectation.fulfill() + } + + Thread.sleep(forTimeInterval: 1) + semaphore = nil + + waitForExpectations(timeout: 5, handler: nil) + } +}