mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-06 06:30:46 +00:00
win32: Support overlapped (asynchronous) I/O on standard streams in GSFileHandle
* win32: Support overlapped I/O on standard streams in GSFileHandle * Add isStandardInput instance variable * Restrict PeekConsoleInput on stdin * Update ChangeLog
This commit is contained in:
parent
6b537b4a45
commit
32eb5d2acd
4 changed files with 100 additions and 25 deletions
|
@ -1,3 +1,12 @@
|
||||||
|
2022-08-16 Hugo Melder <contact@hugomelder.com>
|
||||||
|
|
||||||
|
* Source/win32/GSFileHandle.m:
|
||||||
|
Support overlapped I/O on standard streams in GSFileHandle.
|
||||||
|
* Source/GSFileHandle.h:
|
||||||
|
Add the isStandardStream instance variable.
|
||||||
|
* Tests/base/NSRunLoop/performers.m:
|
||||||
|
Remove extraneous unistd header from unit test.
|
||||||
|
|
||||||
2022-08-16 Richard Frith-Macdonald <rfm@gnu.org>
|
2022-08-16 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
* Source/NSOperation.m: Remove restriction (of 8) on the maximum
|
* Source/NSOperation.m: Remove restriction (of 8) on the maximum
|
||||||
|
|
|
@ -52,6 +52,9 @@ struct sockaddr_in;
|
||||||
int descriptor;
|
int descriptor;
|
||||||
BOOL closeOnDealloc;
|
BOOL closeOnDealloc;
|
||||||
BOOL isStandardFile;
|
BOOL isStandardFile;
|
||||||
|
// stdin, stdout, and stderr
|
||||||
|
BOOL isStandardStream;
|
||||||
|
BOOL isStandardInput;
|
||||||
BOOL isNullDevice;
|
BOOL isNullDevice;
|
||||||
BOOL isSocket;
|
BOOL isSocket;
|
||||||
BOOL isNonBlocking;
|
BOOL isNonBlocking;
|
||||||
|
|
|
@ -1065,6 +1065,7 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
readOK = NO;
|
readOK = NO;
|
||||||
|
isStandardStream = YES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
@ -1083,6 +1084,8 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
writeOK = NO;
|
writeOK = NO;
|
||||||
|
isStandardStream = YES;
|
||||||
|
isStandardInput = YES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
@ -1101,6 +1104,7 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
readOK = NO;
|
readOK = NO;
|
||||||
|
isStandardStream = YES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
@ -1158,8 +1162,7 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
|
||||||
{
|
{
|
||||||
if (GetFileType(h) == FILE_TYPE_PIPE)
|
if (GetFileType(h) == FILE_TYPE_PIPE)
|
||||||
{
|
{
|
||||||
/* If we can't get named pipe info, we assume this is a socket.
|
// If we can't get named pipe info, we assume this is a socket.
|
||||||
*/
|
|
||||||
if (GetNamedPipeInfo(h, 0, 0, 0, 0) == 0)
|
if (GetNamedPipeInfo(h, 0, 0, 0, 0) == 0)
|
||||||
{
|
{
|
||||||
isSocket = YES;
|
isSocket = YES;
|
||||||
|
@ -2089,33 +2092,87 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
HANDLE h;
|
||||||
|
h = (HANDLE)_get_osfhandle(descriptor);
|
||||||
|
|
||||||
|
/* Overlapped (asynchronous) I/O on a standard stream requires
|
||||||
|
* a different interface to that of a pipe.
|
||||||
|
*
|
||||||
|
* Opening a standard stream ("CONIN$", "CONOUT$", "CONERR$") via
|
||||||
|
* CreateFile() with the FILE_FLAG_OVERLAPPED flag has no effect
|
||||||
|
* on the handle; the parameter dwFlagsAndAttributes is ignored when
|
||||||
|
* creating a standard stream handle.
|
||||||
|
*
|
||||||
|
* A Windows standard stream is not an anonymous or named pipe and
|
||||||
|
* PeekNamedPipe is therefore not supported. Instead, PeekConsoleInput
|
||||||
|
* is used to "peek" into the standard stream.
|
||||||
|
*/
|
||||||
|
if (YES == isStandardInput && YES == isStandardStream)
|
||||||
|
{
|
||||||
|
/* Stores the number of input records read
|
||||||
|
*/
|
||||||
|
DWORD bytes = 0;
|
||||||
|
|
||||||
|
/* PeekConsoleInput fails, if it returns a non-zero value.
|
||||||
|
*/
|
||||||
|
if (PeekConsoleInput(h, 0, 0, &bytes) == 0)
|
||||||
|
{
|
||||||
|
DWORD e = GetLastError();
|
||||||
|
NSString *s;
|
||||||
|
|
||||||
|
s = [NSString stringWithFormat: @"Standard input peek problem: %lu - %@", e,
|
||||||
|
[NSError _last]];
|
||||||
|
[readInfo setObject: s forKey: GSFileHandleNotificationError];
|
||||||
|
|
||||||
|
NSLog(@"%@", s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (bytes == 0)
|
||||||
|
{
|
||||||
|
return; // No data available yet.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (NO == isStandardInput && YES == isStandardStream) {
|
||||||
|
NSString *s;
|
||||||
|
|
||||||
|
s = @"Reading from stdout and stderr is not available.";
|
||||||
|
[readInfo setObject: s forKey: GSFileHandleNotificationError];
|
||||||
|
|
||||||
|
NSLog(@"%@", s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
/* If this is not a socket or a standard file, we assume it's a pipe
|
/* If this is not a socket or a standard file, we assume it's a pipe
|
||||||
* and therefore we need to check to see if data really is available.
|
* and therefore we need to check to see if data really is available.
|
||||||
*/
|
*/
|
||||||
if (NO == isSocket && NO == isStandardFile)
|
else if (NO == isSocket && NO == isStandardFile)
|
||||||
{
|
{
|
||||||
HANDLE h = (HANDLE)_get_osfhandle(descriptor);
|
DWORD bytes = 0;
|
||||||
DWORD bytes = 0;
|
|
||||||
|
|
||||||
if (PeekNamedPipe(h, 0, 0, 0, &bytes, 0) == 0)
|
if (PeekNamedPipe(h, 0, 0, 0, &bytes, 0) == 0)
|
||||||
{
|
{
|
||||||
DWORD e = GetLastError();
|
DWORD e = GetLastError();
|
||||||
|
|
||||||
if (e != ERROR_BROKEN_PIPE && e != ERROR_HANDLE_EOF)
|
if (e != ERROR_BROKEN_PIPE && e != ERROR_HANDLE_EOF)
|
||||||
{
|
{
|
||||||
NSLog(@"pipe peek problem %lu: %@", e, [NSError _last]);
|
NSString *s;
|
||||||
return;
|
|
||||||
}
|
s = [NSString stringWithFormat: @"pipe peek problem: %lu - %@", e,
|
||||||
/* In the case of a broken pipe, we fall through so that a read
|
[NSError _last]];
|
||||||
* attempt is performed allowing higer level code to notice the
|
[readInfo setObject: s forKey: GSFileHandleNotificationError];
|
||||||
* problem and deal with it.
|
|
||||||
*/
|
NSLog(@"%@", s);
|
||||||
}
|
return;
|
||||||
else if (bytes == 0)
|
}
|
||||||
{
|
/* In the case of a broken pipe, we fall through so that a read
|
||||||
return; // No data available yet.
|
* attempt is performed allowing higer level code to notice the
|
||||||
}
|
* problem and deal with it.
|
||||||
}
|
*/
|
||||||
|
}
|
||||||
|
else if (bytes == 0)
|
||||||
|
{
|
||||||
|
return; // No data available yet.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (operation == NSFileHandleDataAvailableNotification)
|
if (operation == NSFileHandleDataAvailableNotification)
|
||||||
{
|
{
|
||||||
|
@ -2417,6 +2474,14 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
/* Invoking SetNamedPipeHandleState on a standard stream results in an
|
||||||
|
* ERROR_INVALID_FUNCTION (1) error message. Proceed only if the
|
||||||
|
* file descriptor is not a standard stream.
|
||||||
|
*/
|
||||||
|
else if (isStandardStream == YES)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
else if (isNonBlocking == flag)
|
else if (isNonBlocking == flag)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
#import <Foundation/NSFileHandle.h>
|
#import <Foundation/NSFileHandle.h>
|
||||||
#import <Foundation/NSDictionary.h>
|
#import <Foundation/NSDictionary.h>
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
START_SET("NSRunLoop performers")
|
START_SET("NSRunLoop performers")
|
||||||
|
|
Loading…
Reference in a new issue