| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- //
- // ConcurrencyTests.swift
- // KeychainSwift
- //
- // Created by Eli Kohen on 08/02/2017.
- // Copyright © 2017 Marketplacer. All rights reserved.
- //
- import XCTest
- class ConcurrencyTests: XCTestCase {
- var obj: KeychainSwift!
- override func setUp() {
- super.setUp()
- obj = KeychainSwift()
- obj.clear()
- obj.lastQueryParameters = nil
- obj.synchronizable = false
- }
- // MARK: - addSynchronizableIfRequired
- func testConcurrencyDoesntCrash() {
- let expectation = self.expectation(description: "Wait for write loop")
- let dataToWrite = "{ asdf ñlk BNALSKDJFÑLAKSJDFÑLKJ ZÑCLXKJ ÑALSKDFJÑLKASJDFÑLKJASDÑFLKJAÑSDLKFJÑLKJ}"
- obj.set(dataToWrite, forKey: "test-key")
- var writes = 0
- let readQueue = DispatchQueue(label: "ReadQueue", attributes: [])
- readQueue.async {
- for _ in 0..<400 {
- let _: String? = synchronize( { completion in
- let result: String? = self.obj.get("test-key")
- DispatchQueue.global(qos: .background).async {
- DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
- completion(result)
- }
- }
- }, timeoutWith: nil)
- }
- }
- let readQueue2 = DispatchQueue(label: "ReadQueue2", attributes: [])
- readQueue2.async {
- for _ in 0..<400 {
- let _: String? = synchronize( { completion in
- let result: String? = self.obj.get("test-key")
- DispatchQueue.global(qos: .background).async {
- DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
- completion(result)
- }
- }
- }, timeoutWith: nil)
- }
- }
- let readQueue3 = DispatchQueue(label: "ReadQueue3", attributes: [])
- readQueue3.async {
- for _ in 0..<400 {
- let _: String? = synchronize( { completion in
- let result: String? = self.obj.get("test-key")
- DispatchQueue.global(qos: .background).async {
- DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
- completion(result)
- }
- }
- }, timeoutWith: nil)
- }
- }
- let writeQueue = DispatchQueue(label: "WriteQueue", attributes: [])
- writeQueue.async {
- for _ in 0..<500 {
- let written: Bool = synchronize({ completion in
- DispatchQueue.global(qos: .background).async {
- DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
- let result = self.obj.set(dataToWrite, forKey: "test-key")
- completion(result)
- }
- }
- }, timeoutWith: false)
- if written {
- writes = writes + 1
- }
- }
- expectation.fulfill()
- }
- for _ in 0..<1000 {
- self.obj.set(dataToWrite, forKey: "test-key")
- let _ = self.obj.get("test-key")
- }
- self.waitForExpectations(timeout: 20, handler: nil)
- XCTAssertEqual(500, writes)
- }
- }
- // Synchronizes a asynch closure
- // Ref: https://forums.developer.apple.com/thread/11519
- func synchronize<ResultType>(_ asynchClosure: (_ completion: @escaping (ResultType) -> ()) -> Void,
- timeout: DispatchTime = DispatchTime.distantFuture, timeoutWith: @autoclosure @escaping () -> ResultType) -> ResultType {
- let sem = DispatchSemaphore(value: 0)
- var result: ResultType?
- asynchClosure { (r: ResultType) -> () in
- result = r
- sem.signal()
- }
- _ = sem.wait(timeout: timeout)
- if result == nil {
- result = timeoutWith()
- }
- return result!
- }
|