From 03c5f7dd2259ed8dfc9f008e0391088863c094cd Mon Sep 17 00:00:00 2001 From: Ryan Lovelett Date: Wed, 7 Feb 2018 21:57:43 -0500 Subject: [PATCH] Create a scaffold implementation of FileHandle#waitForDataInBackgroundAndNotify This change alone is still insufficient. It needs a few details to be addressed. 1. From the docs it says the function must be called from a thread that has an active run loop. How does one go about checking if a thread has an active run-loop? Would that be an assert or precondition or something else entirely? 2. From the docs it says the function will post the NSFileHandleDataAvailable notification on the current thread. Presumably that means the thread that was used to call waitForDataInBackgroundAndNotify in the first place. How do you specify the thread when posting a notification? 3. Tests. Are there any suggestions for tests? --- Foundation/FileHandle.swift | 15 ++++++++++++++- TestFoundation/TestFileHandle.swift | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Foundation/FileHandle.swift b/Foundation/FileHandle.swift index 2f31d44e72..663c13fb43 100644 --- a/Foundation/FileHandle.swift +++ b/Foundation/FileHandle.swift @@ -8,6 +8,7 @@ // import CoreFoundation +import Dispatch #if os(OSX) || os(iOS) import Darwin @@ -345,7 +346,19 @@ extension FileHandle { } open func waitForDataInBackgroundAndNotify() { - NSUnimplemented() + let global = DispatchQueue.global(qos: .background) + let channel = DispatchIO(type: .stream, fileDescriptor: fileDescriptor, queue: global) { _ in } + + // The desire is to trigger the handler blocka as soon as there is any data in the channel. + channel.setLimit(lowWater: 1) + + channel.read(offset: 0, length: Int.max, queue: global) { (_, _, error) in + var notification = Notification(name: .NSFileHandleDataAvailable, object: nil, userInfo: nil) + if error != 0 { + notification.userInfo = ["NSFileHandleError": NSNumber(value: error)] + } + NotificationCenter.default.post(notification) + } } open var readabilityHandler: ((FileHandle) -> Void)? { diff --git a/TestFoundation/TestFileHandle.swift b/TestFoundation/TestFileHandle.swift index 1b389ff0f7..8776a38145 100644 --- a/TestFoundation/TestFileHandle.swift +++ b/TestFoundation/TestFileHandle.swift @@ -21,6 +21,7 @@ class TestFileHandle : XCTestCase { ("test_constants", test_constants), ("test_pipe", test_pipe), ("test_nullDevice", test_nullDevice), + ("test_waitForDataInBackgroundAndNotify", test_waitForDataInBackgroundAndNotify), ] } @@ -64,4 +65,21 @@ class TestFileHandle : XCTestCase { fh.seek(toFileOffset: 0) XCTAssertEqual(fh.readDataToEndOfFile().count, 0) } + + func test_waitForDataInBackgroundAndNotify() { + let expect = expectation(description: "Receiving asynchronous data from pipe") + let pipe = Pipe() + pipe.fileHandleForReading.waitForDataInBackgroundAndNotify() + + NotificationCenter.default.addObserver(forName: .NSFileHandleDataAvailable, object: nil, queue: nil) { (notification) in + XCTAssertEqual(notification.name, .NSFileHandleDataAvailable) + XCTAssertNil(notification.userInfo) + expect.fulfill() + } + + let data = Data(bytes: [0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21]) + pipe.fileHandleForWriting.write(data) + + waitForExpectations(timeout: 2) + } }