Skip to content

Commit bfcb4a8

Browse files
authored
Merge pull request #2932 from spevans/pr_sr_13822
2 parents a1d7c17 + af1c2c7 commit bfcb4a8

File tree

2 files changed

+52
-15
lines changed

2 files changed

+52
-15
lines changed

Sources/Foundation/FileHandle.swift

Lines changed: 17 additions & 14 deletions
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: -
@@ -619,8 +619,9 @@ open class FileHandle : NSObject {
619619
try _immediatelyClose()
620620
}
621621
}
622-
623-
private func _immediatelyClose() throws {
622+
623+
// Shutdown any read/write sources and handlers, and optionally close the file descriptor.
624+
private func _immediatelyClose(closeFd: Bool = true) throws {
624625
guard self != FileHandle._nulldeviceFileHandle else { return }
625626
guard _isPlatformHandleValid else { return }
626627

@@ -632,18 +633,20 @@ open class FileHandle : NSObject {
632633
writabilitySource = nil
633634
readabilitySource = nil
634635
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)
636+
637+
if closeFd {
638+
#if os(Windows)
639+
guard CloseHandle(self._handle) else {
640+
throw _NSErrorWithWindowsError(GetLastError(), reading: true)
641+
}
642+
self._handle = INVALID_HANDLE_VALUE
643+
#else
644+
guard _close(_fd) >= 0 else {
645+
throw _NSErrorWithErrno(errno, reading: true)
646+
}
647+
_fd = -1
648+
#endif
644649
}
645-
_fd = -1
646-
#endif
647650
}
648651

649652
// MARK: -

Tests/Foundation/Tests/TestFileHandle.swift

Lines changed: 35 additions & 1 deletion
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)