Skip to content

Commit 2a0dbb2

Browse files
committed
SR-13822: FileHandle closeOnDealloc is not respected
1 parent 5b9797f commit 2a0dbb2

File tree

2 files changed

+38
-2
lines changed

2 files changed

+38
-2
lines changed

Sources/Foundation/FileHandle.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,9 @@ 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+
if _closeOnDealloc {
481+
try? _immediatelyClose()
482+
}
481483
}
482484

483485
// 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 shoud 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)