winsock stream fixes

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@23243 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
rfm 2006-08-10 09:15:30 +00:00
parent 8fb233e3dc
commit 6f9c159619
5 changed files with 181 additions and 77 deletions

View file

@ -1,3 +1,11 @@
2006-08-10 Richard Frith-Macdonald <rfm@gnu.org>
* Source\GSStream.h:
* Source\GSStream.m:
* Source\unix\NSStream.m:
* Source\win32\NSStreamWin32.m:
Ensure all events are posted. Fixup for winsock write signalling.
2006-08-09 Richard Frith-Macdonald <rfm@gnu.org> 2006-08-09 Richard Frith-Macdonald <rfm@gnu.org>
* Documentation/Base.gsdoc: Improve links to macros. * Documentation/Base.gsdoc: Improve links to macros.

View file

@ -68,12 +68,12 @@
id _delegate; /* Delegate controls operation. */\ id _delegate; /* Delegate controls operation. */\
NSMutableDictionary *_properties; /* storage for properties */\ NSMutableDictionary *_properties; /* storage for properties */\
BOOL _delegateValid;/* whether the delegate responds*/\ BOOL _delegateValid;/* whether the delegate responds*/\
BOOL _unhandledData; /* no read/write since event */\
NSError *_lastError; /* last error occured */\ NSError *_lastError; /* last error occured */\
NSStreamStatus _currentStatus;/* current status */\ NSStreamStatus _currentStatus;/* current status */\
NSMutableArray *_modes; /* currently scheduled modes. */\ NSMutableArray *_modes; /* currently scheduled modes. */\
NSRunLoop *_runloop; /* currently scheduled loop. */\ NSRunLoop *_runloop; /* currently scheduled loop. */\
void *_loopID; /* file descriptor etc */\ void *_loopID; /* file descriptor etc. */\
int _events; /* Signalled events. */\
} }
/** /**
@ -124,7 +124,7 @@ IVARS
- (void) _recordError; - (void) _recordError;
/** /**
* say whether there is unahdnled data for the stream. * say whether there is unhandled data for the stream.
*/ */
- (BOOL) _unhandledData; - (BOOL) _unhandledData;
@end @end

View file

