Skip to content

Commit

Permalink
switch to nshashtable
Browse files Browse the repository at this point in the history
  • Loading branch information
kosyloa committed Oct 5, 2023
1 parent 4cba5f7 commit 506c2e3
Show file tree
Hide file tree
Showing 13 changed files with 54 additions and 48 deletions.
8 changes: 4 additions & 4 deletions DXFeedFramework.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@
641BCBC12A21077800FE23C2 /* EventCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641BCBC02A21077800FE23C2 /* EventCode.swift */; };
641BDD582AC71CCE00236B78 /* LatencyTestCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641BDD572AC71CCE00236B78 /* LatencyTestCommand.swift */; };
641BDD592AC7215200236B78 /* LatencyDiagnostic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6417DD1E2A39C9A7008912D6 /* LatencyDiagnostic.swift */; };
641BDD5B2AC72BD400236B78 /* ConcurrentWeakHashTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641BDD5A2AC72BD400236B78 /* ConcurrentWeakHashTable.swift */; };
641BDD5D2ACD67A000236B78 /* LatencyListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641BDD5C2ACD67A000236B78 /* LatencyListener.swift */; };
641BDD5E2ACD67A400236B78 /* LatencyListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641BDD5C2ACD67A000236B78 /* LatencyListener.swift */; };
641BDD612ACD697B00236B78 /* AbstractEventListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641BDD602ACD697B00236B78 /* AbstractEventListener.swift */; };
641BDD622ACD697B00236B78 /* AbstractEventListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641BDD602ACD697B00236B78 /* AbstractEventListener.swift */; };
641BDD5B2AC72BD400236B78 /* ConcurentWeakSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 641BDD5A2AC72BD400236B78 /* ConcurentWeakSet.swift */; };
642528D02A3C534D00A04E41 /* TimeInterval+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6469F8CD2A3B2F9900846831 /* TimeInterval+Ext.swift */; };
642528D12A3C534D00A04E41 /* TimeInterval+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6469F8CD2A3B2F9900846831 /* TimeInterval+Ext.swift */; };
64262CCE2A4DA64700BA6BA3 /* RealityKitContent in Frameworks */ = {isa = PBXBuildFile; platformFilters = (xros, ); productRef = 64262CCD2A4DA64700BA6BA3 /* RealityKitContent */; };
Expand Down Expand Up @@ -516,9 +516,9 @@
641BCBBB2A20ED8100FE23C2 /* DXEndpointObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DXEndpointObserver.swift; sourceTree = "<group>"; };
641BCBC02A21077800FE23C2 /* EventCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventCode.swift; sourceTree = "<group>"; };
641BDD572AC71CCE00236B78 /* LatencyTestCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatencyTestCommand.swift; sourceTree = "<group>"; };
641BDD5A2AC72BD400236B78 /* ConcurrentWeakHashTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConcurrentWeakHashTable.swift; sourceTree = "<group>"; };
641BDD5C2ACD67A000236B78 /* LatencyListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatencyListener.swift; sourceTree = "<group>"; };
641BDD602ACD697B00236B78 /* AbstractEventListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbstractEventListener.swift; sourceTree = "<group>"; };
641BDD5A2AC72BD400236B78 /* ConcurentWeakSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConcurentWeakSet.swift; sourceTree = "<group>"; };
64262CC92A4DA64600BA6BA3 /* DXVisionQuoteTableApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DXVisionQuoteTableApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
64262CCC2A4DA64700BA6BA3 /* RealityKitContent */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = RealityKitContent; sourceTree = "<group>"; };
64262CCF2A4DA64700BA6BA3 /* VisionQuoteTableAppApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisionQuoteTableAppApp.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1009,7 +1009,7 @@
64656F642A1CAFC5006A0B19 /* String+Pointee.swift */,
64656F6A2A1CFAC2006A0B19 /* BridgeUtil.swift */,
64656F6E2A1CFC12006A0B19 /* WeakBox.swift */,
641BDD5A2AC72BD400236B78 /* ConcurentWeakSet.swift */,
641BDD5A2AC72BD400236B78 /* ConcurrentWeakHashTable.swift */,
64104FC42A26059B00D1FC41 /* ConcurrentSet.swift */,
64104FC62A2613BC00D1FC41 /* ConcurrentArray.swift */,
6469F8C12A3B169A00846831 /* MathUtil.swift */,
Expand Down Expand Up @@ -2087,7 +2087,7 @@
8088D77329C3A2F400F240CB /* GraalException.swift in Sources */,
64C771FF2A9504ED009868C2 /* SnapshotProcessor.swift in Sources */,
64C771F82A94B88C009868C2 /* TimeAndSaleType.swift in Sources */,
641BDD5B2AC72BD400236B78 /* ConcurentWeakSet.swift in Sources */,
641BDD5B2AC72BD400236B78 /* ConcurrentWeakHashTable.swift in Sources */,
6447A5DF2A8E56FC00739CCF /* IIndexedEvent.swift in Sources */,
64104FC52A26059B00D1FC41 /* ConcurrentSet.swift in Sources */,
64BA925F2A306B9600BE26A0 /* Profile.swift in Sources */,
Expand Down
15 changes: 7 additions & 8 deletions DXFeedFramework/Api/DXEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,7 @@ public class DXEndpoint {
DXPublisher()
}()
/// A list of state change listeners callback. observersSet - not typed variable(as storage).
/// observers - typed list wrapper.
private var observersSet = ConcurrentWeakSet<AnyObject>()
private var observers: [DXEndpointObserver] {
return observersSet.reader {
$0.allObjects.compactMap { value in value as? DXEndpointObserver }
}
}
private var observersSet = ConcurrentWeakHashTable<DXEndpointObserver>()

