-
Notifications
You must be signed in to change notification settings - Fork 72
Implemented recursive lock using pthread mutex #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import PackageDescription | ||
|
|
||
| let package = Package( | ||
| name: "DipLinuxTests", | ||
| targets: [], | ||
| dependencies: [ | ||
| .Package(url: "https://github.com/AliSoftware/Dip", majorVersion: 4, minor: 2), | ||
| ] | ||
| ) | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| import Glibc | ||
| import XCTest | ||
| import Dip | ||
|
|
||
|
|
||
| private protocol Service: class { | ||
| var client: Client? { get set } | ||
| } | ||
|
|
||
| private protocol Client: class { | ||
| var service: Service { get } | ||
| } | ||
|
|
||
| private class ClientImp: Client, Equatable { | ||
| var service: Service | ||
| init(service: Service) { | ||
| self.service = service | ||
| } | ||
| } | ||
|
|
||
| private func ==<T: ClientImp>(lhs: T, rhs: T) -> Bool { | ||
| return lhs === rhs | ||
| } | ||
|
|
||
| private class ServiceImp: Service, Hashable { | ||
| weak var client: Client? | ||
| init() {} | ||
|
|
||
| var hashValue: Int { | ||
| return unsafeAddressOf(self).hashValue | ||
| } | ||
| } | ||
|
|
||
| private func ==<T: ServiceImp>(lhs: T, rhs: T) -> Bool { | ||
| return lhs === rhs | ||
| } | ||
|
|
||
|
|
||
| typealias TMain = @convention(c) (UnsafeMutablePointer<Void>) -> UnsafeMutablePointer<Void> | ||
|
|
||
| func dispatch_async(block: TMain) { | ||
| var pid: pthread_t = 0 | ||
| pthread_create(&pid, nil, block, nil) | ||
| } | ||
|
|
||
| func dispatch_sync(block: TMain) -> UnsafeMutablePointer<Void> { | ||
| var pid: pthread_t = 0 | ||
| var result: UnsafeMutablePointer<Void> = nil | ||
| pthread_create(&pid, nil, block, nil) | ||
| pthread_join(pid, &result) | ||
| return result | ||
| } | ||
|
|
||
| private var resolvedServices = Set<ServiceImp>() | ||
| private var resolvedClients = Array<ClientImp>() | ||
|
|
||
| private var lock: pthread_spinlock_t = 0 | ||
| pthread_spin_init(&lock, 0) | ||
|
|
||
| private var container: DependencyContainer! | ||
|
|
||
| class ThreadSafetyTests: XCTestCase { | ||
|
|
||
| var allTests: [(String, () throws -> Void)] { | ||
| return [ | ||
| ("testSingletonThreadSafety", testSingletonThreadSafety), | ||
| ("testFactoryThreadSafety", testFactoryThreadSafety), | ||
| ("testCircularReferenceThreadSafety", testCircularReferenceThreadSafety) | ||
| ] | ||
| } | ||
|
|
||
| func setUp() { | ||
| container = DependencyContainer() | ||
| } | ||
|
|
||
| func tearDown() { | ||
| } | ||
|
|
||
| func testSingletonThreadSafety() { | ||
| container.register(.Singleton) { ServiceImp() as Service } | ||
|
|
||
| for _ in 0..<100 { | ||
| dispatch_async { _ in | ||
| let service = try! container.resolve() as Service | ||
| pthread_spin_lock(&lock) | ||
| resolvedServices.insert(service as! ServiceImp) | ||
| pthread_spin_unlock(&lock) | ||
| return nil | ||
| } | ||
| } | ||
|
|
||
| sleep(1) | ||
| XCTAssertEqual(resolvedServices.count, 1, "Should create only one instance") | ||
| resolvedServices.removeAll() | ||
| } | ||
|
|
||
|
|
||
| func testFactoryThreadSafety() { | ||
| container.register { ServiceImp() as Service } | ||
|
|
||
| for _ in 0..<100 { | ||
| dispatch_async { _ in | ||
| let service = try! container.resolve() as Service | ||
| pthread_spin_lock(&lock) | ||
| resolvedServices.insert(service as! ServiceImp) | ||
| pthread_spin_unlock(&lock) | ||
| return nil | ||
| } | ||
| } | ||
|
|
||
| sleep(1) | ||
| XCTAssertEqual(resolvedServices.count, 100, "All instances should be different") | ||
| resolvedServices.removeAll() | ||
| } | ||
|
|
||
|
|
||
| func testCircularReferenceThreadSafety() { | ||
| container.register(.ObjectGraph) { ClientImp(service: try container.resolve()) as Client } | ||
|
|
||
| let resolveClient: TMain = { _ in | ||
| let resolved = try! container.resolve() as Client | ||
| return UnsafeMutablePointer(Unmanaged.passUnretained(resolved as! ClientImp).toOpaque()) | ||
| } | ||
| container.register(.ObjectGraph) { ServiceImp() as Service } | ||
| .resolveDependencies { container, service in | ||
| var clientPointer: UnsafeMutablePointer<Void> = nil | ||
| clientPointer = dispatch_sync(resolveClient) | ||
| let client = Unmanaged<ClientImp>.fromOpaque(COpaquePointer(clientPointer)).takeUnretainedValue() | ||
| service.client = client | ||
| } | ||
|
|
||
| for _ in 0..<100 { | ||
| dispatch_async { _ in | ||
| let client = try! container.resolve() as Client | ||
| pthread_spin_lock(&lock) | ||
| resolvedClients.append(client as! ClientImp) | ||
| pthread_spin_unlock(&lock) | ||
| return nil | ||
| } | ||
| } | ||
|
|
||
| sleep(2) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to let al the threads finish. Of course joining each thread would be better (then dispatch_xxx should return thread's pid). Again it's only because of limitations of swift-core libs, in this case xctest does not provide async utils yet. |
||
|
|
||
| XCTAssertEqual(resolvedClients.count, 100, "Instances should be not reused in different object graphs") | ||
| for client in resolvedClients { | ||
| let service = client.service as! ServiceImp | ||
| let serviceClient = service.client as! ClientImp | ||
| XCTAssertEqual(serviceClient, client, "Instances should be reused when resolving single object graph") | ||
| } | ||
|
|
||
| resolvedClients.removeAll() | ||
| } | ||
|
|
||
| } | ||
|
|
||
| XCTMain([ThreadSafetyTests()]) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't both of those be in
Utils.swift?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's only a test code. I did't have a chance to checkout what is there in swift-core-libdispatch, if it is usable I will try to replace it.