我正在使用NSTask,配置3 NSPipe,并希望从standardOutput和standardError读取。我在里面做 - 第一次为stdout,另一次为stderr。 我不能使用readInBackgroundAndNotify和waitForDataInBackgroundAndNotify,因为我的代码已经在单独的线程中运行了,我不希望在那里运行NSRunLoop并分离新的后台线程......但是读取-stdout和stderr会导致某些挂起而当没有其中一个渠道中存在数据。
所以我使用这段代码:
@implementation NSFileHandle (isReadableAddon)
- (BOOL)isReadable
{
int fd = [self fileDescriptor];
fd_set fdset;
struct timeval tmout = { 0, 0 }; // return immediately
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
if (select(fd + 1, &fdset, NULL, NULL, &tmout) <= 0)
return NO;
return FD_ISSET(fd, &fdset);
}
@end
但是,当isReadable返回YES并且我调用[fileHandle availableData]时,我的线程仍会阻塞。 为什么? 当isReadable返回YES时,我期望调用availableData方法而不会阻塞。
答案 0 :(得分:0)
Swift 4版本(对于macOS):
extension FileHandle {
var isReadable: Bool {
var fdset = fd_set()
FileDescriptor.fdZero(&fdset)
FileDescriptor.fdSet(fileDescriptor, set: &fdset)
var tmout = timeval()
let status = select(fileDescriptor + 1, &fdset, nil, nil, &tmout)
return status > 0
}
}
/// References:
/// - http://swiftrien.blogspot.com/2015/11/swift-code-library-replacements-for.html
/// - https://github.com/kylef-archive/fd/blob/master/Sources/FDSet.swift
public struct FileDescriptor {
public static func fdZero(_ set: inout fd_set) {
set.fds_bits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
}
public static func fdSet(_ fd: Int32, set: inout fd_set) {
let intOffset = Int32(fd / 32)
let bitOffset = fd % 32
let mask = Int32(1) << bitOffset
switch intOffset {
case 0: set.fds_bits.0 = set.fds_bits.0 | mask
case 1: set.fds_bits.1 = set.fds_bits.1 | mask
case 2: set.fds_bits.2 = set.fds_bits.2 | mask
case 3: set.fds_bits.3 = set.fds_bits.3 | mask
case 4: set.fds_bits.4 = set.fds_bits.4 | mask
case 5: set.fds_bits.5 = set.fds_bits.5 | mask
case 6: set.fds_bits.6 = set.fds_bits.6 | mask
case 7: set.fds_bits.7 = set.fds_bits.7 | mask
case 8: set.fds_bits.8 = set.fds_bits.8 | mask
case 9: set.fds_bits.9 = set.fds_bits.9 | mask
case 10: set.fds_bits.10 = set.fds_bits.10 | mask
case 11: set.fds_bits.11 = set.fds_bits.11 | mask
case 12: set.fds_bits.12 = set.fds_bits.12 | mask
case 13: set.fds_bits.13 = set.fds_bits.13 | mask
case 14: set.fds_bits.14 = set.fds_bits.14 | mask
case 15: set.fds_bits.15 = set.fds_bits.15 | mask
case 16: set.fds_bits.16 = set.fds_bits.16 | mask
case 17: set.fds_bits.17 = set.fds_bits.17 | mask
case 18: set.fds_bits.18 = set.fds_bits.18 | mask
case 19: set.fds_bits.19 = set.fds_bits.19 | mask
case 20: set.fds_bits.20 = set.fds_bits.20 | mask
case 21: set.fds_bits.21 = set.fds_bits.21 | mask
case 22: set.fds_bits.22 = set.fds_bits.22 | mask
case 23: set.fds_bits.23 = set.fds_bits.23 | mask
case 24: set.fds_bits.24 = set.fds_bits.24 | mask
case 25: set.fds_bits.25 = set.fds_bits.25 | mask
case 26: set.fds_bits.26 = set.fds_bits.26 | mask
case 27: set.fds_bits.27 = set.fds_bits.27 | mask
case 28: set.fds_bits.28 = set.fds_bits.28 | mask
case 29: set.fds_bits.29 = set.fds_bits.29 | mask
case 30: set.fds_bits.30 = set.fds_bits.30 | mask
case 31: set.fds_bits.31 = set.fds_bits.31 | mask
default: break
}
}
public static func fdClr(_ fd: Int32, set: inout fd_set) {
let intOffset = Int32(fd / 32)
let bitOffset = fd % 32
let mask = ~(Int32(1) << bitOffset)
switch intOffset {
case 0: set.fds_bits.0 = set.fds_bits.0 & mask
case 1: set.fds_bits.1 = set.fds_bits.1 & mask
case 2: set.fds_bits.2 = set.fds_bits.2 & mask
case 3: set.fds_bits.3 = set.fds_bits.3 & mask
case 4: set.fds_bits.4 = set.fds_bits.4 & mask
case 5: set.fds_bits.5 = set.fds_bits.5 & mask
case 6: set.fds_bits.6 = set.fds_bits.6 & mask
case 7: set.fds_bits.7 = set.fds_bits.7 & mask
case 8: set.fds_bits.8 = set.fds_bits.8 & mask
case 9: set.fds_bits.9 = set.fds_bits.9 & mask
case 10: set.fds_bits.10 = set.fds_bits.10 & mask
case 11: set.fds_bits.11 = set.fds_bits.11 & mask
case 12: set.fds_bits.12 = set.fds_bits.12 & mask
case 13: set.fds_bits.13 = set.fds_bits.13 & mask
case 14: set.fds_bits.14 = set.fds_bits.14 & mask
case 15: set.fds_bits.15 = set.fds_bits.15 & mask
case 16: set.fds_bits.16 = set.fds_bits.16 & mask
case 17: set.fds_bits.17 = set.fds_bits.17 & mask
case 18: set.fds_bits.18 = set.fds_bits.18 & mask
case 19: set.fds_bits.19 = set.fds_bits.19 & mask
case 20: set.fds_bits.20 = set.fds_bits.20 & mask
case 21: set.fds_bits.21 = set.fds_bits.21 & mask
case 22: set.fds_bits.22 = set.fds_bits.22 & mask
case 23: set.fds_bits.23 = set.fds_bits.23 & mask
case 24: set.fds_bits.24 = set.fds_bits.24 & mask
case 25: set.fds_bits.25 = set.fds_bits.25 & mask
case 26: set.fds_bits.26 = set.fds_bits.26 & mask
case 27: set.fds_bits.27 = set.fds_bits.27 & mask
case 28: set.fds_bits.28 = set.fds_bits.28 & mask
case 29: set.fds_bits.29 = set.fds_bits.29 & mask
case 30: set.fds_bits.30 = set.fds_bits.30 & mask
case 31: set.fds_bits.31 = set.fds_bits.31 & mask
default: break
}
}
public static func fdIsSet(_ fd: Int32, set: inout fd_set) -> Bool {
let intOffset = Int(fd / 32)
let bitOffset = fd % 32
let mask = Int32(1) << bitOffset
switch intOffset {
case 0: return set.fds_bits.0 & mask != 0
case 1: return set.fds_bits.1 & mask != 0
case 2: return set.fds_bits.2 & mask != 0
case 3: return set.fds_bits.3 & mask != 0
case 4: return set.fds_bits.4 & mask != 0
case 5: return set.fds_bits.5 & mask != 0
case 6: return set.fds_bits.6 & mask != 0
case 7: return set.fds_bits.7 & mask != 0
case 8: return set.fds_bits.8 & mask != 0
case 9: return set.fds_bits.9 & mask != 0
case 10: return set.fds_bits.10 & mask != 0
case 11: return set.fds_bits.11 & mask != 0
case 12: return set.fds_bits.12 & mask != 0
case 13: return set.fds_bits.13 & mask != 0
case 14: return set.fds_bits.14 & mask != 0
case 15: return set.fds_bits.15 & mask != 0
case 16: return set.fds_bits.16 & mask != 0
case 17: return set.fds_bits.17 & mask != 0
case 18: return set.fds_bits.18 & mask != 0
case 19: return set.fds_bits.19 & mask != 0
case 20: return set.fds_bits.20 & mask != 0
case 21: return set.fds_bits.21 & mask != 0
case 22: return set.fds_bits.22 & mask != 0
case 23: return set.fds_bits.23 & mask != 0
case 24: return set.fds_bits.24 & mask != 0
case 25: return set.fds_bits.25 & mask != 0
case 26: return set.fds_bits.26 & mask != 0
case 27: return set.fds_bits.27 & mask != 0
case 28: return set.fds_bits.28 & mask != 0
case 29: return set.fds_bits.29 & mask != 0
case 30: return set.fds_bits.30 & mask != 0
case 31: return set.fds_bits.31 & mask != 0
default: return false
}
}
}
答案 1 :(得分:0)
我也更喜欢使用select
,但是如果您想使用非阻塞式阅读,这里有一个对我有用的要点。
雨燕4
var flags = fcntl(fileDescriptor, F_GETFL)
_ = fcntl(fileDescriptor, F_SETFL, flags | O_NONBLOCK)
public var nonBlockingAvailableData: Data? {
return self.__readDataOfLength(Int.max, untilEOF: false)
}
internal func __readDataOfLength(_ length: Int, untilEOF: Bool) -> Data? {
let _closed: Bool = false
let _fd = self.fileDescriptor
var statbuf = stat()
var dynamicBuffer: UnsafeMutableRawPointer? = nil
var total = 0
if _closed || fstat(_fd, &statbuf) < 0 {
fatalError("Unable to read file")
}
if statbuf.st_mode & S_IFMT != S_IFREG {
/* We get here on sockets, character special files, FIFOs ... */
var currentAllocationSize: size_t = 1024 * 8
dynamicBuffer = malloc(currentAllocationSize)
var remaining = length
while remaining > 0 {
let amountToRead = min(1024 * 8, remaining)
// Make sure there is always at least amountToRead bytes available in the buffer.
if (currentAllocationSize - total) < amountToRead {
currentAllocationSize *= 2
dynamicBuffer = reallocf(dynamicBuffer!, currentAllocationSize)
if dynamicBuffer == nil {
fatalError("unable to allocate backing buffer")
}
}
let amtRead = read(_fd, dynamicBuffer!.advanced(by: total), amountToRead)
//Needs better errorhandling, check ERRNO?
if amtRead == -1 {
return nil
}
if 0 > amtRead {
free(dynamicBuffer)
fatalError("read failure")
}
if 0 == amtRead {
break // EOF
}
total += amtRead
remaining -= amtRead
if total == length || untilEOF == false {
break // We read everything the client asked for.
}
}
}
if length == Int.max && total > 0 {
dynamicBuffer = reallocf(dynamicBuffer!, total)
}
if total == 0 {
free(dynamicBuffer)
}
else if total > 0 {
let bytePtr = dynamicBuffer!.bindMemory(to: UInt8.self, capacity: total)
return Data(bytesNoCopy: bytePtr, count: total, deallocator: .free)
}
else {
assertionFailure("The total number of read bytes must not be negative")
free(dynamicBuffer)
}
return Data()
}
请参见Linux select手册页:
在Linux下,select()可能会报告 套接字文件描述符为“准备用于 阅读”,尽管如此 随后的读取块。这可能 例如,当数据具有 到达但经检查有误 校验和并被丢弃。也许有 在其他情况下,文件 描述符被虚假地报告为 准备。因此使用起来可能更安全 不应在套接字上使用O_NONBLOCK