KCSession icon indicating copy to clipboard operation
KCSession copied to clipboard

Finish this thing that somebody emailed me one time

Open kconner opened this issue 10 years ago • 0 comments

My email response was as follows:

Aha, you found the unfinished business in KCSession. I'm still surprised it crashed the app rather than merely blocking on large writes. Anyway, I had been wanting to do asynchronous main thread writing but I hadn't put in the time to figure it out.

I'd like to include this code and test it further.

In particular, I want to see how the server behaves when writing multiple large messages. KCFileHandleSession's effective behavior is to just call -write:maxLength: once per message, so sending a few large messages in sequence ought to test this decently.

  1. The docs say:

"To stop writing data to the file or socket, set the value of this property to nil. Doing so cancels the dispatch source and cleans up the file handle’s structures appropriately."

…and that's done as soon as the whole buffer has been run through. Do the "file handle's structures" include the socket? If so, does this mean that the first write works, but if it completes, the second write won't work?

I'm skeptical that this is actually a problem, but I want to check.

  1. Your code creates writeabilityHandler to own the particular NSData to be sent. Does that mean that if you start a second write before the first completes, the first NSData will never finish sending?

If so, I think a suitable fix is this:

- (BOOL)hasSpaceAvailable {
    return self.fileHandle.writeabilityHandler == nil;
}

… and in the handler block:

    if ([pendingData length] == 0) {
        thisFileHandle.writeabilityHandler = nil;
        [self.delegate stream:self handleEvent:NSStreamEventHasSpaceAvailable]
    }

Suggested code was as follows:

- (NSInteger)write:(const uint8_t *)buffer maxLength:(NSUInteger)length {

    NSData *dataToSend = [NSData dataWithBytesNoCopy:(void *)buffer length:length freeWhenDone:NO];

    NSMutableData* pendingData = [[NSMutableData alloc] initWithCapacity:1024];
    [pendingData appendData:dataToSend];

    self.fileHandle.writeabilityHandler = ^(NSFileHandle* thisFileHandle)
    {
        long amountSent = send([self.fileHandle fileDescriptor], [pendingData bytes], [pendingData length], MSG_DONTWAIT);
        if (amountSent < 0) {
            // errno is provided by system
            NSLog(@"Error while sending response: %d", errno);
        } else {
            [pendingData replaceBytesInRange:NSMakeRange(0, amountSent) withBytes:NULL length:0];
        }

        // Finishing
        if ([pendingData length] == 0) {
            thisFileHandle.writeabilityHandler = nil;
        }
    };

    return length;
}

kconner avatar May 07 '14 03:05 kconner