Skip to content

Commit 6c8cb71

Browse files
committed
fixed file descriptors that were not closed
1 parent fab6f71 commit 6c8cb71

File tree

2 files changed

+50
-17
lines changed

2 files changed

+50
-17
lines changed

Sources/TSCBasic/Lock.swift

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ enum ProcessLockError: Swift.Error {
5050
public final class FileLock {
5151
/// File descriptor to the lock file.
5252
#if os(Windows)
53-
@ThreadLocal private var handle: HANDLE?
53+
@ThreadLocal @AutoClosing private var handle: HANDLE?
5454
#else
55-
@ThreadLocal private var fileDescriptor: CInt?
55+
@ThreadLocal @AutoClosing private var fileDescriptor: CInt?
5656
#endif
5757

5858
/// Path to the lock file.
@@ -136,21 +136,11 @@ public final class FileLock {
136136
overlapped.hEvent = nil
137137
UnlockFileEx(handle, 0, DWORD(INT_MAX), DWORD(INT_MAX), &overlapped)
138138
#else
139-
guard let fd = fileDescriptor else { return }
139+
guard let fd = fileDescriptor else { return }
140140
flock(fd, LOCK_UN)
141141
#endif
142142
}
143143

144-
deinit {
145-
#if os(Windows)
146-
guard let handle = handle else { return }
147-
CloseHandle(handle)
148-
#else
149-
guard let fd = fileDescriptor else { return }
150-
close(fd)
151-
#endif
152-
}
153-
154144
/// Execute the given block while holding the lock.
155145
public func withLock<T>(type: LockType = .exclusive, _ body: () throws -> T) throws -> T {
156146
try lock(type: type)

Sources/TSCBasic/Thread.swift

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010

1111
import Foundation
12+
import TSCLibc
1213

1314
/// This class bridges the gap between Darwin and Linux Foundation Threading API.
1415
/// It provides closure based execution and a join method to block the calling thread
@@ -86,17 +87,59 @@ final private class ThreadImpl: Foundation.Thread {
8687
typealias ThreadImpl = Foundation.Thread
8788
#endif
8889

90+
protocol Defaultable {
91+
static var defaultValue: Self { get }
92+
}
93+
94+
extension Optional: Defaultable {
95+
static var defaultValue: Optional<Wrapped> { .none }
96+
}
97+
8998
/// `ThreadLocal` properties are thread-specific. Every thread has its own instance of the wrapped property.
90-
@propertyWrapper struct ThreadLocal<Value> {
99+
@propertyWrapper final class ThreadLocal<Value: Defaultable> {
91100
private var storage: NSMutableDictionary { ThreadImpl.current.threadDictionary }
92101
private let key = UUID().uuidString
93102

94-
var wrappedValue: Value? {
95-
get { storage[key] as? Value }
103+
var wrappedValue: Value {
104+
get {
105+
if let value = storage[key] as? Value {
106+
return value
107+
} else {
108+
let value = Value.defaultValue
109+
storage[key] = value
110+
return value
111+
}
112+
}
96113
set { storage[key] = newValue }
97114
}
98115

99-
init(wrappedValue: Value?) {
116+
init(wrappedValue: Value) {
117+
self.wrappedValue = wrappedValue
118+
}
119+
}
120+
121+
/// Automatically closes the wrapped file descriptor on deinit.
122+
@propertyWrapper final class AutoClosing: NSObject, Defaultable {
123+
#if os(Windows)
124+
typealias T = HANDLE
125+
#else
126+
typealias T = CInt
127+
#endif
128+
var wrappedValue: T?
129+
130+
static var defaultValue: AutoClosing { AutoClosing(wrappedValue: .none) }
131+
132+
init(wrappedValue: T?) {
100133
self.wrappedValue = wrappedValue
101134
}
135+
136+
deinit {
137+
if let wrappedValue = wrappedValue {
138+
#if os(Windows)
139+
CloseHandle(wrappedValue)
140+
#else
141+
close(wrappedValue)
142+
#endif
143+
}
144+
}
102145
}

0 commit comments

Comments
 (0)