ConcurrencyTests.swift 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. //
  2. // ConcurrencyTests.swift
  3. // KeychainSwift
  4. //
  5. // Created by Eli Kohen on 08/02/2017.
  6. //
  7. import XCTest
  8. @testable import KeychainSwift
  9. class ConcurrencyTests: XCTestCase {
  10. var obj: KeychainSwift!
  11. override func setUp() {
  12. super.setUp()
  13. obj = KeychainSwift()
  14. obj.clear()
  15. obj.lastQueryParameters = nil
  16. obj.synchronizable = false
  17. }
  18. // MARK: - addSynchronizableIfRequired
  19. func testConcurrencyDoesntCrash() {
  20. let expectation = self.expectation(description: "Wait for write loop")
  21. let expectation2 = self.expectation(description: "Wait for write loop")
  22. let dataToWrite = "{ asdf ñlk BNALSKDJFÑLAKSJDFÑLKJ ZÑCLXKJ ÑALSKDFJÑLKASJDFÑLKJASDÑFLKJAÑSDLKFJÑLKJ}"
  23. obj.set(dataToWrite, forKey: "test-key")
  24. var writes = 0
  25. let readQueue = DispatchQueue(label: "ReadQueue", attributes: [])
  26. readQueue.async {
  27. for _ in 0..<400 {
  28. let _: String? = synchronize( { completion in
  29. let result: String? = self.obj.get("test-key")
  30. DispatchQueue.global(qos: .background).async {
  31. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
  32. completion(result)
  33. }
  34. }
  35. }, timeoutWith: nil)
  36. }
  37. }
  38. let readQueue2 = DispatchQueue(label: "ReadQueue2", attributes: [])
  39. readQueue2.async {
  40. for _ in 0..<400 {
  41. let _: String? = synchronize( { completion in
  42. let result: String? = self.obj.get("test-key")
  43. DispatchQueue.global(qos: .background).async {
  44. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
  45. completion(result)
  46. }
  47. }
  48. }, timeoutWith: nil)
  49. }
  50. }
  51. let readQueue3 = DispatchQueue(label: "ReadQueue3", attributes: [])
  52. readQueue3.async {
  53. for _ in 0..<400 {
  54. let _: String? = synchronize( { completion in
  55. let result: String? = self.obj.get("test-key")
  56. DispatchQueue.global(qos: .background).async {
  57. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
  58. completion(result)
  59. }
  60. }
  61. }, timeoutWith: nil)
  62. }
  63. }
  64. let deleteQueue = DispatchQueue(label: "deleteQueue", attributes: [])
  65. deleteQueue.async {
  66. for _ in 0..<400 {
  67. let _: Bool = synchronize( { completion in
  68. let result = self.obj.delete("test-key")
  69. DispatchQueue.global(qos: .background).async {
  70. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
  71. completion(result)
  72. }
  73. }
  74. }, timeoutWith: false)
  75. }
  76. }
  77. let deleteQueue2 = DispatchQueue(label: "deleteQueue2", attributes: [])
  78. deleteQueue2.async {
  79. for _ in 0..<400 {
  80. let _: Bool = synchronize( { completion in
  81. let result = self.obj.delete("test-key")
  82. DispatchQueue.global(qos: .background).async {
  83. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
  84. completion(result)
  85. }
  86. }
  87. }, timeoutWith: false)
  88. }
  89. }
  90. let clearQueue = DispatchQueue(label: "clearQueue", attributes: [])
  91. clearQueue.async {
  92. for _ in 0..<400 {
  93. let _: Bool = synchronize( { completion in
  94. let result = self.obj.clear()
  95. DispatchQueue.global(qos: .background).async {
  96. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
  97. completion(result)
  98. }
  99. }
  100. }, timeoutWith: false)
  101. }
  102. }
  103. let clearQueue2 = DispatchQueue(label: "clearQueue2", attributes: [])
  104. clearQueue2.async {
  105. for _ in 0..<400 {
  106. let _: Bool = synchronize( { completion in
  107. let result = self.obj.clear()
  108. DispatchQueue.global(qos: .background).async {
  109. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
  110. completion(result)
  111. }
  112. }
  113. }, timeoutWith: false)
  114. }
  115. }
  116. let writeQueue = DispatchQueue(label: "WriteQueue", attributes: [])
  117. writeQueue.async {
  118. for _ in 0..<500 {
  119. let written: Bool = synchronize({ completion in
  120. DispatchQueue.global(qos: .background).async {
  121. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
  122. let result = self.obj.set(dataToWrite, forKey: "test-key")
  123. completion(result)
  124. }
  125. }
  126. }, timeoutWith: false)
  127. if written {
  128. writes = writes + 1
  129. }
  130. }
  131. expectation.fulfill()
  132. }
  133. let writeQueue2 = DispatchQueue(label: "WriteQueue2", attributes: [])
  134. writeQueue2.async {
  135. for _ in 0..<500 {
  136. let written: Bool = synchronize({ completion in
  137. DispatchQueue.global(qos: .background).async {
  138. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5)) {
  139. let result = self.obj.set(dataToWrite, forKey: "test-key")
  140. completion(result)
  141. }
  142. }
  143. }, timeoutWith: false)
  144. if written {
  145. writes = writes + 1
  146. }
  147. }
  148. expectation2.fulfill()
  149. }
  150. for _ in 0..<1000 {
  151. self.obj.set(dataToWrite, forKey: "test-key")
  152. let _ = self.obj.get("test-key")
  153. }
  154. self.waitForExpectations(timeout: 30, handler: nil)
  155. XCTAssertEqual(1000, writes)
  156. }
  157. }
  158. // Synchronizes a asynch closure
  159. // Ref: https://forums.developer.apple.com/thread/11519
  160. func synchronize<ResultType>(_ asynchClosure: (_ completion: @escaping (ResultType) -> ()) -> Void,
  161. timeout: DispatchTime = DispatchTime.distantFuture, timeoutWith: @autoclosure @escaping () -> ResultType) -> ResultType {
  162. let sem = DispatchSemaphore(value: 0)
  163. var result: ResultType?
  164. asynchClosure { (r: ResultType) -> () in
  165. result = r
  166. sem.signal()
  167. }
  168. _ = sem.wait(timeout: timeout)
  169. if result == nil {
  170. result = timeoutWith()
  171. }
  172. return result!
  173. }