mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
1156 lines
28 KiB
Objective-C
1156 lines
28 KiB
Objective-C
/** Implementation of network port object based on windows mailboxes
|
|
Copyright (C) 2005 Free Software Foundation, Inc.
|
|
|
|
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
|
|
This file is part of the GNUstep Base Library.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free
|
|
Software Foundation, Inc.,
|
|
31 Milk Street #960789 Boston, MA 02196 USA. */
|
|
|
|
#include "common.h"
|
|
#define EXPOSE_NSPort_IVARS 1
|
|
#define EXPOSE_NSMessagePort_IVARS 1
|
|
#include "Foundation/NSArray.h"
|
|
#include "Foundation/NSNotification.h"
|
|
#include "Foundation/NSError.h"
|
|
#include "Foundation/NSException.h"
|
|
#include "Foundation/NSRunLoop.h"
|
|
#include "Foundation/NSByteOrder.h"
|
|
#include "Foundation/NSData.h"
|
|
#include "Foundation/NSDate.h"
|
|
#include "Foundation/NSMapTable.h"
|
|
#include "Foundation/NSPortMessage.h"
|
|
#include "Foundation/NSPortNameServer.h"
|
|
#include "Foundation/NSLock.h"
|
|
#include "Foundation/NSThread.h"
|
|
#include "Foundation/NSConnection.h"
|
|
#include "Foundation/NSDebug.h"
|
|
#include "Foundation/NSPathUtilities.h"
|
|
#include "Foundation/NSValue.h"
|
|
#include "Foundation/NSFileManager.h"
|
|
#include "Foundation/NSProcessInfo.h"
|
|
|
|
#include "../GSPrivate.h"
|
|
#include "../GSPortPrivate.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#define UNISTR(X) \
|
|
((const unichar*)[(X) cStringUsingEncoding: NSUnicodeStringEncoding])
|
|
|
|
#if 0
|
|
#define M_LOCK(X) {NSDebugMLLog(@"NSMessagePort",@"lock %@",X); [X lock];}
|
|
#define M_UNLOCK(X) {NSDebugMLLog(@"NSMessagePort",@"unlock %@",X); [X unlock];}
|
|
#else
|
|
#define M_LOCK(X) {[X lock];}
|
|
#define M_UNLOCK(X) {[X unlock];}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* The GSPortItemType constant is used to identify the type of data in
|
|
* each packet read. All data transmitted is in a packet, each packet
|
|
* has an initial packet type and packet length.
|
|
*/
|
|
typedef enum {
|
|
GSP_ITEM, /* Expecting a port item header. */
|
|
GSP_PORT, /* Simple port item. */
|
|
GSP_DATA, /* Simple data item. */
|
|
GSP_HEAD /* Port message header + initial data. */
|
|
} GSPortItemType;
|
|
|
|
/*
|
|
* The GSPortItemHeader structure defines the header for each item transmitted.
|
|
* Its contents are transmitted in network byte order.
|
|
*/
|
|
typedef struct {
|
|
uint32_t type; /* A GSPortItemType as a 4-byte number. */
|
|
uint32_t length; /* The length of the item (excluding header). */
|
|
} GSPortItemHeader;
|
|
|
|
/*
|
|
* The GSPortMsgHeader structure is at the start of any item of type GSP_HEAD.
|
|
* Its contents are transmitted in network byte order.
|
|
* Any additional data in the item is an NSData object.
|
|
* NB. additional data counts as part of the same item.
|
|
*/
|
|
typedef struct {
|
|
uint32_t mId; /* The ID for the message starting with this. */
|
|
uint32_t nItems; /* Number of items (including this one). */
|
|
unsigned char version;
|
|
unsigned char port[16];
|
|
} GSPortMsgHeader;
|
|
|
|
typedef enum {
|
|
RS_NONE, // Not started yet
|
|
RS_MESG, // Waiting to be notified of a message arriving
|
|
RS_SIZE, // Need to determine message size
|
|
RS_DATA // Need to read message data
|
|
} ReadState;
|
|
|
|
typedef struct {
|
|
NSString *name;
|
|
NSRecursiveLock *lock;
|
|
HANDLE rHandle;
|
|
HANDLE wHandle;
|
|
HANDLE rEvent;
|
|
HANDLE wEvent;
|
|
ReadState rState;
|
|
OVERLAPPED rOv;
|
|
OVERLAPPED wOv;
|
|
DWORD rSize;
|
|
DWORD wSize;
|
|
NSMutableData *wData; /* Data object being written. */
|
|
DWORD wLength; /* Amount written so far. */
|
|
NSMutableArray *wMsgs; /* Message in progress. */
|
|
NSMutableData *rData; /* Buffer for incoming data */
|
|
DWORD rLength; /* Amount read so far. */
|
|
DWORD rWant; /* Amount desired. */
|
|
NSMutableArray *rMsgs; /* Messages in progress. */
|
|
} internal;
|
|
#define PORT(X) ((internal*)((NSMessagePort*)X)->_internal)
|
|
|
|
@implementation NSMessagePort
|
|
|
|
static SECURITY_ATTRIBUTES security;
|
|
|
|
static NSRecursiveLock *messagePortLock = nil;
|
|
|
|
/*
|
|
* Maps port name to NSMessagePort objects.
|
|
*/
|
|
static NSMapTable *ports = 0;
|
|
static Class messagePortClass = 0;
|
|
|
|
- (BOOL) _setupSendPort
|
|
{
|
|
internal *this = (internal*)self->_internal;
|
|
BOOL result;
|
|
|
|
M_LOCK(this->lock);
|
|
if (this->wHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
NSString *path;
|
|
|
|
path = [NSString stringWithFormat:
|
|
@"\\\\.\\mailslot\\GNUstep\\NSMessagePort\\%@", this->name];
|
|
|
|
this->wHandle = CreateFileW(
|
|
UNISTR(path),
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
&security,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
|
(HANDLE)0);
|
|
if (this->wHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
NSDebugMLLog(@"NSMessagePort",
|
|
@"unable to access mailslot '%@' for write - %@",
|
|
[self name], [NSError _last]);
|
|
result = NO;
|
|
}
|
|
else
|
|
{
|
|
this->wEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
this->wMsgs = [NSMutableArray new];
|
|
result = YES;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = YES;
|
|
}
|
|
M_UNLOCK(this->lock);
|
|
return result;
|
|
}
|
|
|
|
+ (void) initialize
|
|
{
|
|
if (self == [NSMessagePort class])
|
|
{
|
|
messagePortClass = self;
|
|
ports = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks,
|
|
NSNonOwnedPointerMapValueCallBacks, 0);
|
|
[[NSObject leakAt: &ports] release];
|
|
messagePortLock = [NSRecursiveLock new];
|
|
[[NSObject leakAt: &messagePortLock] release];
|
|
security.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
security.lpSecurityDescriptor = 0; // Default
|
|
security.bInheritHandle = FALSE;
|
|
}
|
|
}
|
|
|
|
+ (id) newWithName: (NSString*)name
|
|
{
|
|
NSMessagePort *p;
|
|
|
|
M_LOCK(messagePortLock);
|
|
p = RETAIN((NSMessagePort*)NSMapGet(ports, (void*)name));
|
|
if (p == nil)
|
|
{
|
|
p = [[self alloc] initWithName: name];
|
|
}
|
|
M_UNLOCK(messagePortLock);
|
|
return p;
|
|
}
|
|
|
|
- (void) addConnection: (NSConnection*)aConnection
|
|
toRunLoop: (NSRunLoop*)aLoop
|
|
forMode: (NSString*)aMode
|
|
{
|
|
NSDebugMLLog(@"NSMessagePort", @"%@ add to 0x%p in mode %@",
|
|
self, aLoop, aMode);
|
|
NSAssert(PORT(self)->rHandle != INVALID_HANDLE_VALUE,
|
|
@"Attempt to listen on send port");
|
|
[aLoop addEvent: (void*)(uintptr_t)PORT(self)->rEvent
|
|
type: ET_HANDLE
|
|
watcher: (id<RunLoopEvents>)self
|
|
forMode: aMode];
|
|
}
|
|
|
|
- (id) copyWithZone: (NSZone*)zone
|
|
{
|
|
return RETAIN(self);
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
[self finalize];
|
|
[super dealloc];
|
|
}
|
|
|
|
- (NSString*) description
|
|
{
|
|
NSString *desc;
|
|
|
|
desc = [NSString stringWithFormat: @"<NSMessagePort %p with name %@>",
|
|
self, PORT(self)->name];
|
|
return desc;
|
|
}
|
|
|
|
- (void) finalize
|
|
{
|
|
internal *this;
|
|
|
|
NSDebugMLLog(@"NSMessagePort", @"NSMessagePort 0x%p finalized", self);
|
|
[self invalidate];
|
|
this = PORT(self);
|
|
if (this != 0)
|
|
{
|
|
DESTROY(this->name);
|
|
DESTROY(this->rData);
|
|
DESTROY(this->rMsgs);
|
|
DESTROY(this->wMsgs);
|
|
DESTROY(this->lock);
|
|
NSZoneFree(NSDefaultMallocZone(), _internal);
|
|
_internal = 0;
|
|
}
|
|
}
|
|
|
|
- (id) conversation: (NSPort*)recvPort
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
- (void) handlePortMessage: (NSPortMessage*)m
|
|
{
|
|
id d = [self delegate];
|
|
|
|
if (d == nil)
|
|
{
|
|
NSDebugMLLog(@"NSMessagePort",
|
|
@"No delegate to handle incoming message");
|
|
return;
|
|
}
|
|
if ([d respondsToSelector: @selector(handlePortMessage:)] == NO)
|
|
{
|
|
NSDebugMLLog(@"NSMessagePort", @"delegate doesn't handle messages");
|
|
return;
|
|
}
|
|
NSDebugMLLog(@"NSMessagePort", @"%@ asking %@ to handle msg", self, d);
|
|
[d handlePortMessage: m];
|
|
}
|
|
|
|
- (NSUInteger) hash
|
|
{
|
|
return [PORT(self)->name hash];
|
|
}
|
|
|
|
- (id) init
|
|
{
|
|
static unsigned sequence = 0;
|
|
static int ident;
|
|
internal *this;
|
|
NSString *path;
|
|
|
|
if (sequence == 0)
|
|
{
|
|
ident = [[NSProcessInfo processInfo] processIdentifier];
|
|
}
|
|
M_LOCK(messagePortLock);
|
|
_internal = NSZoneMalloc(NSDefaultMallocZone(), sizeof(internal));
|
|
memset(_internal, '\0', sizeof(internal));
|
|
this = PORT(self);
|
|
self->_is_valid = YES;
|
|
this->name = [[NSString alloc] initWithFormat: @"%08x%08x",
|
|
((unsigned)ident), sequence++];
|
|
|
|
this->lock = [NSRecursiveLock new];
|
|
this->wHandle = INVALID_HANDLE_VALUE;
|
|
this->wEvent = INVALID_HANDLE_VALUE;
|
|
|
|
this->rState = RS_NONE;
|
|
this->rEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
this->rData = [NSMutableData new];
|
|
this->rMsgs = [NSMutableArray new];
|
|
|
|
path = [NSString stringWithFormat:
|
|
@"\\\\.\\mailslot\\GNUstep\\NSMessagePort\\%@", this->name];
|
|
|
|
this->rHandle = CreateMailslotW(
|
|
UNISTR(path),
|
|
0, /* No max message size. */
|
|
MAILSLOT_WAIT_FOREVER, /* No read/write timeout. */
|
|
&security);
|
|
|
|
if (this->rHandle == INVALID_HANDLE_VALUE)
|
|
{
|
|
NSLog(@"unable to create mailslot '%@' - %@",
|
|
this->name, [NSError _last]);
|
|
DESTROY(self);
|
|
}
|
|
else
|
|
{
|
|
NSMapInsert(ports, (void*)this->name, (void*)self);
|
|
NSDebugMLLog(@"NSMessagePort", @"Created listening port: %@", self);
|
|
|
|
/*
|
|
* Simulate a read event to kick off the I/O for this handle.
|
|
* If we can't start reading, we will be invalidated, and must
|
|
* then destroy self.
|
|
*/
|
|
[self receivedEventRead];
|
|
if ([self isValid] == NO)
|
|
{
|
|
DESTROY(self);
|
|
}
|
|
}
|
|
|
|
M_UNLOCK(messagePortLock);
|
|
return self;
|
|
}
|
|
|
|
- (id) initWithName: (NSString*)name
|
|
{
|
|
NSMessagePort *p;
|
|
BOOL found = NO;
|
|
|
|
M_LOCK(messagePortLock);
|
|
p = RETAIN((NSMessagePort*)NSMapGet(ports, (void*)name));
|
|
if (p == nil)
|
|
{
|
|
internal *this;
|
|
|
|
_internal = NSZoneMalloc(NSDefaultMallocZone(), sizeof(internal));
|
|
memset(_internal, '\0', sizeof(internal));
|
|
this = PORT(self);
|
|
self->_is_valid = YES;
|
|
this->name = [name copy];
|
|
|
|
this->lock = [NSRecursiveLock new];
|
|
|
|
this->rState = RS_NONE;
|
|
|
|
this->rHandle = INVALID_HANDLE_VALUE;
|
|
this->rEvent = INVALID_HANDLE_VALUE;
|
|
this->wHandle = INVALID_HANDLE_VALUE;
|
|
this->wEvent = INVALID_HANDLE_VALUE;
|
|
}
|
|
else
|
|
{
|
|
found = YES;
|
|
DESTROY(self);
|
|
self = p;
|
|
}
|
|
|
|
/* This is a 'speaking' port ... set it up for write operation
|
|
* if necessary.
|
|
* NB. This must be done (to create the mailbox) before the port
|
|
* is added to the nameserver mapping ... or nother process might
|
|
* try to access the mailbox before it exists.
|
|
*/
|
|
if ([self _setupSendPort] == NO)
|
|
{
|
|
DESTROY(self);
|
|
}
|
|
|
|
if (self != nil && found == NO)
|
|
{
|
|
/* This was newly created ... add to map so that it can be found.
|
|
*/
|
|
NSMapInsert(ports, (void*)[self name], (void*)self);
|
|
NSDebugMLLog(@"NSMessagePort", @"Created speaking port: %@", self);
|
|
}
|
|
M_UNLOCK(messagePortLock);
|
|
return self;
|
|
}
|
|
|
|
- (void) invalidate
|
|
{
|
|
RETAIN(self);
|
|
if ([self isValid] == YES)
|
|
{
|
|
internal *this;
|
|
|
|
this = PORT(self);
|
|
M_LOCK(this->lock);
|
|
if ([self isValid] == YES)
|
|
{
|
|
M_LOCK(messagePortLock);
|
|
if (this->rHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
(void) CancelIo(this->rHandle);
|
|
}
|
|
if (this->wHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
(void) CancelIo(this->wHandle);
|
|
}
|
|
if (this->rEvent != INVALID_HANDLE_VALUE)
|
|
{
|
|
(void) CloseHandle(this->rEvent);
|
|
this->rEvent = INVALID_HANDLE_VALUE;
|
|
}
|
|
if (this->wEvent != INVALID_HANDLE_VALUE)
|
|
{
|
|
(void) CloseHandle(this->wEvent);
|
|
this->wEvent = INVALID_HANDLE_VALUE;
|
|
}
|
|
if (this->rHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
(void) CloseHandle(this->rHandle);
|
|
this->rHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
if (this->wHandle != INVALID_HANDLE_VALUE)
|
|
{
|
|
(void) CloseHandle(this->wHandle);
|
|
this->wHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
NSMapRemove(ports, (void*)this->name);
|
|
M_UNLOCK(messagePortLock);
|
|
|
|
[[NSMessagePortNameServer sharedInstance] removePort: self];
|
|
[super invalidate];
|
|
}
|
|
M_UNLOCK(this->lock);
|
|
}
|
|
RELEASE(self);
|
|
}
|
|
|
|
- (BOOL) isEqual: (id)anObject
|
|
{
|
|
if (anObject == self)
|
|
{
|
|
return YES;
|
|
}
|
|
if ([anObject class] == [self class])
|
|
{
|
|
NSMessagePort *o = (NSMessagePort*)anObject;
|
|
|
|
return [PORT(o)->name isEqual: PORT(self)->name];
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (NSString*) name
|
|
{
|
|
return PORT(self)->name;
|
|
}
|
|
|
|
|
|
/*
|
|
* Called when an event occurs on a listener port
|
|
* ALSO called when the port is created, to start reading.
|
|
*/
|
|
- (void) receivedEventRead
|
|
{
|
|
internal *this = PORT(self);
|
|
|
|
M_LOCK(this->lock);
|
|
|
|
NSDebugMLLog(@"NSMessagePort", @"entered with rWant=%lu", this->rWant);
|
|
|
|
if (this->rState == RS_MESG)
|
|
{
|
|
/*
|
|
* Have we read something?
|
|
*/
|
|
if (GetOverlappedResult(
|
|
this->rHandle,
|
|
&this->rOv,
|
|
&this->rSize,
|
|
TRUE) == 0)
|
|
{
|
|
errno = GetLastError();
|
|
NSDebugMLLog(@"NSMessagePort", @"overlapped result=%d", errno);
|
|
/*
|
|
* Our overlapped read attempt should fail ... because mailslots
|
|
* insist we read an entire message in one go, and we asked it
|
|
* to read zero bytes. The error we are expecting is
|
|
* ERROR_INSUFFICIENT_BUFFER ... indicating that there is a
|
|
* message to be read ... so we can ask for its size and read it
|
|
* synchronously.
|
|
*/
|
|
if (errno == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
this->rState = RS_SIZE;
|
|
}
|
|
else
|
|
{
|
|
NSLog(@"GetOverlappedResult failed ... %@", [NSError _last]);
|
|
this->rState = RS_NONE;
|
|
this->rLength = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NSLog(@"GetOverlappedResult success ... %lu", this->rSize);
|
|
this->rState = RS_NONE;
|
|
this->rLength = 0;
|
|
}
|
|
}
|
|
|
|
if (this->rState == RS_SIZE)
|
|
{
|
|
if (GetMailslotInfo(
|
|
this->rHandle,
|
|
0,
|
|
&this->rWant,
|
|
0,
|
|
0) == 0)
|
|
{
|
|
NSLog(@"unable to get info from mailslot '%@' - %@",
|
|
this->name, [NSError _last]);
|
|
[self invalidate];
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
this->rState = RS_DATA;
|
|
NSDebugMLLog(@"NSMessagePort", @"mailslot size=%lu",
|
|
this->rWant);
|
|
[this->rData setLength: this->rWant];
|
|
if (ReadFile(this->rHandle,
|
|
[this->rData mutableBytes], // Store results here
|
|
this->rWant,
|
|
&this->rSize,
|
|
NULL) == 0)
|
|
{
|
|
NSLog(@"unable to read from mailslot '%@' - %@",
|
|
this->name, [NSError _last]);
|
|
[self invalidate];
|
|
return;
|
|
}
|
|
if (this->rSize != this->rWant)
|
|
{
|
|
NSLog(@"only read %lu of %lu bytes from mailslot '%@' - %@",
|
|
this->rSize, this->rWant, this->name, [NSError _last]);
|
|
[self invalidate];
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
NSDebugMLLog(@"NSMessagePort", @"Read complete on %@",
|
|
self);
|
|
}
|
|
this->rLength = this->rSize;
|
|
this->rSize = 0;
|
|
this->rState = RS_NONE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do next part only if we have completed a read.
|
|
*/
|
|
if (this->rLength > 0 && this->rLength == this->rWant)
|
|
{
|
|
unsigned char *buf = [this->rData mutableBytes];
|
|
GSPortItemType rType;
|
|
GSPortItemHeader *pih;
|
|
unsigned off = 0;
|
|
unsigned len;
|
|
unsigned rId = 0;
|
|
unsigned nItems = 0;
|
|
NSMessagePort *rPort = nil;
|
|
NSMutableArray *rItems = nil;
|
|
|
|
while (off + sizeof(GSPortItemHeader) <= this->rLength)
|
|
{
|
|
pih = (GSPortItemHeader*)(buf + off);
|
|
off += sizeof(GSPortItemHeader);
|
|
rType = GSSwapBigI32ToHost(pih->type);
|
|
len = GSSwapBigI32ToHost(pih->length);
|
|
if (len + off > this->rLength)
|
|
{
|
|
NSLog(@"%@ - unreasonable length (%u) for data", self, len);
|
|
break;
|
|
}
|
|
if (rType != GSP_HEAD && rItems == nil)
|
|
{
|
|
NSLog(@"%@ - initial part of message had bad type", self);
|
|
break;
|
|
}
|
|
|
|
if (rType == GSP_HEAD)
|
|
{
|
|
GSPortMsgHeader *pmh;
|
|
NSString *n;
|
|
NSMutableData *d;
|
|
|
|
if (len < sizeof(GSPortMsgHeader))
|
|
{
|
|
NSLog(@"%@ - bad length for header", self);
|
|
break;
|
|
}
|
|
pmh = (GSPortMsgHeader*)(buf + off);
|
|
off += sizeof(GSPortMsgHeader);
|
|
len -= sizeof(GSPortMsgHeader);
|
|
rId = GSSwapBigI32ToHost(pmh->mId);
|
|
nItems = GSSwapBigI32ToHost(pmh->nItems);
|
|
if (nItems == 0)
|
|
{
|
|
NSLog(@"%@ - unable to decode item count", self);
|
|
break;
|
|
}
|
|
n = [[NSString alloc] initWithBytes: pmh->port
|
|
length: 16
|
|
encoding: NSASCIIStringEncoding];
|
|
NSDebugFLLog(@"NSMessagePort", @"Decoded port as '%@'", n);
|
|
rPort = [messagePortClass newWithName: n];
|
|
RELEASE(n);
|
|
if (rPort == nil)
|
|
{
|
|
NSLog(@"%@ - unable to decode remote port", self);
|
|
break;
|
|
}
|
|
rItems = [NSMutableArray alloc];
|
|
rItems = [rItems initWithCapacity: nItems];
|
|
d = [[NSMutableData alloc] initWithBytes: buf + off
|
|
length: len];
|
|
[rItems addObject: d];
|
|
RELEASE(d);
|
|
}
|
|
else if (rType == GSP_DATA)
|
|
{
|
|
NSMutableData *d;
|
|
|
|
d = [[NSMutableData alloc] initWithBytes: buf + off
|
|
length: len];
|
|
[rItems addObject: d];
|
|
RELEASE(d);
|
|
}
|
|
else if (rType == GSP_PORT)
|
|
{
|
|
NSMessagePort *p;
|
|
NSString *n;
|
|
|
|
if (len != 16)
|
|
{
|
|
NSLog(@"%@ - bad length for port item", self);
|
|
break;
|
|
}
|
|
n = [[NSString alloc] initWithBytes: buf + off
|
|
length: 16
|
|
encoding: NSASCIIStringEncoding];
|
|
NSDebugFLLog(@"NSMessagePort", @"Decoded port as '%@'", n);
|
|
p = [messagePortClass newWithName: n];
|
|
RELEASE(n);
|
|
if (p == nil)
|
|
{
|
|
NSLog(@"%@ - unable to decode remote port", self);
|
|
break;
|
|
}
|
|
[rItems addObject: p];
|
|
RELEASE(p);
|
|
}
|
|
off += len;
|
|
if (nItems == [rItems count])
|
|
{
|
|
NSPortMessage *pm;
|
|
|
|
pm = [NSPortMessage allocWithZone: NSDefaultMallocZone()];
|
|
pm = [pm initWithSendPort: rPort
|
|
receivePort: self
|
|
components: rItems];
|
|
DESTROY(rPort);
|
|
DESTROY(rItems);
|
|
[pm setMsgid: rId];
|
|
[this->rMsgs addObject: pm];
|
|
RELEASE(pm);
|
|
break;
|
|
}
|
|
}
|
|
DESTROY(rPort);
|
|
DESTROY(rItems);
|
|
}
|
|
|
|
/*
|
|
* Got something ... is it all we want? If not, ask to read more.
|
|
*/
|
|
if ([self isValid] == YES
|
|
&& (this->rState == RS_NONE || this->rLength < this->rWant))
|
|
{
|
|
int rc;
|
|
|
|
this->rOv.Offset = 0;
|
|
this->rOv.OffsetHigh = 0;
|
|
this->rOv.hEvent = this->rEvent;
|
|
if (this->rState == RS_NONE)
|
|
{
|
|
this->rLength = 0;
|
|
this->rWant = 1;
|
|
}
|
|
if ([this->rData length] < (this->rWant - this->rLength))
|
|
{
|
|
[this->rData setLength: this->rWant - this->rLength];
|
|
}
|
|
rc = ReadFile(this->rHandle,
|
|
[this->rData mutableBytes], // Store results here
|
|
this->rWant - this->rLength,
|
|
&this->rSize,
|
|
&this->rOv);
|
|
|
|
if (rc > 0)
|
|
{
|
|
NSDebugMLLog(@"NSMessagePort", @"Read immediate on %@", self);
|
|
if (this->rState == RS_NONE)
|
|
{
|
|
this->rState = RS_SIZE;
|
|
}
|
|
SetEvent(this->rEvent);
|
|
}
|
|
else if ((errno = GetLastError()) == ERROR_IO_PENDING)
|
|
{
|
|
NSDebugMLLog(@"NSMessagePort", @"Read queued on %@", self);
|
|
if (this->rState == RS_NONE)
|
|
{
|
|
this->rState = RS_MESG;
|
|
}
|
|
}
|
|
else if (errno == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
NSDebugMLLog(@"NSMessagePort", @"Read retry on %@", self);
|
|
if (this->rState == RS_NONE)
|
|
{
|
|
this->rState = RS_SIZE;
|
|
}
|
|
SetEvent(this->rEvent);
|
|
}
|
|
else
|
|
{
|
|
NSLog(@"unable to read from mailslot '%@' - %@",
|
|
this->name, [NSError _last]);
|
|
[self invalidate];
|
|
}
|
|
}
|
|
|
|
while ([this->rMsgs count] > 0)
|
|
{
|
|
NSPortMessage *pm;
|
|
|
|
pm = RETAIN([this->rMsgs objectAtIndex: 0]);
|
|
[this->rMsgs removeObjectAtIndex: 0];
|
|
|
|
NSDebugMLLog(@"NSMessagePort", @"got message %@ on 0x%p", pm, self);
|
|
M_UNLOCK(this->lock);
|
|
NS_DURING
|
|
{
|
|
[self handlePortMessage: pm];
|
|
}
|
|
NS_HANDLER
|
|
{
|
|
M_LOCK(this->lock);
|
|
RELEASE(pm);
|
|
[localException raise];
|
|
}
|
|
NS_ENDHANDLER
|
|
M_LOCK(this->lock);
|
|
RELEASE(pm);
|
|
}
|
|
|
|
M_UNLOCK(this->lock);
|
|
}
|
|
|
|
/*
|
|
* Called when an event occurs on a speaker port
|
|
* ALSO called when we start trying to write a new message and there
|
|
* wasn't one in progress.
|
|
*/
|
|
- (void) receivedEventWrite
|
|
{
|
|
internal *this = PORT(self);
|
|
|
|
M_LOCK(this->lock);
|
|
|
|
if (this->wData != nil)
|
|
{
|
|
/*
|
|
* Have we written something?
|
|
*/
|
|
if (GetOverlappedResult(
|
|
this->wHandle,
|
|
&this->wOv,
|
|
&this->wSize,
|
|
TRUE) == 0)
|
|
{
|
|
NSLog(@"GetOverlappedResult failed ... %@", [NSError _last]);
|
|
}
|
|
else
|
|
{
|
|
this->wLength += this->wSize;
|
|
this->wSize = 0;
|
|
}
|
|
}
|
|
|
|
again:
|
|
|
|
/*
|
|
* Handle start of next data item if we have completed one,
|
|
* or if we are called without a write in progress.
|
|
*/
|
|
if (this->wData == nil || this->wLength == [this->wData length])
|
|
{
|
|
if (this->wData != nil)
|
|
{
|
|
NSDebugMLLog(@"NSMessagePort",
|
|
@"completed write on 0x%p", self);
|
|
[this->wMsgs removeObjectIdenticalTo: this->wData];
|
|
this->wData = nil;
|
|
}
|
|
if ([this->wMsgs count] > 0)
|
|
{
|
|
this->wData = [this->wMsgs objectAtIndex: 0];
|
|
}
|
|
this->wLength = 0; // Nothing written yet.
|
|
}
|
|
|
|
if (this->wData != nil)
|
|
{
|
|
int rc;
|
|
|
|
this->wOv.Offset = 0;
|
|
this->wOv.OffsetHigh = 0;
|
|
this->wOv.hEvent = this->wEvent;
|
|
rc = WriteFile(this->wHandle,
|
|
[this->wData bytes], // Output from here
|
|
[this->wData length] - this->wLength,
|
|
&this->wSize, // Store number of bytes written
|
|
&this->wOv);
|
|
if (rc > 0)
|
|
{
|
|
NSDebugMLLog(@"NSMessagePort", @"Write of %lu performs %lu",
|
|
(unsigned long)([this->wData length] - this->wLength), this->wSize);
|
|
this->wLength += this->wSize;
|
|
goto again;
|
|
}
|
|
else if ((errno = GetLastError()) != ERROR_IO_PENDING)
|
|
{
|
|
/* This is probably an end of file
|
|
* eg. when the process at the other end has terminated.
|
|
*/
|
|
NSDebugMLLog(@"NSMessagePort",
|
|
@"unable to write to mailslot '%@' - %@",
|
|
this->name, [NSError _last]);
|
|
[self invalidate];
|
|
}
|
|
else
|
|
{
|
|
NSDebugMLLog(@"NSMessagePort", @"Write of %lu queued",
|
|
(unsigned long)([this->wData length] - this->wLength));
|
|
}
|
|
}
|
|
M_UNLOCK(this->lock);
|
|
}
|
|
|
|
- (void) receivedEvent: (void*)data
|
|
type: (RunLoopEventType)type
|
|
extra: (void*)extra
|
|
forMode: (NSString*)mode
|
|
{
|
|
RETAIN(self);
|
|
if ([self isValid] == YES)
|
|
{
|
|
internal *this = PORT(self);
|
|
|
|
NSDebugMLLog(@"NSMessagePort", @"got event on %@ in mode %@", self, mode);
|
|
if (this->rEvent == (HANDLE)data)
|
|
{
|
|
[self receivedEventRead];
|
|
}
|
|
else
|
|
{
|
|
[self receivedEventWrite];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NSDebugMLLog(@"NSMessagePort",
|
|
@"got event on invalidated port 0x%p in mode %@", self, mode);
|
|
}
|
|
RELEASE(self);
|
|
}
|
|
|
|
|
|
- (void) removeConnection: (NSConnection*)aConnection
|
|
fromRunLoop: (NSRunLoop*)aLoop
|
|
forMode: (NSString*)aMode
|
|
{
|
|
NSDebugMLLog(@"NSMessagePort", @"%@ remove from 0x%p in mode %@",
|
|
self, aLoop, aMode);
|
|
[aLoop removeEvent: (void*)(uintptr_t)PORT(self)->rEvent
|
|
type: ET_HANDLE
|
|
forMode: aMode
|
|
all: NO];
|
|
}
|
|
|
|
/*
|
|
* This returns the amount of space that a port coder should reserve at the
|
|
* start of its encoded data so that the NSMessagePort can insert header info
|
|
* into the data.
|
|
* The idea is that a message consisting of a single data item with space at
|
|
* the start can be written directly without having to copy data to another
|
|
* buffer etc.
|
|
*/
|
|
- (NSUInteger) reservedSpaceLength
|
|
{
|
|
return sizeof(GSPortItemHeader) + sizeof(GSPortMsgHeader);
|
|
}
|
|
|
|
- (oneway void) release
|
|
{
|
|
/* We lock the port table while checking, to prevent
|
|
* another thread from grabbing this port while we are
|
|
* checking it.
|
|
* If we are going to deallocate the object, we first remove
|
|
* it from the table so that no other thread will find it
|
|
* and try to use it while it is being deallocated.
|
|
*/
|
|
M_LOCK(messagePortLock);
|
|
if (NSDecrementExtraRefCountWasZero(self))
|
|
{
|
|
NSMapRemove(ports, (void*)[self name]);
|
|
M_UNLOCK(messagePortLock);
|
|
[self dealloc];
|
|
}
|
|
else
|
|
{
|
|
M_UNLOCK(messagePortLock);
|
|
}
|
|
}
|
|
|
|
- (BOOL) sendBeforeDate: (NSDate*)when
|
|
msgid: (NSInteger)msgId
|
|
components: (NSMutableArray*)components
|
|
from: (NSPort*)receivingPort
|
|
reserved: (NSUInteger)length
|
|
{
|
|
NSMutableData *h = nil;
|
|
NSMutableData *first;
|
|
BOOL sent = NO;
|
|
unsigned c;
|
|
unsigned i;
|
|
internal *this;
|
|
GSPortItemHeader *pih;
|
|
GSPortMsgHeader *pmh;
|
|
|
|
NSDebugMLLog(@"NSMessagePort",
|
|
@"send message\n Send: %@\n Recv: %@\n Components -\n%@",
|
|
self, receivingPort, components);
|
|
|
|
if ([self isValid] == NO)
|
|
{
|
|
return NO;
|
|
}
|
|
this = PORT(self);
|
|
|
|
if (PORT(self)->wHandle == INVALID_HANDLE_VALUE
|
|
&& [self _setupSendPort] == NO)
|
|
{
|
|
NSLog(@"Attempt to send through recv port");
|
|
}
|
|
|
|
c = (unsigned)[components count];
|
|
if (c == 0)
|
|
{
|
|
NSLog(@"empty components sent");
|
|
return NO;
|
|
}
|
|
/*
|
|
* If the reserved length in the first data object is wrong - we have to
|
|
* fail.
|
|
*/
|
|
if (length != [self reservedSpaceLength])
|
|
{
|
|
NSLog(@"bad reserved length - %lu", (unsigned long)length);
|
|
return NO;
|
|
}
|
|
NSAssert([receivingPort isKindOfClass: messagePortClass] == YES,
|
|
@"Receiving port is not the correct type");
|
|
NSAssert([receivingPort isValid] == YES,
|
|
@"Receiving port is not valid");
|
|
NSAssert(PORT(receivingPort)->rHandle != INVALID_HANDLE_VALUE,
|
|
@"Attempt to send to send port");
|
|
|
|
first = [components objectAtIndex: 0];
|
|
if (c == 1)
|
|
{
|
|
//h = RETAIN(first);
|
|
h = [first mutableCopy];
|
|
}
|
|
if (c > 1)
|
|
{
|
|
unsigned l = 0;
|
|
id o;
|
|
|
|
for (i = 1; i < c; i++)
|
|
{
|
|
o = [components objectAtIndex: i];
|
|
if ([o isKindOfClass: [NSData class]] == YES)
|
|
{
|
|
l += sizeof(GSPortItemHeader) + [o length];
|
|
}
|
|
else
|
|
{
|
|
l += sizeof(GSPortItemHeader) + 16; // A port
|
|
}
|
|
}
|
|
h = [[NSMutableData alloc] initWithCapacity: [first length] + l];
|
|
[h appendData: first];
|
|
|
|
for (i = 1; i < c; i++)
|
|
{
|
|
GSPortItemHeader ih;
|
|
|
|
o = [components objectAtIndex: i];
|
|
if ([o isKindOfClass: [NSData class]] == YES)
|
|
{
|
|
ih.type = GSSwapHostI32ToBig(GSP_DATA);
|
|
ih.length = GSSwapHostI32ToBig([o length]);
|
|
[h appendBytes: &ih length: sizeof(ih)];
|
|
[h appendData: o];
|
|
}
|
|
else
|
|
{
|
|
ih.type = GSSwapHostI32ToBig(GSP_PORT);
|
|
ih.length = GSSwapHostI32ToBig(16);
|
|
[h appendBytes: &ih length: sizeof(ih)];
|
|
[h appendBytes: [o UTF8String] length: 16];
|
|
}
|
|
}
|
|
}
|
|
|
|
pih = (GSPortItemHeader*)[h mutableBytes];
|
|
pih->type = GSSwapHostI32ToBig(GSP_HEAD);
|
|
pih->length = GSSwapHostI32ToBig([first length] - sizeof(GSPortItemHeader));
|
|
pmh = (GSPortMsgHeader*)&pih[1];
|
|
pmh->mId = GSSwapHostI32ToBig(msgId);
|
|
pmh->nItems = GSSwapHostI32ToBig(c);
|
|
pmh->version = 0;
|
|
memcpy(pmh->port, [[(NSMessagePort*)receivingPort name] UTF8String], 16);
|
|
|
|
/*
|
|
* Now send the message.
|
|
*/
|
|
|
|
RETAIN(self);
|
|
M_LOCK(this->lock);
|
|
[this->wMsgs addObject: h];
|
|
if (this->wData == nil)
|
|
{
|
|
[self receivedEventWrite]; // Start async write.
|
|
}
|
|
|
|
if ([this->wMsgs indexOfObjectIdenticalTo: h] == NSNotFound)
|
|
{
|
|
sent = YES; // Write completed synchronously
|
|
}
|
|
else
|
|
{
|
|
NSRunLoop *loop = [NSRunLoop currentRunLoop];
|
|
|
|
[loop addEvent: (void*)(uintptr_t)this->wEvent
|
|
type: ET_HANDLE
|
|
watcher: (id<RunLoopEvents>)self
|
|
forMode: NSConnectionReplyMode];
|
|
[loop addEvent: (void*)(uintptr_t)this->wEvent
|
|
type: ET_HANDLE
|
|
watcher: (id<RunLoopEvents>)self
|
|
forMode: NSDefaultRunLoopMode];
|
|
|
|
while ([self isValid] == YES
|
|
&& [this->wMsgs indexOfObjectIdenticalTo: h] != NSNotFound
|
|
&& [when timeIntervalSinceNow] > 0)
|
|
{
|
|
M_UNLOCK(this->lock);
|
|
NS_DURING
|
|
[loop runMode: NSConnectionReplyMode beforeDate: when];
|
|
NS_HANDLER
|
|
M_LOCK(this->lock);
|
|
[loop removeEvent: (void*)(uintptr_t)this->wEvent
|
|
type: ET_HANDLE
|
|
forMode: NSConnectionReplyMode
|
|
all: NO];
|
|
[loop removeEvent: (void*)(uintptr_t)this->wEvent
|
|
type: ET_HANDLE
|
|
forMode: NSDefaultRunLoopMode
|
|
all: NO];
|
|
M_UNLOCK(this->lock);
|
|
[localException raise];
|
|
NS_ENDHANDLER
|
|
M_LOCK(this->lock);
|
|
}
|
|
|
|
[loop removeEvent: (void*)(uintptr_t)this->wEvent
|
|
type: ET_HANDLE
|
|
forMode: NSConnectionReplyMode
|
|
all: NO];
|
|
[loop removeEvent: (void*)(uintptr_t)this->wEvent
|
|
type: ET_HANDLE
|
|
forMode: NSDefaultRunLoopMode
|
|
all: NO];
|
|
|
|
if ([this->wMsgs indexOfObjectIdenticalTo: h] == NSNotFound)
|
|
{
|
|
sent = YES;
|
|
}
|
|
}
|
|
M_UNLOCK(this->lock);
|
|
RELEASE(h);
|
|
RELEASE(self);
|
|
|
|
return sent;
|
|
}
|
|
|
|
- (NSDate*) timedOutEvent: (void*)data
|
|
type: (RunLoopEventType)type
|
|
forMode: (NSString*)mode
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
@end
|
|
|
|
|