private static var instances = [Role: DXEndpoint]()

Expand Down Expand Up @@ -512,6 +506,11 @@ public class Builder {

extension DXEndpoint: EndpointListener {
func changeState(old: DXEndpointState, new: DXEndpointState) {
observers.forEach { $0.endpointDidChangeState(old: old, new: new) }
observersSet.reader {
let enumerator = $0.objectEnumerator()
while let observer = enumerator.nextObject() as? DXEndpointObserver {
observer.endpointDidChangeState(old: old, new: new)
}
}
}
}
7 changes: 5 additions & 2 deletions DXFeedFramework/Api/DXFeedSubcription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class DXFeedSubcription {
fileprivate let events: Set<EventCode>
/// A set listeners of events
/// observers - typed list wrapper.
private let listeners = ConcurrentWeakSet<AnyObject>()
private let listeners = ConcurrentWeakHashTable<AnyObject>()

/// - Throws: ``GraalException`` Rethrows exception from Java, ``ArgumentException/argumentNil``
internal init(native: NativeSubscription?, events: [EventCode]) throws {
Expand Down Expand Up @@ -96,7 +96,10 @@ public class DXFeedSubcription {
extension DXFeedSubcription: DXEventListener {
public func receiveEvents(_ events: [MarketEvent]) {
listeners.reader { items in
items.allObjects.compactMap { $0 as? DXEventListener }.forEach { $0.receiveEvents(events) }
let enumerator = items.objectEnumerator()
while let observer = enumerator.nextObject() as? DXEventListener {
observer.receiveEvents(events)
}
}
}
}
Expand Down
9 changes: 5 additions & 4 deletions DXFeedFramework/Ipf/Live/DXInstrumentProfileCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import Foundation
/// Removal of instrument profile is represented by an ``InstrumentProfile`` instance with a
/// ``InstrumentProfile/type`` equal to ``InstrumentProfileType/removed``
public class DXInstrumentProfileCollector {
private let listeners = ConcurrentWeakSet<AnyObject>()
private let listeners = ConcurrentWeakHashTable<AnyObject>()
let native: NativeInstrumentProfileCollector

/// Creates instrument profile connection.
Expand Down Expand Up @@ -128,9 +128,10 @@ public class DXInstrumentProfileCollector {
extension DXInstrumentProfileCollector: DXInstrumentProfileUpdateListener {
public func instrumentProfilesUpdated(_ instruments: [InstrumentProfile]) {
listeners.reader { items in
items.allObjects.compactMap {
$0 as? DXInstrumentProfileUpdateListener
}.forEach { $0.instrumentProfilesUpdated(instruments) }
let enumerator = items.objectEnumerator()
while let observer = enumerator.nextObject() as? DXInstrumentProfileUpdateListener {
observer.instrumentProfilesUpdated(instruments)
}
}
}
}
12 changes: 7 additions & 5 deletions DXFeedFramework/Ipf/Live/DXInstrumentProfileConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ public class DXInstrumentProfileConnection {
private let native: NativeInstrumentProfileConnection
private let collector: DXInstrumentProfileCollector

private var observersSet = ConcurrentWeakSet<AnyObject>()
private var observers: [DXInstrumentProfileConnectionObserver] {
return observersSet.reader { $0.allObjects.compactMap { value in value as? DXInstrumentProfileConnectionObserver } }
}
private var observersSet = ConcurrentWeakHashTable<AnyObject>()

/// Creates instrument profile connection with a specified address and collector.
///
Expand Down Expand Up @@ -136,6 +133,11 @@ public class DXInstrumentProfileConnection {

extension DXInstrumentProfileConnection: NativeIPFConnectionListener {
func connectionDidChangeState(old: DXInstrumentProfileConnectionState, new: DXInstrumentProfileConnectionState) {
observers.forEach { $0.connectionDidChangeState(old: old, new: new) }
observersSet.reader { items in
let enumerator = items.objectEnumerator()
while let observer = enumerator.nextObject() as? DXInstrumentProfileConnection {
observer.connectionDidChangeState(old: old, new: new)
}
}
}
}
2 changes: 1 addition & 1 deletion DXFeedFramework/Native/Endpoint/NativeEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class NativeEndpoint {
func getNativeFeed() -> NativeFeed? {
return self.feed
}
func addListener(_ listener: any EndpointListener) throws {
func addListener(_ listener: EndpointListener) throws {
removeListener()
let weakListener = WeakListener(value: listener)
NativeEndpoint.listeners.append(newElement: weakListener)
Expand Down
1 change: 0 additions & 1 deletion DXFeedFramework/Utils/ConcurrentSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public class ConcurrentSet<T>: CustomStringConvertible where T: Hashable {
}

public func insert(_ newMember: T) {
let weakValue = WeakBox(value: newMember)
writer { $0.insert(newMember) }
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,39 @@
//
// ConcurentWeakSet.swift
// ConcurrentWeakHashTable.swift
// DXFeedFramework
//
// Created by Aleksey Kosylo on 29.09.23.
//

import Foundation

public class ConcurrentWeakSet<T: AnyObject> {
private var set: NSHashTable<T> = .weakObjects()
public class ConcurrentWeakHashTable<T> {
internal var set: NSHashTable<AnyObject> = .weakObjects()
private let accessQueue = DispatchQueue(label: "com.dxfeed.set_nshashtable", attributes: .concurrent)

public var count: Int {
reader { $0.count }
}

public func insert(_ newMember: T) {
writer { $0.add(newMember) }
writer {
$0.add(newMember as AnyObject)
}
}

public func remove(_ member: T) {
writer { $0.remove(member) }
writer { $0.remove(member as AnyObject) }
}

public func removeAll() {
writer { $0.removeAllObjects() }
}

public func reader<U>(_ block: (NSHashTable<T>) throws -> U) rethrows -> U {
public func reader<U>(_ block: (NSHashTable<AnyObject>) throws -> U) rethrows -> U {
try accessQueue.sync { try block(set) }
}

public func writer(_ block: @escaping (inout NSHashTable<T>) -> Void) {
public func writer(_ block: @escaping (inout NSHashTable<AnyObject>) -> Void) {
accessQueue.async(flags: .barrier) { block(&self.set) }
}
}
3 changes: 2 additions & 1 deletion DXFeedFrameworkTests/DXFeedFrameworkTests.xctestplan
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
],
"environmentVariableEntries" : [

]
],
"testRepetitionMode" : "retryOnFailure"
},
"testTargets" : [
{
Expand Down
2 changes: 1 addition & 1 deletion DXFeedFrameworkTests/EndpointPublisherTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ final class EndpointPublisherTest: XCTestCase {
DXEndpointState.connecting: expectation(description: "Connecting")]
let listener = TestListener(expectations: expectations)
endpoint?.add(observer: listener)
try endpoint?.connect(":4700")
try endpoint?.connect(":4777")
let exps = Array(expectations.filter({ element in
element.key != .notConnected
}).values)
Expand Down
3 changes: 2 additions & 1 deletion DXFeedFrameworkTests/EndpointTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ final class EndpointTest: XCTestCase {
DXEndpointState.notConnected: expectation(description: "NotConnected")]
let listener = TestListener(expectations: expectations)
endpoint?.add(observer: listener)
try endpoint?.connect(endpointAddress)
let exps = Array(expectations.filter({ element in
element.key != .notConnected
}).values)
try endpoint?.connect(endpointAddress)
wait(for: exps, timeout: 1)

try endpoint?.disconnect()
let expsNotConnected = Array(expectations.filter({ element in
element.key == .notConnected
Expand Down
21 changes: 11 additions & 10 deletions DXFeedFrameworkTests/Listeners/TestListener.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,7 @@ import Foundation
import XCTest
@testable import DXFeedFramework

class TestListener: DXEndpointObserver, Hashable {
static func == (lhs: TestListener, rhs: TestListener) -> Bool {
return lhs.expectations == rhs.expectations
}

func hash(into hasher: inout Hasher) {
hasher.combine(expectations)
}

var state = DXEndpointState.notConnected
class TestListener: DXEndpointObserver {
var expectations: [DXEndpointState: XCTestExpectation]
init(expectations: [DXEndpointState: XCTestExpectation]) {
self.expectations = expectations
Expand All @@ -31,3 +22,13 @@ class TestListener: DXEndpointObserver, Hashable {
}
}
}

extension TestListener: Hashable {
static func == (lhs: TestListener, rhs: TestListener) -> Bool {
return lhs.expectations == rhs.expectations
}

func hash(into hasher: inout Hasher) {
hasher.combine(expectations)
}
}
3 changes: 0 additions & 3 deletions Samples/PerfTestCL/ConnectEventListener.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,3 @@ class ConnectEventListener: AbstractEventListener {
}
}
}



0 comments on commit 506c2e3

Please sign in to comment.