@ -144,8 +144,11 @@ static RunLoopEventType typeForStream(NSStream *aStream)
[_runloop removeStream: self mode: [_modes objectAtIndex: i]]; [_runloop removeStream: self mode: [_modes objectAtIndex: i]];
} }
} }
_unhandledData = NO;
[self _setStatus: NSStreamStatusClosed]; [self _setStatus: NSStreamStatusClosed];
/* We don't want to send any events the the delegate after the
* stream has been closed.
*/
_delegateValid = NO;
} }
- (void) dealloc - (void) dealloc
@ -213,18 +216,19 @@ static RunLoopEventType typeForStream(NSStream *aStream)
- (void) removeFromRunLoop: (NSRunLoop *)aRunLoop forMode: (NSString *)mode - (void) removeFromRunLoop: (NSRunLoop *)aRunLoop forMode: (NSString *)mode
{ {
NSAssert(_runloop == aRunLoop, if (_runloop == aRunLoop)
@"Attempt to remove unscheduled runloop");
if ([_modes containsObject: mode])
{ {
if ([self _isOpened]) if ([_modes containsObject: mode])
{ {
[_runloop removeStream: self mode: mode]; if ([self _isOpened])
} {
[_modes removeObject: mode]; [_runloop removeStream: self mode: mode];
if ([_modes count] == 0) }
{ [_modes removeObject: mode];
DESTROY(_runloop); if ([_modes count] == 0)
{
DESTROY(_runloop);
}
} }
} }
} }
@ -256,8 +260,15 @@ static RunLoopEventType typeForStream(NSStream *aStream)
{ {
_delegate = self; _delegate = self;
} }
_delegateValid if ([self streamStatus] != NSStreamStatusClosed
= [_delegate respondsToSelector: @selector(stream:handleEvent:)]; && [self streamStatus] != NSStreamStatusError)
{
/* We don't want to send any events the the delegate after the
* stream has been closed.
*/
_delegateValid
= [_delegate respondsToSelector: @selector(stream:handleEvent:)];
}
} }
- (BOOL) setProperty: (id)property forKey: (NSString *)key - (BOOL) setProperty: (id)property forKey: (NSString *)key
@ -359,47 +370,106 @@ static RunLoopEventType typeForStream(NSStream *aStream)
NSStreamStatus last = [self streamStatus]; NSStreamStatus last = [self streamStatus];
NSStreamStatus current; NSStreamStatus current;
if (event == NSStreamEventHasSpaceAvailable if (event == NSStreamEventNone)
|| event == NSStreamEventHasBytesAvailable)
{ {
/* If we have a data event, we mark the stream as having unhandled return;
* data (so we can refrain from triggering again) until a read or
* write operation (as approriate) has been performed.
*/
_unhandledData = YES;
_unhandledData = YES;
} }
if (_delegateValid == YES) else if (event == NSStreamEventOpenCompleted)
{ {
[_delegate stream: self handleEvent: event]; if ((_events & event) == 0)
}
while ((current = [self streamStatus]) != last)
{
last = current;
/* If we our status changed while the handler was dealing with an
* event, we must send it the new event to let it know.
*/
if (current == NSStreamStatusAtEnd)
{ {
_events |= NSStreamEventOpenCompleted;
if (_delegateValid == YES) if (_delegateValid == YES)
{ {
event = NSStreamEventEndEncountered; [_delegate stream: self
[_delegate stream: self handleEvent: event]; handleEvent: NSStreamEventOpenCompleted];
} }
} }
}
else if (event == NSStreamEventHasBytesAvailable)
{
if ((_events & NSStreamEventOpenCompleted) == 0)
{
_events |= NSStreamEventOpenCompleted;
if (_delegateValid == YES)
{
[_delegate stream: self
handleEvent: NSStreamEventOpenCompleted];
}
}
if ((_events & NSStreamEventHasBytesAvailable) == 0)
{
_events |= NSStreamEventHasBytesAvailable;
if (_delegateValid == YES)
{
[_delegate stream: self
handleEvent: NSStreamEventHasBytesAvailable];
}
}
}
else if (event == NSStreamEventHasSpaceAvailable)
{
if ((_events & NSStreamEventOpenCompleted) == 0)
{
_events |= NSStreamEventOpenCompleted;
if (_delegateValid == YES)
{
[_delegate stream: self
handleEvent: NSStreamEventOpenCompleted];
}
}
if ((_events & NSStreamEventHasSpaceAvailable) == 0)
{
_events |= NSStreamEventHasSpaceAvailable;
if (_delegateValid == YES)
{
[_delegate stream: self
handleEvent: NSStreamEventHasSpaceAvailable];
}
}
}
else if (event == NSStreamEventErrorOccurred)
{
if ((_events & NSStreamEventErrorOccurred) == 0)
{
_events |= NSStreamEventErrorOccurred;
if (_delegateValid == YES)
{
[_delegate stream: self
handleEvent: NSStreamEventErrorOccurred];
}
}
}
else if (event == NSStreamEventEndEncountered)
{
if ((_events & NSStreamEventEndEncountered) == 0)
{
_events |= NSStreamEventEndEncountered;
if (_delegateValid == YES)
{
[_delegate stream: self
handleEvent: NSStreamEventEndEncountered];
}
}
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"Unknown event (%d) passed to _sendEvent:", event];
}
/* If our status changed while the handler was dealing with an
* event, we may need to send it the new event to let it know.
*/
if ((current = [self streamStatus]) != last)
{
if (current == NSStreamStatusAtEnd)
{
[self _sendEvent: NSStreamEventEndEncountered];
}
else if (current == NSStreamStatusError) else if (current == NSStreamStatusError)
{ {
if (_delegateValid == YES) [self _sendEvent: NSStreamEventErrorOccurred];
{
event = NSStreamEventErrorOccurred;
[_delegate stream: self handleEvent: event];
}
}
else
{
return; // not an event.
} }
} }
} }
@ -421,12 +491,17 @@ static RunLoopEventType typeForStream(NSStream *aStream)
- (BOOL) _unhandledData - (BOOL) _unhandledData
{ {
return _unhandledData; if (_events
& (NSStreamEventHasBytesAvailable | NSStreamEventHasSpaceAvailable))
{
return YES;
}
return NO;
} }
- (BOOL) runLoopShouldBlock: (BOOL*)trigger - (BOOL) runLoopShouldBlock: (BOOL*)trigger
{ {
if (_unhandledData == YES if ([self _unhandledData] == YES
|| _currentStatus == NSStreamStatusError || _currentStatus == NSStreamStatusError
|| _currentStatus == NSStreamStatusAtEnd) || _currentStatus == NSStreamStatusAtEnd)
{ {
@ -512,7 +587,7 @@ static RunLoopEventType typeForStream(NSStream *aStream)
unsigned long dataSize = [_data length]; unsigned long dataSize = [_data length];
unsigned long copySize; unsigned long copySize;
_unhandledData = NO; _events &= ~NSStreamEventHasSpaceAvailable;
NSAssert(dataSize >= _pointer, @"Buffer overflow!"); NSAssert(dataSize >= _pointer, @"Buffer overflow!");
if (len + _pointer > dataSize) if (len + _pointer > dataSize)
{ {
@ -602,7 +677,7 @@ static RunLoopEventType typeForStream(NSStream *aStream)
- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len - (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len
{ {
_unhandledData = NO; _events &= ~NSStreamEventHasBytesAvailable;
if (_fixedSize) if (_fixedSize)
{ {
unsigned long dataLen = [_data length]; unsigned long dataLen = [_data length];

View file

@ -297,7 +297,7 @@ static void setNonblocking(int fd)
{ {
int readLen; int readLen;
_unhandledData = NO; _events &= ~NSStreamEventHasBytesAvailable;
readLen = read((intptr_t)_loopID, buffer, len); readLen = read((intptr_t)_loopID, buffer, len);
if (readLen < 0 && errno != EAGAIN && errno != EINTR) if (readLen < 0 && errno != EAGAIN && errno != EINTR)
[self _recordError]; [self _recordError];
@ -475,7 +475,7 @@ static void setNonblocking(int fd)
{ {
int readLen; int readLen;
_unhandledData = NO; _events &= ~NSStreamEventHasBytesAvailable;
readLen = read((intptr_t)_loopID, buffer, len); readLen = read((intptr_t)_loopID, buffer, len);
if (readLen < 0 && errno != EAGAIN && errno != EINTR) if (readLen < 0 && errno != EAGAIN && errno != EINTR)
[self _recordError]; [self _recordError];
@ -676,7 +676,7 @@ static void setNonblocking(int fd)
{ {
int writeLen; int writeLen;
_unhandledData = NO; _events &= ~NSStreamEventHasSpaceAvailable;
writeLen = write((intptr_t)_loopID, buffer, len); writeLen = write((intptr_t)_loopID, buffer, len);
if (writeLen < 0 && errno != EAGAIN && errno != EINTR) if (writeLen < 0 && errno != EAGAIN && errno != EINTR)
[self _recordError]; [self _recordError];
@ -790,7 +790,7 @@ static void setNonblocking(int fd)
{ {
int writeLen; int writeLen;
_unhandledData = NO; _events &= ~NSStreamEventHasSpaceAvailable;
writeLen = write((intptr_t)_loopID, buffer, len); writeLen = write((intptr_t)_loopID, buffer, len);
if (writeLen < 0 && errno != EAGAIN && errno != EINTR) if (writeLen < 0 && errno != EAGAIN && errno != EINTR)
[self _recordError]; [self _recordError];
@ -1386,7 +1386,7 @@ static void setNonblocking(int fd)
socklen_t len = [ins sockLen]; socklen_t len = [ins sockLen];
int acceptReturn = accept((intptr_t)_loopID, [ins peerAddr], &len); int acceptReturn = accept((intptr_t)_loopID, [ins peerAddr], &len);
_unhandledData = NO; _events &= ~NSStreamEventHasBytesAvailable;
if (acceptReturn < 0) if (acceptReturn < 0)
{ // test for real error { // test for real error
if (errno != EWOULDBLOCK if (errno != EWOULDBLOCK

View file

@ -330,7 +330,7 @@ static void setNonblocking(SOCKET fd)
{ {
DWORD readLen; DWORD readLen;
_unhandledData = NO; _events &= ~NSStreamEventHasBytesAvailable;
if (ReadFile((HANDLE)_loopID, buffer, len, &readLen, NULL) == 0) if (ReadFile((HANDLE)_loopID, buffer, len, &readLen, NULL) == 0)
{ {
[self _recordError]; [self _recordError];
@ -509,7 +509,7 @@ static void setNonblocking(SOCKET fd)
{ {
NSStreamStatus myStatus = [self streamStatus]; NSStreamStatus myStatus = [self streamStatus];
_unhandledData = NO; _events &= ~NSStreamEventHasBytesAvailable;
if (myStatus == NSStreamStatusReading) if (myStatus == NSStreamStatusReading)
{ {
myStatus = [self _check]; myStatus = [self _check];
@ -595,7 +595,7 @@ static void setNonblocking(SOCKET fd)
{ {
NSStreamStatus myStatus = [self streamStatus]; NSStreamStatus myStatus = [self streamStatus];
if (_unhandledData == YES || myStatus == NSStreamStatusError) if ([self _unhandledData] == YES || myStatus == NSStreamStatusError)
{ {
*trigger = NO; *trigger = NO;
return NO; return NO;
@ -735,7 +735,7 @@ static void setNonblocking(SOCKET fd)
{ {
int readLen; int readLen;
_unhandledData = NO; _events &= ~NSStreamEventHasBytesAvailable;
readLen = recv(_sock, buffer, len, 0); readLen = recv(_sock, buffer, len, 0);
if (readLen == SOCKET_ERROR) if (readLen == SOCKET_ERROR)
{ {
@ -847,18 +847,25 @@ else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self);
{ {
if (events.lNetworkEvents & FD_WRITE) if (events.lNetworkEvents & FD_WRITE)
{ {
NSAssert([_sibling _isOpened], NSInternalInconsistencyException);
/* Clear NSStreamStatusWriting if it was set */
[_sibling _setStatus: NSStreamStatusOpen]; [_sibling _setStatus: NSStreamStatusOpen];
while ([_sibling hasSpaceAvailable]
&& [_sibling _unhandledData] == NO)
{
[_sibling _sendEvent: NSStreamEventHasSpaceAvailable];
}
} }
/* On winsock a socket is always writable unless it has had
* failure/closure or a write blocked and we have not been
* signalled again.
*/
while ([_sibling _unhandledData] == NO
&& [_sibling hasSpaceAvailable])
{
[_sibling _sendEvent: NSStreamEventHasSpaceAvailable];
}
if (events.lNetworkEvents & FD_READ) if (events.lNetworkEvents & FD_READ)
{ {
[self _setStatus: NSStreamStatusOpen]; [self _setStatus: NSStreamStatusOpen];
while ([self hasBytesAvailable] while ([self hasBytesAvailable]
&& _unhandledData == NO) && [self _unhandledData] == NO)
{ {
[self _sendEvent: NSStreamEventHasBytesAvailable]; [self _sendEvent: NSStreamEventHasBytesAvailable];
} }
@ -871,7 +878,7 @@ else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self);
[_sibling _sendEvent: NSStreamEventEndEncountered]; [_sibling _sendEvent: NSStreamEventEndEncountered];
} }
while ([self hasBytesAvailable] while ([self hasBytesAvailable]
&& _unhandledData == NO) && [self _unhandledData] == NO)
{ {
[self _sendEvent: NSStreamEventHasBytesAvailable]; [self _sendEvent: NSStreamEventHasBytesAvailable];
} }
@ -1007,7 +1014,7 @@ else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self);
{ {
DWORD writeLen; DWORD writeLen;
_unhandledData = NO; _events &= ~NSStreamEventHasSpaceAvailable;
if (_shouldAppend == YES) if (_shouldAppend == YES)
{ {
SetFilePointer((HANDLE)_loopID, 0, 0, FILE_END); SetFilePointer((HANDLE)_loopID, 0, 0, FILE_END);
@ -1122,7 +1129,7 @@ else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self);
{ {
NSStreamStatus myStatus = [self streamStatus]; NSStreamStatus myStatus = [self streamStatus];
_unhandledData = NO; _events &= ~NSStreamEventHasSpaceAvailable;
if (len < 0) if (len < 0)
{ {
return -1; return -1;
@ -1214,7 +1221,7 @@ else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self);
{ {
NSStreamStatus myStatus = [self streamStatus]; NSStreamStatus myStatus = [self streamStatus];
if (_unhandledData == YES || myStatus == NSStreamStatusError) if ([self _unhandledData] == YES || myStatus == NSStreamStatusError)
{ {
*trigger = NO; *trigger = NO;
return NO; return NO;
@ -1285,7 +1292,7 @@ else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self);
{ {
int writeLen; int writeLen;
_unhandledData = NO; _events &= ~NSStreamEventHasSpaceAvailable;
writeLen = send(_sock, buffer, len, 0); writeLen = send(_sock, buffer, len, 0);
if (writeLen == SOCKET_ERROR) if (writeLen == SOCKET_ERROR)
{ {
@ -1466,13 +1473,19 @@ else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self);
{ {
if (events.lNetworkEvents & FD_WRITE) if (events.lNetworkEvents & FD_WRITE)
{ {
/* Clear NSStreamStatusWriting if it was set */
[self _setStatus: NSStreamStatusOpen]; [self _setStatus: NSStreamStatusOpen];
while ([self hasSpaceAvailable]
&& _unhandledData == NO)
{
[self _sendEvent: NSStreamEventHasSpaceAvailable];
}
} }
/* On winsock a socket is always writable unless it has had
* failure/closure or a write blocked and we have not been
* signalled again.
*/
while ([self _unhandledData] == NO && [self hasSpaceAvailable])
{
[self _sendEvent: NSStreamEventHasSpaceAvailable];
}
if (events.lNetworkEvents & FD_READ) if (events.lNetworkEvents & FD_READ)
{ {
[_sibling _setStatus: NSStreamStatusOpen]; [_sibling _setStatus: NSStreamStatusOpen];
@ -1504,6 +1517,14 @@ else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self);
- (BOOL) runLoopShouldBlock: (BOOL*)trigger - (BOOL) runLoopShouldBlock: (BOOL*)trigger
{ {
*trigger = YES; *trigger = YES;
if ([self _unhandledData] == NO && [self streamStatus] == NSStreamStatusOpen)
{
/* In winsock, a writable status is only signalled if an earlier
* write failed (because it would block), so we must simulate the
* writable event by having the run loop trigger without blocking.
*/
return NO;
}
return YES; return YES;
} }
@end @end
@ -1949,7 +1970,7 @@ else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self);
socklen_t len = [ins sockLen]; socklen_t len = [ins sockLen];
int acceptReturn = accept(_sock, [ins peerAddr], &len); int acceptReturn = accept(_sock, [ins peerAddr], &len);
_unhandledData = NO; _events &= ~NSStreamEventHasBytesAvailable;
if (acceptReturn == INVALID_SOCKET) if (acceptReturn == INVALID_SOCKET)
{ {
errno = WSAGetLastError();// test for real error errno = WSAGetLastError();// test for real error