diff --git a/ChangeLog b/ChangeLog index ede2fcf3fa..fde5c97df9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,20 @@ -2024-01-08 Richard Frith-Macdonald +2024-01-30 Richard Frith-Macdonald + + * Source/GSFileHandle.m: + * Source/GSPrivate.h: + * Source/GSSocketStream.m: + * Source/GSStream.m: + * Source/unix/GSRunLoopCtxt.m: + Make GSTcpTune options available to streams as well as file handles. + Improve debug logging for streasm operations. + Schedule streams in run loop on conclusion of TLS handshake (so we get + events telling us to start again after a blocked write). + +2024-01-29 Richard Frith-Macdonald * Source/NSOperation.m: patch for issue #366 by Larry Campbell. -2024-01-08 Richard Frith-Macdonald +2024-01-28 Richard Frith-Macdonald * Source/NSJSONSerialization.m: Fix for issue #365 ... don't try to create a string ending with the first half of a unicode surrogate pair. diff --git a/Source/GSFileHandle.m b/Source/GSFileHandle.m index 506d47217a..6595d9719b 100644 --- a/Source/GSFileHandle.m +++ b/Source/GSFileHandle.m @@ -35,6 +35,7 @@ #import "Foundation/NSHost.h" #import "Foundation/NSByteOrder.h" #import "Foundation/NSProcessInfo.h" +#import "Foundation/NSStream.h" #import "Foundation/NSUserDefaults.h" #import "GSPrivate.h" #import "GSNetwork.h" @@ -100,21 +101,14 @@ static GSFileHandle *fh_stdout = nil; static GSFileHandle *fh_stderr = nil; -@interface GSTcpTune : NSObject -- (int) delay; -- (int) recvSize; -- (int) sendSize: (int)bytesToSend; -- (void) tune: (void*)handle; -@end - @implementation GSTcpTune static int tuneDelay = 0; static int tuneLinger = -1; static int tuneReceive = 0; static BOOL tuneSendAll = NO; -static int tuneRBuf = 0; -static int tuneSBuf = 0; +static int tuneRcvBuf = 0; +static int tuneSndBuf = 0; + (void) defaultsChanged: (NSNotification*)n { @@ -134,8 +128,8 @@ + (void) defaultsChanged: (NSNotification*)n { tuneLinger = [str intValue]; } - tuneRBuf = (int)[defs integerForKey: @"GSTcpRcvBuf"]; - tuneSBuf = (int)[defs integerForKey: @"GSTcpSndBuf"]; + tuneRcvBuf = (int)[defs integerForKey: @"GSTcpRcvBuf"]; + tuneSndBuf = (int)[defs integerForKey: @"GSTcpSndBuf"]; tuneReceive = (int)[defs integerForKey: @"GSTcpReceive"]; tuneSendAll = [defs boolForKey: @"GSTcpSendAll"]; tuneDelay = [defs boolForKey: @"GSTcpDelay"]; @@ -161,33 +155,33 @@ + (void) initialize } } -- (int) delay ++ (int) delay { return tuneDelay; // Milliseconds to delay close } -- (int) recvSize ++ (int) recvSize { if (tuneReceive > 0) { return tuneReceive; // Return receive buffer size } - if (tuneRBuf > 0) + if (tuneRcvBuf > 0) { - return tuneRBuf; // Return socket receive buffer size + return tuneRcvBuf; // Return socket receive buffer size } return READ_SIZE; // Return hard-coded default } -- (int) sendSize: (int)bytesToSend ++ (int) sendSize: (int)bytesToSend { if (YES == tuneSendAll) { return bytesToSend; // Try to send all in one go } - if (tuneSBuf > 0 && tuneSBuf <= bytesToSend) + if (tuneSndBuf > 0 && tuneSndBuf <= bytesToSend) { - return tuneSBuf; // Limit to socket send buffer + return tuneSndBuf; // Limit to socket send buffer } if (NETBUF_SIZE <= bytesToSend) { @@ -196,10 +190,25 @@ - (int) sendSize: (int)bytesToSend return bytesToSend; } -- (void) tune: (void*)handle ++ (void) tune: (void*)handle with: (id)opts { int desc = (int)(intptr_t)handle; int value; + id o; + +#ifndef BROKEN_SO_REUSEADDR + /* + * Under decent systems, SO_REUSEADDR means that the port can be reused + * immediately that this process exits. Under some it means + * that multiple processes can serve the same port simultaneously. + * We don't want that broken behavior! + */ + if (setsockopt(desc, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(value)) + < 0) + { + NSDebugMLLog(@"GSTcpTune", @"setsockopt reuseaddr failed for %d", desc); + } +#endif /* * Enable tcp-level tracking of whether connection is alive. @@ -208,10 +217,17 @@ - (void) tune: (void*)handle if (setsockopt(desc, SOL_SOCKET, SO_KEEPALIVE, (char *)&value, sizeof(value)) < 0) { - NSDebugMLLog(@"GSTcpTune", @"setsockopt keepalive failed"); + NSDebugMLLog(@"GSTcpTune", @"setsockopt keepalive failed for %d", desc); } - if (tuneLinger >= 0) + +#define OPTS(X) ([opts isKindOfClass: [NSStream class]] \ + ? [(NSStream*)opts propertyForKey: X] \ + : [(NSDictionary*)opts objectForKey: X]) + + o = OPTS(@"GSTcpLinger"); + value = (o ? [o intValue] : tuneLinger); + if (value >= 0) { struct linger l; @@ -219,40 +235,55 @@ - (void) tune: (void*)handle l.l_linger = tuneLinger; if (setsockopt(desc, SOL_SOCKET, SO_LINGER, (char *)&l, sizeof(l)) < 0) { - NSLog(@"Failed to set GSTcpLinger %d: %@", - tuneLinger, [NSError _last]); + NSLog(@"Failed to set GSTcpLinger for %d to %d: %@", + desc, value, [NSError _last]); + } + else + { + NSDebugMLLog(@"GSTcpTune", @"Set GSTcpLinger for %d to %d", + desc, value); } } - if (tuneRBuf > 0) + o = OPTS(@"GSTcpRcvBuf"); + value = (o ? [o intValue] : tuneRcvBuf); + if (value > 0) { /* Set the receive buffer for the socket. */ if (setsockopt(desc, SOL_SOCKET, SO_RCVBUF, - (char *)&tuneRBuf, sizeof(tuneRBuf)) < 0) + (char *)&value, sizeof(value)) < 0) { - NSLog(@"Failed to set GSTcpRcvBuf %d: %@", tuneRBuf, [NSError _last]); + NSLog(@"Failed to set GSTcpRcvBuf for %d to %d: %@", + desc, value, [NSError _last]); } else { - NSDebugMLLog(@"GSTcpTune", @"Set GSTcpRcvBuf %d", tuneRBuf); + NSDebugMLLog(@"GSTcpTune", @"Set GSTcpRcvBuf for %d to %d", + desc, value); } } - if (tuneSBuf > 0) + + o = OPTS(@"GSTcpSndBuf"); + value = (o ? [o intValue] : tuneSndBuf); + if (value > 0) { /* Set the send buffer for the socket. */ if (setsockopt(desc, SOL_SOCKET, SO_SNDBUF, - (char *)&tuneSBuf, sizeof(tuneSBuf)) < 0) + (char *)&value, sizeof(value)) < 0) { - NSLog(@"Failed to set GSTcpSndBuf %d: %@", tuneSBuf, [NSError _last]); + NSLog(@"Failed to set GSTcpSndBuf for %d to %d: %@", + desc, value, [NSError _last]); } else { - NSDebugMLLog(@"GSTcpTune", @"Set GSTcpSndBuf %d", tuneSBuf); + NSDebugMLLog(@"GSTcpTune", @"Set GSTcpSndBuf for %d to %d", + desc, value); } } } + @end // Key to info dictionary for operation mode. @@ -265,14 +296,9 @@ - (void) receivedEventWrite; @implementation GSFileHandle -static GSTcpTune *tune = nil; - + (void) initialize { - if (nil == tune) - { - tune = [GSTcpTune new]; - } + [GSTcpTune class]; } /** @@ -949,7 +975,7 @@ - (id) initAsClientInBackgroundAtAddress: (NSString*)a return nil; } - [tune tune: (void*)(intptr_t)net]; + [GSTcpTune tune: (void*)(intptr_t)net with: nil]; if (lhost != nil) { @@ -1431,7 +1457,7 @@ - (void*) nativeHandle - (NSData*) availableData { - int rmax = [tune recvSize]; + int rmax = [GSTcpTune recvSize]; char buf[rmax]; NSMutableData* d; int len; @@ -1501,7 +1527,7 @@ - (NSData*) availableData - (NSData*) readDataToEndOfFile { - int rmax = [tune recvSize]; + int rmax = [GSTcpTune recvSize]; char buf[rmax]; NSMutableData* d; int len; @@ -1529,7 +1555,7 @@ - (NSData*) readDataOfLength: (unsigned)len { NSMutableData *d; int got; - int rmax = [tune recvSize]; + int rmax = [GSTcpTune recvSize]; char buf[rmax]; [self checkRead]; @@ -1577,7 +1603,7 @@ - (void) writeData: (NSData*)item { int toWrite = len - pos; - toWrite = [tune sendSize: toWrite]; + toWrite = [GSTcpTune sendSize: toWrite]; rval = [self write: (char*)ptr+pos length: toWrite]; if (rval < 0) { @@ -1807,7 +1833,7 @@ - (void) closeFile #endif if (YES == isSocket) { - int milli = [tune delay]; + int milli = [GSTcpTune delay]; shutdown(descriptor, SHUT_WR); if (milli > 0) @@ -2168,7 +2194,7 @@ - (void) receivedEventRead struct sockaddr sin; unsigned int size = sizeof(sin); - [tune tune: (void*)(intptr_t)desc]; + [GSTcpTune tune: (void*)(intptr_t)desc with: nil]; h = [[[self class] alloc] initWithFileDescriptor: desc closeOnDealloc: YES]; @@ -2192,7 +2218,7 @@ - (void) receivedEventRead NSMutableData *item; int length; int received = 0; - int rmax = [tune recvSize]; + int rmax = [GSTcpTune recvSize]; char buf[rmax]; item = [readInfo objectForKey: NSFileHandleNotificationDataItem]; diff --git a/Source/GSPrivate.h b/Source/GSPrivate.h index f7b07ac954..6696640d15 100644 --- a/Source/GSPrivate.h +++ b/Source/GSPrivate.h @@ -546,6 +546,15 @@ GSPrivateUnloadModule(FILE *errorStream, - (void) setFrame: (id)aFrame; @end +/* For tuning socket connections + */ +@interface GSTcpTune : NSObject ++ (int) delay; ++ (int) recvSize; ++ (int) sendSize: (int)bytesToSend; ++ (void) tune: (void*)handle with: (id)opts; +@end + BOOL GSPrivateIsCollectable(const void *ptr) GS_ATTRIB_PRIVATE; diff --git a/Source/GSSocketStream.m b/Source/GSSocketStream.m index 7e2abb2af3..1d3d7e3be3 100644 --- a/Source/GSSocketStream.m +++ b/Source/GSSocketStream.m @@ -451,14 +451,14 @@ - (void) stream: (NSStream*)stream issuer: (NSString*)i owner: (NSString*)o; { e = [[[tls ostream] streamError] code]; NSDebugFLLog(@"NSStream", - @"GSTLSPush write for %p error %d (%s)", + @"GSTLSPush write for %@ error %d (%s)", [tls ostream], e, strerror(e)); } else { e = EAGAIN; // Tell GNUTLS this would block. NSDebugFLLog(@"NSStream", - @"GSTLSPush write for %p of %lu would block", + @"GSTLSPush write for %@ of %lu would block", [tls ostream], (unsigned long)len); } #if HAVE_GNUTLS_TRANSPORT_SET_ERRNO @@ -470,12 +470,12 @@ - (void) stream: (NSStream*)stream issuer: (NSString*)i owner: (NSString*)o; } if (len != result) { - NSDebugFLLog(@"NSStream", @"GSTLSPush write for %p of %ld (tried %lu)", + NSDebugFLLog(@"NSStream", @"GSTLSPush write for %@ of %ld (tried %lu)", [tls ostream], (long)result, (unsigned long)len); } else { - NSDebugFLLog(@"NSStream", @"GSTLSPush write for %p of %ld success", + NSDebugFLLog(@"NSStream", @"GSTLSPush write for %@ of %ld success", [tls ostream], (long)result); } return result; @@ -768,6 +768,7 @@ - (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)event { [istream _resetEvents: NSStreamEventOpenCompleted]; [istream _sendEvent: NSStreamEventOpenCompleted]; + [istream _schedule]; } else { @@ -780,6 +781,7 @@ - (void) stream: (NSStream*)stream handleEvent: (NSStreamEvent)event | NSStreamEventHasSpaceAvailable]; [ostream _sendEvent: NSStreamEventOpenCompleted]; [ostream _sendEvent: NSStreamEventHasSpaceAvailable]; + [ostream _schedule]; } else { @@ -1853,6 +1855,7 @@ - (void) open } else { + [GSTcpTune tune: (void*)(intptr_t)s with: self]; [self _setSock: s]; [_sibling _setSock: s]; } @@ -2374,6 +2377,7 @@ - (void) open } else { + [GSTcpTune tune: (void*)(intptr_t)s with: self]; [self _setSock: s]; [_sibling _setSock: s]; } @@ -2779,6 +2783,7 @@ - (void) open } else { + [GSTcpTune tune: (void*)(intptr_t)s with: self]; [(GSSocketStream*)self _setSock: s]; } diff --git a/Source/unix/GSRunLoopCtxt.m b/Source/unix/GSRunLoopCtxt.m index 0a67759515..5ad09b75e7 100644 --- a/Source/unix/GSRunLoopCtxt.m +++ b/Source/unix/GSRunLoopCtxt.m @@ -255,6 +255,7 @@ - (BOOL) pollUntil: (int)milliseconds within: (NSArray*)contexts unsigned count; unsigned int i; BOOL immediate = NO; + BOOL debug = GSDebugSet(@"NSRunLoop"); i = GSIArrayCount(watchers); @@ -319,18 +320,21 @@ - (BOOL) pollUntil: (int)milliseconds within: (NSArray*)contexts fd = (int)(intptr_t)info->data; setPollfd(fd, POLLPRI, self); NSMapInsert(_efdMap, (void*)(intptr_t)fd, info); + if (debug) NSLog(@"listening for EDESC %d", fd); break; case ET_RDESC: fd = (int)(intptr_t)info->data; setPollfd(fd, POLLIN, self); NSMapInsert(_rfdMap, (void*)(intptr_t)fd, info); + if (debug) NSLog(@"listening for RDESC %d", fd); break; case ET_WDESC: fd = (int)(intptr_t)info->data; setPollfd(fd, POLLOUT, self); NSMapInsert(_wfdMap, (void*)(intptr_t)fd, info); + if (debug) NSLog(@"listening for WDESC %d", fd); break; case ET_TRIGGER: @@ -353,7 +357,7 @@ - (BOOL) pollUntil: (int)milliseconds within: (NSArray*)contexts port_fd_array = malloc(sizeof(NSInteger)*port_fd_size); [port getFds: port_fd_array count: &port_fd_count]; } - NSDebugMLLog(@"NSRunLoop", + if (debug) NSLog( @"listening to %"PRIdPTR" port handles\n", port_fd_count); while (port_fd_count--) {