Skip to content

Commit

Permalink
[ADD] Thread safe Set object (#25)
Browse files Browse the repository at this point in the history
* [ADD] Thread safe Set

* [TEST] Thread safe contain and remove task

---------

Co-authored-by: N'rick <[email protected]>
  • Loading branch information
JCNrick and N'rick authored Jun 6, 2024
1 parent 24017fd commit ecb14ce
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 0 deletions.
4 changes: 4 additions & 0 deletions NGTools.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
B295F3B4290772E500CB4087 /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = B295F3B3290772E500CB4087 /* CryptoSwift */; };
EDA367DA2BDBF7D90070383D /* VersionRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA367D92BDBF7D90070383D /* VersionRange.swift */; };
EDA367DC2BDBF8430070383D /* VersionRangeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDA367DB2BDBF8430070383D /* VersionRangeTests.swift */; };
F718AA012C10B4E700137A5E /* ThreadSafeSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = F718AA002C10B4E700137A5E /* ThreadSafeSet.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -133,6 +134,7 @@
B295F3AD2907729D00CB4087 /* CryptoRSA.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CryptoRSA.swift; sourceTree = "<group>"; };
EDA367D92BDBF7D90070383D /* VersionRange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionRange.swift; sourceTree = "<group>"; };
EDA367DB2BDBF8430070383D /* VersionRangeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionRangeTests.swift; sourceTree = "<group>"; };
F718AA002C10B4E700137A5E /* ThreadSafeSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadSafeSet.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -208,6 +210,7 @@
A42157BA26EA34CA008C21D2 /* Utils */ = {
isa = PBXGroup;
children = (
F718AA002C10B4E700137A5E /* ThreadSafeSet.swift */,
A4F8E8292A86BD1C0058F809 /* Storage */,
A4C75F4E27BED95B00F752A3 /* Migration */,
A42157BB26EA34CA008C21D2 /* curry.swift */,
Expand Down Expand Up @@ -460,6 +463,7 @@
A421580E26EA350F008C21D2 /* String+Extensions.swift in Sources */,
A421580526EA350F008C21D2 /* URL+Extensions.swift in Sources */,
A4C75F5027BED96E00F752A3 /* MigrationRunner.swift in Sources */,
F718AA012C10B4E700137A5E /* ThreadSafeSet.swift in Sources */,
A421580C26EA350F008C21D2 /* NSDirectionalEdgeInsets+Extensions.swift in Sources */,
A494C0B927C40EC700EA0C37 /* MigrationTaskState.swift in Sources */,
B295F3B02907729D00CB4087 /* CryptoRSA.swift in Sources */,
Expand Down
44 changes: 44 additions & 0 deletions Sources/NGTools/Utils/ThreadSafeSet.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// File.swift
//
//
// Created by Jeanne Creantor, N'rick (Ordinateur) on 2024-06-04.
//

import Foundation

public class ThreadSafeSet<T: Hashable> {
private var set: Set<T> = []
private let queue = DispatchQueue(label: "com.nuglif.safeThreadSet.\(T.self)", attributes: .concurrent)

public init(set: Set<T> = []) {
self.set = set
}

public func insert(_ newElement: T) {
queue.async(flags: .barrier) {
self.set.insert(newElement)
}
}

public func remove(_ member: T) {
queue.async(flags: .barrier) {
self.set.remove(member)
}
}

public func removeAll() {
queue.async(flags: .barrier) {
self.set.removeAll()
}
}

public func contains(_ member: T) -> Bool {
queue.sync { set.contains(member) }
}

public func first(where predicate: (T) -> Bool) -> T? {
queue.sync { set.first(where: predicate) }
}
}

65 changes: 65 additions & 0 deletions Tests/NGToolsTests/ThreadSafeSetTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// ThreadSafeSetTests.swift
//
//
// Created by Jeanne Creantor, N'rick (Ordinateur) on 2024-06-04.
//

import XCTest

import NGTools

final class ThreadSafeSetTests: XCTestCase {

func testConcurrencyInsertionAndRemove() {
let set = ThreadSafeSet<UUID>()
let expectation = XCTestExpectation(description: "Testing")

let queueA = DispatchQueue(label: "TestQueueA", qos: .background)
let queueB = DispatchQueue(label: "TestQueueB", qos: .background)
let iterations = 1000
let requests = Array(repeating: UUID(), count: iterations)
var completed = 0

requests.forEach { request in
queueA.async { set.insert(request) }

queueB.async {
set.remove(request)
completed += 1
if completed == iterations - 1 {
expectation.fulfill()
}
}
}

wait(for: [expectation], timeout: 3)
}

func testConcurrencyContainsAndRemove() {
let expectation = XCTestExpectation(description: "Testing")

let queueA = DispatchQueue(label: "TestQueueA", qos: .background)
let queueB = DispatchQueue(label: "TestQueueB", qos: .background)
let iterations = 1000
let requests = Array(repeating: UUID(), count: iterations)
let set = ThreadSafeSet<UUID>(set: Set(requests))
var completed = 0

requests.forEach { request in
queueA.async {
_ = set.contains(request)
}

queueB.async {
set.remove(request)
completed += 1
if completed == iterations - 1 {
expectation.fulfill()
}
}
}

wait(for: [expectation], timeout: 3)
}
}

0 comments on commit ecb14ce

Please sign in to comment.