diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 32e8c42..34f5d5a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,5 +11,9 @@ jobs: - uses: actions/checkout@v2 - name: Build run: swift build + test: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 - name: Run tests - run: swift test + run: swift test --sanitize=thread \ No newline at end of file diff --git a/README.md b/README.md index d3b3cf3..3d5a2c9 100644 --- a/README.md +++ b/README.md @@ -184,3 +184,13 @@ Once that succeeds, you can do the actual release: ```sh pod trunk push UnleashProxyClientSwift.podspec --allow-warnings ``` + +## Testing + +In order to test this package you can run the swift test command. To test thread safety, run swift test with: + +``` +swift test --sanitize=thread +``` + +This will give you warnings in the console when you have any data races. diff --git a/Sources/UnleashProxyClientSwift/Poller.swift b/Sources/UnleashProxyClientSwift/Poller.swift index 74f9395..3a95d5a 100644 --- a/Sources/UnleashProxyClientSwift/Poller.swift +++ b/Sources/UnleashProxyClientSwift/Poller.swift @@ -22,19 +22,26 @@ public protocol StorageProvider { public class DictionaryStorageProvider: StorageProvider { private var storage: [String: Toggle] = [:] + private let queue = DispatchQueue(label: "com.unleash.storageprovider") public init() {} public func set(value: Toggle?, key: String) { - storage[key] = value + queue.async { + self.storage[key] = value + } } public func value(key: String) -> Toggle? { - return storage[key] + queue.sync { + return self.storage[key] + } } public func clear() { - storage = [:] + queue.sync { + self.storage = [:] + } } } diff --git a/Tests/UnleashProxyClientSwiftTests/UnleashClientIntegrationTest.swift b/Tests/UnleashProxyClientSwiftTests/UnleashClientIntegrationTest.swift index 73a6bae..cb49c72 100644 --- a/Tests/UnleashProxyClientSwiftTests/UnleashClientIntegrationTest.swift +++ b/Tests/UnleashProxyClientSwiftTests/UnleashClientIntegrationTest.swift @@ -45,6 +45,13 @@ class UnleashIntegrationTests: XCTestCase { }); unleashClient.start() + // Run isEnabled many times to trigger a data race when the poller is updating the data cache + // This is to test that the poller is thread safe, and you can verify this by running the test with + // swift test --sanitize=thread + for _ in 1...15000 { + let result = unleashClient.isEnabled(name: "dataRaceTest") + XCTAssertFalse(result) + } wait(for: [expectation], timeout: 5) } diff --git a/UnleashProxyClientSwift.podspec b/UnleashProxyClientSwift.podspec index 684cf00..746ddfb 100644 --- a/UnleashProxyClientSwift.podspec +++ b/UnleashProxyClientSwift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "UnleashProxyClientSwift" -spec.version = "1.1.0" +spec.version = "1.1.1" spec.summary = "Allows frontend clients to talk to unleash through the unleash edge, frontend API or the (deprecated) unleash proxy" spec.homepage = "https://www.getunleash.io" spec.license = { :type => "MIT", :file => "LICENSE" }