Skip to content

Commit 7f522a6

Browse files
Fixing concurrency crash with translation of index based pagination
1 parent 03c0d96 commit 7f522a6

File tree

2 files changed

+65
-2
lines changed

2 files changed

+65
-2
lines changed

Sources/GraphZahl/Resolution/OutputResolvable/Implementations/Connection/IndexConnectionWrapper.swift

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,28 @@
22
import Foundation
33
import NIO
44

5+
private let lock = Lock()
56
private var translators: [ConnectionIdentifier : IndexCursorTranslator] = [:]
67

8+
private func withTranslator<T>(for connectionIdentifier: ConnectionIdentifier,
9+
using type: IndexCursorTranslator.Type,
10+
perform: (inout IndexCursorTranslator) throws -> T) rethrows -> T {
11+
12+
return try lock.withLock {
13+
return try perform(&translators[connectionIdentifier, default: type.init()])
14+
}
15+
}
16+
17+
private func withTranslator<T>(for connectionIdentifier: ConnectionIdentifier,
18+
using type: IndexCursorTranslator.Type,
19+
perform: (IndexCursorTranslator) throws -> T) rethrows -> T {
20+
21+
let translator = lock.withLock {
22+
return translators[connectionIdentifier, default: type.init()]
23+
}
24+
return try perform(translator)
25+
}
26+
727
private struct ConnectionIdentifier: Hashable {
828
let type: Int
929
let connection: AnyHashable
@@ -32,11 +52,15 @@ struct IndexedConnectionWrapper<Connection: IndexedConnection>: ContextBasedConn
3252
}
3353

3454
private func cursor(for index: Int) throws -> String {
35-
return try translators[identifier, default: Connection.Translator()].cursor(for: index)
55+
return try withTranslator(for: identifier, using: Connection.Translator.self) { translator in
56+
return try translator.cursor(for: index)
57+
}
3658
}
3759

3860
private func index(for cursor: String) throws -> Int {
39-
return try translators[identifier, default: Connection.Translator()].index(for: cursor)
61+
return try withTranslator(for: identifier, using: Connection.Translator.self) { translator in
62+
return try translator.index(for: cursor)
63+
}
4064
}
4165

4266
func context(first: Int?, after: String?, last: Int?, before: String?, eventLoop: EventLoopGroup) -> EventLoopFuture<Context> {

Sources/GraphZahl/Utils/Lock.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#if os(Linux)
2+
import Glibc
3+
#else
4+
import Darwin.C
5+
#endif
6+
7+
final class Lock {
8+
private var mutex = pthread_mutex_t()
9+
10+
init() {
11+
let result = pthread_mutex_init(&mutex, nil)
12+
13+
guard result == 0 else {
14+
fatalError("Failed to initialize Lock: \(result)")
15+
}
16+
}
17+
18+
deinit {
19+
pthread_mutex_destroy(&mutex)
20+
}
21+
22+
func withLock<T>(_ closure: () throws -> (T)) rethrows -> T {
23+
acquire()
24+
defer { release() }
25+
return try closure()
26+
}
27+
28+
func acquire() {
29+
let result = pthread_mutex_lock(&mutex)
30+
31+
guard result == 0 else {
32+
fatalError("Failed to aquire Lock: \(result)")
33+
}
34+
}
35+
36+
func release() {
37+
pthread_mutex_unlock(&mutex)
38+
}
39+
}

0 commit comments

Comments
 (0)