libs-base/Source/win32/NSMessagePort.m

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