Skip to content

Commit 0f9547c

Browse files
committed
SR-13822: FileHandle closeOnDealloc is not respected
1 parent 908227d commit 0f9547c

File tree

2 files changed

+50
-14
lines changed

2 files changed

+50
-14
lines changed

Sources/Foundation/FileHandle.swift

+15-13
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ open class FileHandle : NSObject {
477477
// - deinit tries to .sync { … } to serialize the work on the handle queue, _which we're already on_
478478
// - deadlock! DispatchQueue's deadlock detection triggers and crashes us.
479479
// since all operations on the handle queue retain the handle during use, if the handle is being deinited, then there are no more operations on the queue, so this is serial with respect to them anyway. Just close the handle immediately.
480-
try? _immediatelyClose()
480+
try? _immediatelyClose(closeFd: _closeOnDealloc)
481481
}
482482

483483
// MARK: -
@@ -620,7 +620,7 @@ open class FileHandle : NSObject {
620620
}
621621
}
622622

623-
private func _immediatelyClose() throws {
623+
private func _immediatelyClose(closeFd: Bool = true) throws {
624624
guard self != FileHandle._nulldeviceFileHandle else { return }
625625
guard _isPlatformHandleValid else { return }
626626

@@ -632,18 +632,20 @@ open class FileHandle : NSObject {
632632
writabilitySource = nil
633633
readabilitySource = nil
634634
privateAsyncVariablesLock.unlock()
635-
636-
#if os(Windows)
637-
guard CloseHandle(self._handle) else {
638-
throw _NSErrorWithWindowsError(GetLastError(), reading: true)
639-
}
640-
self._handle = INVALID_HANDLE_VALUE
641-
#else
642-
guard _close(_fd) >= 0 else {
643-
throw _NSErrorWithErrno(errno, reading: true)
635+
636+
if closeFd {
637+
#if os(Windows)
638+
guard CloseHandle(self._handle) else {
639+
throw _NSErrorWithWindowsError(GetLastError(), reading: true)
640+
}
641+
self._handle = INVALID_HANDLE_VALUE
642+
#else
643+
guard _close(_fd) >= 0 else {
644+
throw _NSErrorWithErrno(errno, reading: true)
645+
}
646+
_fd = -1
647+
#endif
644648
}
645-
_fd = -1
646-
#endif
647649
}
648650

649651
// MARK: -

Tests/Foundation/Tests/TestFileHandle.swift

+35-1
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,40 @@ class TestFileHandle : XCTestCase {
579579
XCTAssertTrue(notificationReceived, "Notification should be sent")
580580
}
581581
}
582-
582+
583+
#if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT
584+
func test_closeOnDealloc() throws {
585+
try withTemporaryDirectory() { (url, path) in
586+
let data = try XCTUnwrap("hello".data(using: .utf8))
587+
588+
// closeOnDealloc: true, 2nd write should throw.
589+
do {
590+
let fileUrl = url.appendingPathComponent("testfile")
591+
let fd = try FileHandle._openFileDescriptorForURL(fileUrl, flags: O_CREAT | O_RDWR, reading: false)
592+
do {
593+
let fh = FileHandle(fileDescriptor: fd, closeOnDealloc: true)
594+
XCTAssertNoThrow(try fh.write(contentsOf: data))
595+
}
596+
let fh2 = FileHandle(fileDescriptor: fd, closeOnDealloc: true)
597+
XCTAssertThrowsError(try fh2.write(contentsOf: data))
598+
}
599+
600+
// closeOnDealloc: false, 2nd write should succeed.
601+
do {
602+
let fileUrl = url.appendingPathComponent("testfile2")
603+
let fd = try FileHandle._openFileDescriptorForURL(fileUrl, flags: O_CREAT | O_RDWR, reading: false)
604+
do {
605+
let fh = FileHandle(fileDescriptor: fd, closeOnDealloc: false)
606+
XCTAssertNoThrow(try fh.write(contentsOf: data))
607+
}
608+
// Close the file handle after this write, dont leave it open after this test.
609+
let fh2 = FileHandle(fileDescriptor: fd, closeOnDealloc: true)
610+
XCTAssertNoThrow(try fh2.write(contentsOf: data))
611+
}
612+
}
613+
}
614+
#endif
615+
583616
static var allTests : [(String, (TestFileHandle) -> () throws -> ())] {
584617
var tests: [(String, (TestFileHandle) -> () throws -> ())] = [
585618
("testReadUpToCount", testReadUpToCount),
@@ -607,6 +640,7 @@ class TestFileHandle : XCTestCase {
607640
("test_nullDevice", test_nullDevice),
608641
("testHandleCreationAndCleanup", testHandleCreationAndCleanup),
609642
("testOffset", testOffset),
643+
("test_closeOnDealloc", test_closeOnDealloc),
610644
])
611645
#endif
612646

0 commit comments

Comments
 (0)