Search code examples
iosswiftpipe

Unable to open more than 2560 Pipe() in Swift (even though I close them)


I have an issue that I don't understand, and I need help. I am using Pipe() in Swift, and I found that if I create more than 2560 (more or less) pipes, I cannot create more. The call fails with Too many open files, even though I closed all the files associated with the pipes.

This is a minimal code to reproduce the issue:

for i in 0...1278 {
    NSLog("Testing: \(i)")
    let stdin_pipe = Pipe()
    let stdin_file = fdopen(stdin_pipe.fileHandleForReading.fileDescriptor, "r")
    var stdout_pipe = Pipe()
    var stdout_file = fdopen(stdout_pipe.fileHandleForWriting.fileDescriptor, "w")
    if (stdout_file == nil) {
        let errorString = String(cString: strerror(errno))
        NSLog("Could not create an output stream. Error: \(errorString)")
    }
    do {
        // I'm really trying to close everything (but it doesn't matter):
        close(stdout_pipe.fileHandleForWriting.fileDescriptor)
        close(stdin_pipe.fileHandleForReading.fileDescriptor)
        try stdout_pipe.fileHandleForWriting.close()
        try stdin_pipe.fileHandleForReading.close()
        try stdout_pipe.fileHandleForReading.close()
        try stdin_pipe.fileHandleForWriting.close()
    }
    catch {
        NSLog("Error in closing pipes in MyExtension: \(error.localizedDescription)")
    }
}

It will work for i going to 0 to 1277, and fail for i == 1278. The error (obtained from strerror(errno) is Too many files open, even though there are around 6 file descriptors opened (I checked).

What am I doing wrong?


Solution

  • From fopen(3) (emphasis added):

    The fdopen() function associates a stream with the existing file descriptor, fildes. ... When the stream is closed via fclose(3), fildes is closed also.

    A call to fdopen() must be balanced with a call to fclose(), not close(). The docs say that closing the stream closes the file descriptor. They do not say that closing the file descriptor closes the stream.

    If you do that, everything is balanced and there is no error. Replace this:

    do {
        // I'm really trying to close everything (but it doesn't matter):
        close(stdout_pipe.fileHandleForWriting.fileDescriptor)
        close(stdin_pipe.fileHandleForReading.fileDescriptor)
        try stdout_pipe.fileHandleForWriting.close()
        try stdin_pipe.fileHandleForReading.close()
        try stdout_pipe.fileHandleForReading.close()
        try stdin_pipe.fileHandleForWriting.close()
    }
    catch {
        NSLog("Error in closing pipes in MyExtension: \(error.localizedDescription)")
    }
    

    with this:

    fclose(stdin_file)
    fclose(stdout_file)