ConcurrencyTests.swift 6.7 KB

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