From 39a39a1ab9e3d1c05f5dcc7c3f43b6112ccea05b Mon Sep 17 00:00:00 2001 From: richard Date: Thu, 11 Feb 1999 08:10:24 +0000 Subject: [PATCH] Distributed objects tidyup with a few minor bugfixes git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@3687 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 10 + Headers/gnustep/base/DistributedObjects.h | 5 +- Headers/gnustep/base/NSConnection.h | 12 +- Headers/gnustep/base/NSDistantObject.h | 10 +- Source/NSConnection.m | 702 ++++++++++++---------- Source/NSDistantObject.m | 375 ++++-------- Source/TcpPort.m | 137 +++-- 7 files changed, 625 insertions(+), 626 deletions(-) diff --git a/ChangeLog b/ChangeLog index 18936768a..819253104 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +Thu Feb 11 7:27:00 1999 Richard Frith-Macdonald + + * Source/NSConnection.m: Tidy up with bugfixes and better distributed + retain/release for triangle connections. + * Source/NSDistantObject.m: ditto + * Source/TcpPort.m: ditto + * Source/include/DistributedObjects.h: ditto + * Source/include/NSConnection.h: ditto + * Source/include/NSDistantObject.h: ditto + 1999-02-09 Adam Fedor * Source/HashTable.m: Moved to extensions. diff --git a/Headers/gnustep/base/DistributedObjects.h b/Headers/gnustep/base/DistributedObjects.h index 1f5f0b1c0..53057742f 100644 --- a/Headers/gnustep/base/DistributedObjects.h +++ b/Headers/gnustep/base/DistributedObjects.h @@ -58,10 +58,11 @@ enum { ROOTPROXY_REQUEST, ROOTPROXY_REPLY, CONNECTION_SHUTDOWN, - METHODTYPE_REQUEST, /* these two only needed with NeXT runtime */ - METHODTYPE_REPLY, /* these two only needed with NeXT runtime */ + METHODTYPE_REQUEST, + METHODTYPE_REPLY, PROXY_RELEASE, PROXY_RETAIN, + RETAIN_REPLY }; diff --git a/Headers/gnustep/base/NSConnection.h b/Headers/gnustep/base/NSConnection.h index 6faa1c53f..3cd57e923 100644 --- a/Headers/gnustep/base/NSConnection.h +++ b/Headers/gnustep/base/NSConnection.h @@ -25,11 +25,8 @@ #ifndef __NSConnection_h_GNUSTEP_BASE_INCLUDE #define __NSConnection_h_GNUSTEP_BASE_INCLUDE -#include -#include -#include -#include -#include +#include +#include #include #include #include @@ -100,7 +97,6 @@ extern NSString *NSConnectionProxyCount; /* Objects received */ - (BOOL) independantConversationQueueing; - (void) invalidate; - (BOOL) isValid; -- (BOOL) registerName: (NSString*)name; - (NSArray *) remoteObjects; - (void) removeRequestMode: (NSString*)mode; - (void) removeRunLoop: (NSRunLoop *)runloop; @@ -139,7 +135,9 @@ extern NSString *NSConnectionProxyCount; /* Objects received */ * This catagory contains legacy methods from the original GNU 'Connection' * class, and useful extensions to NSConnection. */ -@interface NSConnection (GNUstepExtensions) +@interface NSConnection (GNUstepExtensions) + +- (void) gcFinalize; /* Setting and getting class configuration */ + (Class) defaultReceivePortClass; diff --git a/Headers/gnustep/base/NSDistantObject.h b/Headers/gnustep/base/NSDistantObject.h index 034a6f3c0..2ab5358af 100644 --- a/Headers/gnustep/base/NSDistantObject.h +++ b/Headers/gnustep/base/NSDistantObject.h @@ -35,7 +35,6 @@ NSConnection *_connection; id _object; unsigned _handle; - BOOL _isVended; Protocol *_protocol; } @@ -56,12 +55,13 @@ @end -@interface NSDistantObject(GNUstepExtensions) +@interface NSDistantObject(GNUstepExtensions) -- awakeAfterUsingCoder: aDecoder; -- classForPortCoder; +- (id) awakeAfterUsingCoder: (NSCoder*)aDecoder; +- (Class) classForPortCoder; - (const char *) selectorTypeForProxy: (SEL)selector; -- forward: (SEL)aSel :(arglist_t)frame; +- (id) forward: (SEL)aSel :(arglist_t)frame; +- (void) gcFinalize; @end #endif /* __NSDistantObject_h_GNUSTEP_BASE_INCLUDE */ diff --git a/Source/NSConnection.m b/Source/NSConnection.m index 4bbb061ac..788b578ff 100644 --- a/Source/NSConnection.m +++ b/Source/NSConnection.m @@ -35,7 +35,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -63,18 +64,12 @@ NSString *NSConnectionLocalCount = @"NSConnectionLocalCount"; NSString *NSConnectionProxyCount = @"NSConnectionProxyCount"; @interface NSDistantObject (NSConnection) -- (BOOL) isVended; - (id) localForProxy; - (void) setProxyTarget: (unsigned)target; -- (void) setVended; - (unsigned) targetForProxy; @end @implementation NSDistantObject (NSConnection) -- (BOOL) isVended -{ - return _isVended; -} - (id) localForProxy { return _object; @@ -83,10 +78,6 @@ NSString *NSConnectionProxyCount = @"NSConnectionProxyCount"; { _handle = target; } -- (void) setVended -{ - _isVended = YES; -} - (unsigned) targetForProxy { return _handle; @@ -120,10 +111,15 @@ static unsigned local_object_counter = 0; counter = (GSLocalCounter*)NSAllocateObject(self, 0, NSDefaultMallocZone()); counter->ref = 1; - counter->object = obj; + counter->object = [obj retain]; counter->target = ++local_object_counter; return counter; } +- (void) dealloc +{ + [object release]; + [super dealloc]; +} @end @@ -244,15 +240,8 @@ static NSTimer *timer; static int debug_connection = 0; -/* Perhaps this should be a hashtable, keyed by remote port. - But we may also need to include the local port---even though - when receiving the local port is fixed, there may be more than - one registered connection (with different in ports) in the - application. */ -/* We could write -hash and -isEqual implementations for NSConnection */ -static NSMutableArray *connection_array; -static NSMutableArray *not_owned; -static NSLock *connection_array_gate; +static NSHashTable *connection_table; +static NSLock *connection_table_gate; static NSMutableDictionary *root_object_dictionary; static NSLock *root_object_dictionary_gate; @@ -278,11 +267,7 @@ static int messages_received_count; + (NSArray*) allConnections { - int count = [connection_array count]; - id cons[count]; - - [connection_array getObjects: cons]; - return [NSArray arrayWithObjects: cons count: count]; + return NSAllHashTableObjects(connection_table); } + (NSConnection*) connectionWithRegisteredName: (NSString*)n @@ -329,9 +314,9 @@ static int messages_received_count; + (void) initialize { - not_owned = [[NSMutableArray alloc] initWithCapacity:8]; - connection_array = [[NSMutableArray alloc] initWithCapacity:8]; - connection_array_gate = [NSLock new]; + connection_table = + NSCreateHashTable (NSNonRetainedObjectHashCallBacks, 0); + connection_table_gate = [NSLock new]; /* xxx When NSHashTable's are working, change this. */ all_connections_local_objects = NSCreateMapTable (NSNonOwnedPointerMapKeyCallBacks, @@ -414,39 +399,10 @@ static int messages_received_count; } } -/* This needs locks */ - (void) dealloc { if (debug_connection) - NSLog(@"deallocating 0x%x\n", (unsigned)self); - [self invalidate]; - - /* Remove rootObject from root_object_dictionary - if this is last connection */ - if (![NSConnection connectionsCountWithInPort:receive_port]) - [NSConnection setRootObject:nil forInPort:receive_port]; - - /* Remove receive port from run loop. */ - [self setRequestMode: nil]; - [[NSRunLoop currentRunLoop] removePort: receive_port - forMode: NSConnectionReplyMode]; - [request_modes release]; - - /* Finished with ports - releasing them may generate a notification */ - [receive_port release]; - [send_port release]; - - /* Don't need notifications any more - so remove self as observer. */ - [NotificationDispatcher removeObserver: self]; - - [proxiesHashGate lock]; - NSFreeMapTable (remote_proxies); - NSFreeMapTable (local_objects); - NSFreeMapTable (local_targets); - NSFreeMapTable (incoming_xref_2_const_ptr); - NSFreeMapTable (outgoing_const_ptr_2_xref); - [proxiesHashGate unlock]; - + NSLog(@"deallocating 0x%x\n", (gsaddr)self); [super dealloc]; } @@ -482,18 +438,6 @@ static int messages_received_count; return newConn; } -/* xxx This method is an anomaly, just until we get a proper name - server for port objects. Richard Frith-MacDonald is working on a - name server. */ -- (BOOL) registerName: (NSString*)name -{ - id old_receive_port = receive_port; - receive_port = [default_receive_port_class newForReceivingFromRegisteredName: name]; - [old_receive_port release]; - return YES; -} - - /* * Keep track of connections created by DO system but not necessarily * ever retained by users code. These must be retained now for later @@ -501,63 +445,67 @@ static int messages_received_count; */ - (void) setNotOwned { - if (![not_owned containsObject:self]) { - [not_owned addObject:self]; - } } /* xxx This needs locks */ - (void) invalidate { - if (is_valid) - { - is_valid = 0; + if (is_valid == NO) + return; - /* - * We can't be the ancestor of anything if we are invalid. - */ - if (self == NSMapGet(receive_port_2_ancestor, receive_port)) - NSMapRemove(receive_port_2_ancestor, receive_port); + is_valid = NO; - /* - * If we have been invalidated, we don't need to retain proxies - * for local objects any more. In fact, we want to get rid of - * these proxies in case they are keeping us retained when we - * might otherwise de deallocated. - */ + /* + * Don't need notifications any more - so remove self as observer. + */ + [NSNotificationCenter removeObserver: self]; + + /* + * We can't be the ancestor of anything if we are invalid. + */ + if (self == NSMapGet(receive_port_2_ancestor, receive_port)) + NSMapRemove(receive_port_2_ancestor, receive_port); + + /* + * If we have been invalidated, we don't need to retain proxies + * for local objects any more. In fact, we want to get rid of + * these proxies in case they are keeping us retained when we + * might otherwise de deallocated. + */ + { + NSArray *targets; + unsigned i; + + [proxiesHashGate lock]; + targets = [NSAllMapTableValues(local_targets) retain]; + for (i = 0; i < [targets count]; i++) { - NSArray *targets; - unsigned i; + id t = [[targets objectAtIndex:i] localForProxy]; - [proxiesHashGate lock]; - targets = [NSAllMapTableValues(local_targets) retain]; - for (i = 0; i < [targets count]; i++) - { - id t = [[targets objectAtIndex:i] localForProxy]; - - [self removeLocalObject:t]; - } - [targets release]; - [proxiesHashGate unlock]; + [self removeLocalObject: t]; } - /* xxx Note: this is causing us to send a shutdown message - to the connection that shut *us* down. Don't do that. - Well, perhaps it's a good idea just in case other side didn't really - send us the shutdown; this way we let them know we're going away */ -#if 0 - [self shutdown]; -#endif + [targets release]; + [proxiesHashGate unlock]; + } - if (debug_connection) - NSLog(@"Invalidating connection 0x%x\n\t%@\n\t%@\n", (unsigned)self, - [receive_port description], [send_port description]); + if (debug_connection) + NSLog(@"Invalidating connection 0x%x\n\t%@\n\t%@\n", (gsaddr)self, + [receive_port description], [send_port description]); - [NotificationDispatcher - postNotificationName: NSConnectionDidDieNotification - object: self]; + /* + * We need to notify any watchers of our death - but if we are already + * in the deallocation process, we can't have a notification retaining + * and autoreleasing us later once we are deallocated - so we do the + * notification with a local autorelease pool to ensure that any release + * is done before the deallocation completes. + */ + { + NSAutoreleasePool *arp = [NSAutoreleasePool new]; - [not_owned removeObjectIdenticalTo:self]; - } + [NSNotificationCenter postNotificationName: NSConnectionDidDieNotification + object: self]; + [arp release]; + } } - (BOOL) isValid @@ -567,25 +515,19 @@ static int messages_received_count; - (void) release { - /* - * In order that connections may be deallocated - we check to see if - * the only thing still retaining us is the connection_array. - * if so (we assume a retain count of 2 means this) we remove self - * from the connection array. - * NB. bracket this operation with retain and release so that we don't - * suffer problems with recursion. - */ - if ([self retainCount] == 2) { - [super retain]; - [connection_array_gate lock]; - [connection_array removeObject: self]; - [timer invalidate]; - timer = nil; - NSResetMapTable(all_connections_local_cached); - [connection_array_gate unlock]; - [super release]; + /* + * If this would cause the connection to be deallocated then we + * must perform all necessary work (done in [-gcFinalize]). + * We bracket the code with a retain and release so that any + * retain/release pairs in the code won't cause recursion. + */ + if ([self retainCount] == 1) + { + [super retain]; + [self gcFinalize]; + [super release]; } - [super release]; + [super release]; } - (NSArray *) remoteObjects @@ -596,9 +538,10 @@ static int messages_received_count; - (void) removeRequestMode: (NSString*)mode { - if ([request_modes containsObject:mode]) { - [request_modes removeObject:mode]; - [[NSRunLoop currentRunLoop] removePort: receive_port forMode: mode]; + if ([request_modes containsObject:mode]) + { + [request_modes removeObject:mode]; + [[NSRunLoop currentRunLoop] removePort: receive_port forMode: mode]; } } @@ -614,7 +557,7 @@ static int messages_received_count; - (NSArray*) requestModes { - return [request_modes copy]; + return [[request_modes copy] autorelease]; } - (NSTimeInterval) requestTimeout @@ -622,11 +565,6 @@ static int messages_received_count; return request_timeout; } -- (id) retain -{ - return [super retain]; -} - - (id) rootObject { return [[self class] rootObjectForInPort: receive_port]; @@ -659,7 +597,7 @@ static int messages_received_count; - (void) setIndependantConversationQueueing: (BOOL)flag { - independant_queueing = flag; + independant_queueing = flag; } - (void) setReplyTimeout: (NSTimeInterval)to @@ -669,14 +607,17 @@ static int messages_received_count; - (void) setRequestMode: (NSString*)mode { - while ([request_modes count]>0 && [request_modes objectAtIndex:0]!=mode) { - [self removeRequestMode:[request_modes objectAtIndex:0]]; + while ([request_modes count] > 0 && [request_modes objectAtIndex: 0] != mode) + { + [self removeRequestMode: [request_modes objectAtIndex: 0]]; } - while ([request_modes count]>1) { - [self removeRequestMode:[request_modes objectAtIndex:1]]; + while ([request_modes count] > 1) + { + [self removeRequestMode: [request_modes objectAtIndex: 1]]; } - if (mode != nil && [request_modes count] == 0) { - [self addRequestMode:mode]; + if (mode != nil && [request_modes count] == 0) + { + [self addRequestMode: mode]; } } @@ -692,32 +633,36 @@ static int messages_received_count; - (NSDictionary*) statistics { - NSMutableDictionary* d; - id o; + NSMutableDictionary *d; + id o; - d = [NSMutableDictionary dictionaryWithCapacity:8]; + d = [NSMutableDictionary dictionaryWithCapacity: 8]; - /* - * These are in OPENSTEP 4.2 - */ - o = [NSNumber numberWithUnsignedInt:rep_in_count]; - [d setObject:o forKey:NSConnectionRepliesReceived]; - o = [NSNumber numberWithUnsignedInt:rep_out_count]; - [d setObject:o forKey:NSConnectionRepliesSent]; - o = [NSNumber numberWithUnsignedInt:req_in_count]; - [d setObject:o forKey:NSConnectionRequestsReceived]; - o = [NSNumber numberWithUnsignedInt:req_out_count]; - [d setObject:o forKey:NSConnectionRequestsSent]; + /* + * These are in OPENSTEP 4.2 + */ + o = [NSNumber numberWithUnsignedInt: rep_in_count]; + [d setObject: o forKey: NSConnectionRepliesReceived]; + o = [NSNumber numberWithUnsignedInt: rep_out_count]; + [d setObject: o forKey: NSConnectionRepliesSent]; + o = [NSNumber numberWithUnsignedInt: req_in_count]; + [d setObject: o forKey: NSConnectionRequestsReceived]; + o = [NSNumber numberWithUnsignedInt: req_out_count]; + [d setObject: o forKey: NSConnectionRequestsSent]; - /* - * These are GNUstep extras - */ - o = [NSNumber numberWithUnsignedInt:NSCountMapTable(local_targets)]; - [d setObject:o forKey:NSConnectionLocalCount]; - o = [NSNumber numberWithUnsignedInt:NSCountMapTable(remote_proxies)]; - [d setObject:o forKey:NSConnectionProxyCount]; + /* + * These are GNUstep extras + */ + o = [NSNumber numberWithUnsignedInt: NSCountMapTable(local_targets)]; + [d setObject: o forKey: NSConnectionLocalCount]; + o = [NSNumber numberWithUnsignedInt: NSCountMapTable(remote_proxies)]; + [d setObject: o forKey: NSConnectionProxyCount]; + [received_request_rmc_queue_gate lock]; + o = [NSNumber numberWithUnsignedInt: [received_request_rmc_queue count]]; + [received_request_rmc_queue_gate unlock]; + [d setObject: o forKey: @"Pending packets"]; - return d; + return d; } @end @@ -726,6 +671,46 @@ static int messages_received_count; @implementation NSConnection (GNUstepExtensions) +- (void) gcFinalize +{ + NSAutoreleasePool *arp = [NSAutoreleasePool new]; + + if (debug_connection) + NSLog(@"finalising 0x%x\n", (gsaddr)self); + + [self invalidate]; + [connection_table_gate lock]; + NSHashRemove(connection_table, self); + [timer invalidate]; + timer = nil; + [connection_table_gate unlock]; + + /* Remove rootObject from root_object_dictionary + if this is last connection */ + if (![NSConnection connectionsCountWithInPort:receive_port]) + [NSConnection setRootObject:nil forInPort:receive_port]; + + /* Remove receive port from run loop. */ + [self setRequestMode: nil]; + [[NSRunLoop currentRunLoop] removePort: receive_port + forMode: NSConnectionReplyMode]; + [request_modes release]; + + /* Finished with ports - releasing them may generate a notification */ + [receive_port release]; + [send_port release]; + + [proxiesHashGate lock]; + NSFreeMapTable (remote_proxies); + NSFreeMapTable (local_objects); + NSFreeMapTable (local_targets); + NSFreeMapTable (incoming_xref_2_const_ptr); + NSFreeMapTable (outgoing_const_ptr_2_xref); + [proxiesHashGate unlock]; + + [arp release]; +} + /* Getting and setting class variables */ + (Class) default_decoding_class @@ -797,26 +782,27 @@ static int messages_received_count; + (unsigned) connectionsCount { - return [connection_array count]; + return NSCountHashTable(connection_table); } + (unsigned) connectionsCountWithInPort: (NSPort*)aPort { - unsigned count = 0; - unsigned pos; + unsigned count = 0; + NSHashEnumerator enumerator; + NSConnection *o; - [connection_array_gate lock]; - count = [connection_array count]; - for (pos = 0; pos < [connection_array count]; pos++) { - id o = [connection_array objectAtIndex:pos]; - - if ([aPort isEqual: [o receivePort]]) { - count++; + [connection_table_gate lock]; + enumerator = NSEnumerateHashTable(connection_table); + while ((o = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil) + { + if ([aPort isEqual: [o receivePort]]) + { + count++; } } - [connection_array_gate unlock]; + [connection_table_gate unlock]; - return count; + return count; } @@ -828,8 +814,8 @@ static int messages_received_count; id newConn; newPort = [[default_receive_port_class newForReceiving] autorelease]; - newConn = [self newForInPort:newPort outPort:nil - ancestorConnection:nil]; + newConn = [self newForInPort: newPort outPort: nil + ancestorConnection: nil]; [self setRootObject:anObj forInPort:newPort]; return newConn; } @@ -891,9 +877,9 @@ static int messages_received_count; + (NSDistantObject*) rootProxyAtPort: (NSPort*)anOutPort withInPort: (NSPort *)anInPort { - NSConnection *newConn = [self newForInPort:anInPort - outPort:anOutPort - ancestorConnection:nil]; + NSConnection *newConn = [self newForInPort: anInPort + outPort: anOutPort + ancestorConnection: nil]; NSDistantObject *newRemote; newRemote = [newConn rootProxy]; @@ -904,25 +890,30 @@ static int messages_received_count; /* This is the designated initializer for NSConnection */ -+ (NSConnection*) newForInPort: (NSPort*)ip outPort: (NSPort*)op - ancestorConnection: ancestor ++ (NSConnection*) newForInPort: (NSPort*)ip + outPort: (NSPort*)op + ancestorConnection: (NSConnection*)ancestor { NSConnection *newConn; NSParameterAssert (ip); /* Find previously existing connection if there */ - newConn = [[self connectionByInPort: ip outPort: op] retain]; + newConn = [self connectionByInPort: ip outPort: op]; if (newConn) - return newConn; - - [connection_array_gate lock]; + { + if (debug_connection > 2) + NSLog(@"Found existing connection (0x%x) for \n\t%@\n\t%@\n", + (gsaddr)newConn, [ip description], [op description]); + return [newConn retain]; + } + [connection_table_gate lock]; newConn = [[NSConnection alloc] _superInit]; if (debug_connection) NSLog(@"Created new connection 0x%x\n\t%@\n\t%@\n", - (unsigned)newConn, [ip description], [op description]); - newConn->is_valid = 1; + (gsaddr)newConn, [ip description], [op description]); + newConn->is_valid = YES; newConn->receive_port = ip; [ip retain]; newConn->send_port = op; @@ -948,7 +939,7 @@ static int messages_received_count; /* This maps [proxy targetForProxy] to proxy. The proxy's are retained. */ newConn->remote_proxies = NSCreateMapTable (NSIntMapKeyCallBacks, - NSObjectMapValueCallBacks, 0); + NSNonOwnedPointerMapValueCallBacks, 0); newConn->incoming_xref_2_const_ptr = NSCreateMapTable (NSIntMapKeyCallBacks, @@ -1009,7 +1000,7 @@ static int messages_received_count; if (![[ancestor delegate] connection: ancestor shouldMakeNewConnection: (NSConnection*)newConn]) { - [connection_array_gate unlock]; + [connection_table_gate unlock]; [newConn release]; return nil; } @@ -1020,7 +1011,7 @@ static int messages_received_count; if (![[ancestor delegate] makeNewConnection: (NSConnection*)newConn sender: ancestor]) { - [connection_array_gate unlock]; + [connection_table_gate unlock]; [newConn release]; return nil; } @@ -1034,15 +1025,15 @@ static int messages_received_count; /* Register ourselves for invalidation notification when the ports become invalid. */ - [NotificationDispatcher addObserver: newConn - selector: @selector(portIsInvalid:) - name: NSPortDidBecomeInvalidNotification - object: ip]; + [NSNotificationCenter addObserver: newConn + selector: @selector(portIsInvalid:) + name: NSPortDidBecomeInvalidNotification + object: ip]; if (op) - [NotificationDispatcher addObserver: newConn - selector: @selector(portIsInvalid:) - name: NSPortDidBecomeInvalidNotification - object: op]; + [NSNotificationCenter addObserver: newConn + selector: @selector(portIsInvalid:) + name: NSPortDidBecomeInvalidNotification + object: op]; /* if OP is nil, making this notification request would have registered us to receive all NSPortDidBecomeInvalidNotification requests, independent of which port posted them. This isn't @@ -1051,12 +1042,12 @@ static int messages_received_count; /* In order that connections may be deallocated - there is an implementation of [-release] to automatically remove the connection from this array when it is the only thing retaining it. */ - [connection_array addObject: newConn]; - [connection_array_gate unlock]; + NSHashInsert(connection_table, (void*)newConn); + [connection_table_gate unlock]; - [NotificationDispatcher + [NSNotificationCenter postNotificationName: NSConnectionDidInitializeNotification - object: newConn]; + object: newConn]; return newConn; } @@ -1064,56 +1055,54 @@ static int messages_received_count; + (NSConnection*) connectionByInPort: (NSPort*)ip outPort: (NSPort*)op { - int count; - int i; + NSHashEnumerator enumerator; + NSConnection *o; NSParameterAssert (ip); - [connection_array_gate lock]; - count = [connection_array count]; - for (i = 0; i < count; i++) + [connection_table_gate lock]; + + enumerator = NSEnumerateHashTable(connection_table); + while ((o = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil) { id newConnInPort; id newConnOutPort; - NSConnection *newConn; - newConn = [connection_array objectAtIndex: i]; - newConnInPort = [newConn receivePort]; - newConnOutPort = [newConn sendPort]; + newConnInPort = [o receivePort]; + newConnOutPort = [o sendPort]; if ([newConnInPort isEqual: ip] && [newConnOutPort isEqual: op]) { - [connection_array_gate unlock]; - return newConn; + [connection_table_gate unlock]; + return o; } } - [connection_array_gate unlock]; + [connection_table_gate unlock]; return nil; } + (NSConnection*) connectionByOutPort: (NSPort*)op { - int i, count; + NSHashEnumerator enumerator; + NSConnection *o; NSParameterAssert (op); - [connection_array_gate lock]; + [connection_table_gate lock]; - count = [connection_array count]; - for (i = 0; i < count; i++) + enumerator = NSEnumerateHashTable(connection_table); + while ((o = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil) { id newConnOutPort; - NSConnection *newConn; - newConn = [connection_array objectAtIndex: i]; - newConnOutPort = [newConn sendPort]; + newConnOutPort = [o sendPort]; if ([newConnOutPort isEqual: op]) { - [connection_array_gate unlock]; - return newConn; + [connection_table_gate unlock]; + return o; } } - [connection_array_gate unlock]; + [connection_table_gate unlock]; return nil; } @@ -1125,7 +1114,7 @@ static int messages_received_count; + setDebug: (int)val { - debug_connection = val; + debug_connection = val; } /* Creating new rmc's for encoding requests and replies */ @@ -1192,7 +1181,6 @@ static int messages_received_count; int seq_num; NSParameterAssert (is_valid); - [[self retain] autorelease]; /* get the method types from the selector */ #if NeXT_runtime @@ -1214,6 +1202,8 @@ static int messages_received_count; op = [self newSendingRequestRmc]; seq_num = [op sequenceNumber]; + if (debug_connection > 4) + NSLog(@"building packet seq %d\n", seq_num); /* Send the types that we're using, so that the performer knows exactly what qualifiers we're using. @@ -1232,7 +1222,7 @@ static int messages_received_count; /* Send the rmc */ [op dismiss]; if (debug_connection > 1) - NSLog(@"Sent message to 0x%x\n", (unsigned)self); + NSLog(@"Sent message to 0x%x\n", (gsaddr)self); req_out_count++; /* Sent a request. */ /* Get the reply rmc, and decode it. */ @@ -1387,7 +1377,7 @@ static int messages_received_count; withName:NULL]; if (debug_connection > 1) - NSLog(@"Handling message from 0x%x\n", (unsigned)self); + NSLog(@"Handling message from 0x%x\n", (gsaddr)self); req_in_count++; /* Handling an incoming request. */ mframe_do_call (forward_type, decoder, encoder); [op dismiss]; @@ -1445,6 +1435,7 @@ static int messages_received_count; if ([rmc connection] != self) { + [rmc dismiss]; [NSException raise: @"ProxyDecodedBadTarget" format: @"request to release object on bad connection"]; } @@ -1456,26 +1447,23 @@ static int messages_received_count; for (pos = 0; pos < count; pos++) { unsigned target; - char vended; NSDistantObject *prox; [rmc decodeValueOfCType: @encode(typeof(target)) at: &target withName: NULL]; - [rmc decodeValueOfCType: @encode(typeof(char)) - at: &vended - withName: NULL]; - prox = (NSDistantObject*)[self includesLocalTarget: target]; if (prox != nil) { - if (vended) - { - [prox setVended]; - } + if (debug_connection > 3) + NSLog(@"releasing object with target (0x%x) on (0x%x)", + target, (gsaddr)self); [self removeLocalObject: [prox localForProxy]]; } + else if (debug_connection > 3) + NSLog(@"releasing object with target (0x%x) on (0x%x) - nothing to do", + target, (gsaddr)self); } [rmc dismiss]; @@ -1483,29 +1471,84 @@ static int messages_received_count; - (void) _service_retain: rmc forConnection: receiving_connection { - unsigned target; + unsigned target; + NSPortCoder *op; NSParameterAssert (is_valid); if ([rmc connection] != self) { + [rmc dismiss]; [NSException raise: @"ProxyDecodedBadTarget" format: @"request to retain object on bad connection"]; } + op = [[self encodingClass] newForWritingWithConnection: [rmc connection] + sequenceNumber: [rmc sequenceNumber] + identifier: RETAIN_REPLY]; + [rmc decodeValueOfCType: @encode(typeof(target)) at: &target withName: NULL]; + if (debug_connection > 3) + NSLog(@"looking to retain local object with target (0x%x) on (0x%x)", + target, (gsaddr)self); + if ([self includesLocalTarget: target] == nil) { GSLocalCounter *counter; - counter = (GSLocalCounter*)[[self class] includesLocalTarget: target]; - if (counter != nil) - [NSDistantObject proxyWithLocal: counter->object connection: self]; + [proxiesHashGate lock]; + counter = NSMapGet (all_connections_local_targets, (void*)target); + if (counter == nil) + { + /* + * If the target doesn't exist for any connection, but still + * persists in the cache (ie it was recently released) then + * we move it back from the cache to the main maps so we can + * retain it on this connection. + */ + counter = NSMapGet (all_connections_local_cached, (void*)target); + if (counter) + { + unsigned t = counter->target; + id o = counter->object; + + NSMapInsert(all_connections_local_objects, (void*)o, counter); + NSMapInsert(all_connections_local_targets, (void*)t, counter); + NSMapRemove(all_connections_local_cached, (void*)t); + if (debug_connection > 3) + NSLog(@"target (0x%x) moved from cache", target); + } + } + [proxiesHashGate unlock]; + if (counter == nil) + { + [op encodeObject: @"target not found anywhere" + withName: @"retain failed"]; + if (debug_connection > 3) + NSLog(@"target (0x%x) not found anywhere for retain", target); + } + else + { + [NSDistantObject proxyWithLocal: counter->object + connection: self]; + [op encodeObject: nil withName: @"retain ok"]; + if (debug_connection > 3) + NSLog(@"retained object (0x%x) target (0x%x) on connection(0x%x)", + counter->object, counter->target, self); + } + } + else + { + [op encodeObject: nil withName: @"already retained"]; + if (debug_connection > 3) + NSLog(@"target (0x%x) already retained on connection (0x%x)", + target, self); } + [op dismiss]; [rmc dismiss]; } @@ -1619,9 +1662,13 @@ static int messages_received_count; - (void) _handleRmc: rmc { - NSConnection* conn = [[rmc connection] retain]; + NSConnection *conn = [rmc connection]; + int ident = [rmc identifier]; - switch ([rmc identifier]) + if (debug_connection > 4) + NSLog(@"handling packet of type %d seq %d\n", ident, [rmc sequenceNumber]); + + switch (ident) { case ROOTPROXY_REQUEST: /* It won't take much time to handle this, so go ahead and service @@ -1642,14 +1689,12 @@ static int messages_received_count; if independant_queuing is NO. */ if (reply_depth == 0 || independant_queueing == NO) { - [self retain]; [conn _service_forwardForProxy: rmc]; /* Service any requests that were queued while we were waiting for replies. xxx Is this the right place for this check? */ if (reply_depth == 0) [self _handleQueuedRmcRequests]; - [self release]; } else { @@ -1661,6 +1706,7 @@ static int messages_received_count; case ROOTPROXY_REPLY: case METHOD_REPLY: case METHODTYPE_REPLY: + case RETAIN_REPLY: /* Remember multi-threaded callbacks will have to be handled specially */ [received_reply_rmc_queue_gate lock]; [received_reply_rmc_queue addObject: rmc]; @@ -1682,11 +1728,10 @@ static int messages_received_count; break; } default: - [conn release]; + [rmc dismiss]; [NSException raise: NSGenericException format: @"unrecognized NSPortCoder identifier"]; } - [conn release]; } - (void) _handleQueuedRmcRequests @@ -1694,6 +1739,7 @@ static int messages_received_count; id rmc; [received_request_rmc_queue_gate lock]; + [self retain]; while (is_valid && ([received_request_rmc_queue count] > 0)) { rmc = [received_request_rmc_queue objectAtIndex: 0]; @@ -1702,6 +1748,7 @@ static int messages_received_count; [self _handleRmc: rmc]; [received_request_rmc_queue_gate lock]; } + [self release]; [received_request_rmc_queue_gate unlock]; } @@ -1770,11 +1817,23 @@ static int messages_received_count; This method is called by InPort when it receives a new packet. */ + (void) invokeWithObject: packet { - id rmc = [NSPortCoder - newDecodingWithPacket: packet - connection: NSMapGet (receive_port_2_ancestor, - [packet receivingInPort])]; - [[rmc connection] _handleRmc: rmc]; + id rmc; + NSConnection *connection; + + if (debug_connection > 3) + NSLog(@"packet arrived on %@", [[packet receivingInPort] description]); + + connection = NSMapGet(receive_port_2_ancestor, [packet receivingInPort]); + if (connection && [connection isValid]) + { + rmc = [NSPortCoder newDecodingWithPacket: packet + connection: connection]; + [[rmc connection] _handleRmc: rmc]; + } + else + { + [packet release]; /* Discard data on invalid connection. */ + } } - (int) _newMsgNumber @@ -1800,13 +1859,13 @@ static int messages_received_count; NSParameterAssert (is_valid); [proxiesHashGate lock]; /* xxx Do we need to check to make sure it's not already there? */ - /* This retains anObj. */ + /* This retains object. */ NSMapInsert(local_objects, (void*)object, anObj); /* * Keep track of local objects accross all connections. */ - counter = NSMapGet(all_connections_local_targets, (void*)target); + counter = NSMapGet(all_connections_local_objects, (void*)object); if (counter) { counter->ref++; @@ -1823,8 +1882,9 @@ static int messages_received_count; [anObj setProxyTarget: target]; NSMapInsert(local_targets, (void*)target, anObj); if (debug_connection > 2) - NSLog(@"add local object (0x%x) to connection (0x%x) (ref %d)\n", - (unsigned)object, (unsigned) self, counter->ref); + NSLog(@"add local object (0x%x) target (0x%x) " + @"to connection (0x%x) (ref %d)\n", + (gsaddr)object, target, (gsaddr) self, counter->ref); [proxiesHashGate unlock]; } @@ -1843,15 +1903,13 @@ static int messages_received_count; /* This should get called whenever an object free's itself */ + (void) removeLocalObject: (id)anObj { - id c; - int i, count = [connection_array count]; + NSHashEnumerator enumerator; + NSConnection *o; - /* Don't assert (is_valid); */ - for (i = 0; i < count; i++) + enumerator = NSEnumerateHashTable(connection_table); + while ((o = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil) { - c = [connection_array objectAtIndex:i]; - [c removeLocalObject: anObj]; -// [c removeProxy: anObj]; + [o removeLocalObject: anObj]; } } @@ -1877,14 +1935,12 @@ static int messages_received_count; counter->ref--; if ((val = counter->ref) == 0) { - NSMapRemove(all_connections_local_objects, (void*)anObj); - NSMapRemove(all_connections_local_targets, (void*)target); /* * If this proxy has been vended onwards by another process, we * need to keep a reference to the local object around for a * while in case that other process needs it. */ - if ([prox isVended]) + if (0) { id item; if (timer == nil) @@ -1897,7 +1953,12 @@ static int messages_received_count; } item = [CachedLocalObject itemWithObject: counter time: 30]; NSMapInsert(all_connections_local_cached, (void*)target, item); + if (debug_connection > 3) + NSLog(@"placed local object (0x%x) target (0x%x) in cache", + (gsaddr)anObj, target); } + NSMapRemove(all_connections_local_objects, (void*)anObj); + NSMapRemove(all_connections_local_targets, (void*)target); } } @@ -1905,13 +1966,14 @@ static int messages_received_count; NSMapRemove(local_targets, (void*)target); if (debug_connection > 2) - NSLog(@"remove local object (0x%x) to connection (0x%x) (ref %d)\n", - (unsigned)anObj, (unsigned) self, val); + NSLog(@"remove local object (0x%x) target (0x%x) " + @"from connection (0x%x) (ref %d)\n", + (gsaddr)anObj, target, (gsaddr)self, val); [proxiesHashGate unlock]; } -- (void) _release_targets: (NSDistantObject**)list count:(unsigned int)number +- (void) _release_targets: (unsigned*)list count: (unsigned)number { NS_DURING { @@ -1921,31 +1983,29 @@ static int messages_received_count; * proxies for them any more. */ if (receive_port && is_valid && number > 0) { - id op; - unsigned int i; + id op; + unsigned i; - op = [[self encodingClass] - newForWritingWithConnection: self - sequenceNumber: [self _newMsgNumber] - identifier: PROXY_RELEASE]; + op = [[self encodingClass] + newForWritingWithConnection: self + sequenceNumber: [self _newMsgNumber] + identifier: PROXY_RELEASE]; - [op encodeValueOfCType: @encode(typeof(number)) - at: &number - withName: NULL]; + [op encodeValueOfCType: @encode(unsigned) + at: &number + withName: NULL]; - for (i = 0; i < number; i++) { - unsigned target = [list[i] targetForProxy]; - char vended = [list[i] isVended]; - - [op encodeValueOfCType: @encode(typeof(target)) - at: &target - withName: NULL]; - [op encodeValueOfCType: @encode(char) - at: &vended - withName: NULL]; + for (i = 0; i < number; i++) + { + [op encodeValueOfCType: @encode(unsigned) + at: &list[i] + withName: NULL]; + if (debug_connection > 3) + NSLog(@"sending release for target (0x%x) on (0x%x)", + list[i], (gsaddr)self); } - [op dismiss]; + [op dismiss]; } } NS_HANDLER @@ -1967,7 +2027,9 @@ static int messages_received_count; if (receive_port && is_valid) { id op; + id ip; unsigned int i; + id result; int seq_num = [self _newMsgNumber]; op = [[self encodingClass] @@ -1980,31 +2042,35 @@ static int messages_received_count; withName: NULL]; [op dismiss]; + ip = [self _getReceivedReplyRmcWithSequenceNumber: seq_num]; + [ip decodeObjectAt: &result withName: NULL]; + if (result != nil) + NSLog(@"failed to retain target - %@\n", result); + [ip dismiss]; } } NS_HANDLER { - if (debug_connection) - NSLog(@"failed to retain target - %@\n", [localException name]); + NSLog(@"failed to retain target - %@\n", [localException name]); } NS_ENDHANDLER } - (void) removeProxy: (NSDistantObject*)aProxy { - unsigned target = (unsigned)[aProxy targetForProxy]; + unsigned target = [aProxy targetForProxy]; - /* Don't assert (is_valid); */ - [proxiesHashGate lock]; - /* This also releases aProxy */ - NSMapRemove (remote_proxies, (void*)target); - [proxiesHashGate unlock]; + /* Don't assert (is_valid); */ + [proxiesHashGate lock]; + /* This also releases aProxy */ + NSMapRemove (remote_proxies, (void*)target); + [proxiesHashGate unlock]; - /* - * Tell the remote application that we have removed our proxy and - * it can release it's local object. - */ - [self _release_targets:&aProxy count:1]; + /* + * Tell the remote application that we have removed our proxy and + * it can release it's local object. + */ + [self _release_targets: &target count: 1]; } - (NSArray *) localObjects @@ -2050,8 +2116,11 @@ static int messages_received_count; NSParameterAssert([aProxy connectionForProxy] == self); [proxiesHashGate lock]; if (NSMapGet (remote_proxies, (void*)target)) - [NSException raise: NSGenericException - format: @"Trying to add the same proxy twice"]; + { + [proxiesHashGate unlock]; + [NSException raise: NSGenericException + format: @"Trying to add the same proxy twice"]; + } NSMapInsert (remote_proxies, (void*)target, aProxy); [proxiesHashGate unlock]; } @@ -2103,9 +2172,6 @@ static int messages_received_count; NSParameterAssert (all_connections_local_targets); [proxiesHashGate lock]; ret = NSMapGet (all_connections_local_targets, (void*)target); - if (ret == nil) { - ret = NSMapGet (all_connections_local_cached, (void*)target); - } [proxiesHashGate unlock]; return ret; } @@ -2253,12 +2319,12 @@ static int messages_received_count; if (debug_connection) NSLog(@"Received port invalidation notification for " - @"connection 0x%x\n\t%@\n", (unsigned)self, + @"connection 0x%x\n\t%@\n", (gsaddr)self, [port description]); /* We shouldn't be getting any port invalidation notifications, except from our own ports; this is how we registered ourselves - with the NotificationDispatcher in + with the NSNotificationCenter in +newForInPort:outPort:ancestorConnection. */ NSParameterAssert (port == receive_port || port == send_port); diff --git a/Source/NSDistantObject.m b/Source/NSDistantObject.m index 3dc26d509..cc3ec4dea 100644 --- a/Source/NSDistantObject.m +++ b/Source/NSDistantObject.m @@ -170,24 +170,7 @@ enum - (void) dealloc { - if (_object) - { - /* - * A proxy for local object retains it's target so that it - * will continue to exist as long as there is a remote - * application using it - so we release the object here. - */ - [_object release]; - } - else - { - /* - * A proxy retains it's connection so that the connection will - * continue to exist as long as there is a somethig to use it. - * So we release our reference to the connection here. - */ - [_connection release]; - } + [self gcFinalize]; [super dealloc]; } @@ -199,7 +182,8 @@ enum if ([aRmc class] != [PortEncoder class]) [NSException raise: NSGenericException -format: @"NSDistantObject objects only encode with PortEncoder class"]; + format: @"NSDistantObject objects only " + @"encode with PortEncoder class"]; encoder_connection = [(NSPortCoder*)aRmc connection]; NSAssert(encoder_connection, NSInternalInconsistencyException); @@ -223,8 +207,7 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; if (debug_proxy) NSLog(@"Sending a proxy, will be remote 0x%x connection 0x%x\n", - (unsigned)proxy_target, - (unsigned)_connection); + proxy_target, (gsaddr)_connection); [aRmc encodeValueOfCType: @encode(typeof(proxy_tag)) at: &proxy_tag @@ -243,8 +226,7 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; if (debug_proxy) NSLog(@"Sending a proxy, will be local 0x%x connection 0x%x\n", - (unsigned)proxy_target, - (unsigned)_connection); + proxy_target, (gsaddr)_connection); [aRmc encodeValueOfCType: @encode(typeof(proxy_tag)) at: &proxy_tag @@ -260,19 +242,32 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; /* * This proxy will still be remote on the other side */ - NSPort *proxy_connection_out_port = [_connection sendPort]; + NSPort *proxy_connection_out_port = [_connection sendPort]; + NSDistantObject *localProxy; - NSAssert(proxy_connection_out_port, NSInternalInconsistencyException); - NSAssert([proxy_connection_out_port isValid], NSInternalInconsistencyException); - NSAssert(proxy_connection_out_port != [encoder_connection sendPort], NSInternalInconsistencyException); + NSAssert(proxy_connection_out_port, + NSInternalInconsistencyException); + NSAssert([proxy_connection_out_port isValid], + NSInternalInconsistencyException); + NSAssert(proxy_connection_out_port != [encoder_connection sendPort], + NSInternalInconsistencyException); proxy_tag = PROXY_REMOTE_FOR_BOTH; + /* + * Get a proxy to refer to self - we send this to the other + * side so we will be retained until the other side has + * obtained a proxy to the original object via a connection + * to the original vendor. + */ + localProxy = [NSDistantObject proxyWithLocal: self + connection: encoder_connection]; + if (debug_proxy) NSLog(@"Sending triangle-connection proxy 0x%x " - @"proxy-conn 0x%x to-conn 0x%x\n", - (unsigned)_object, - (unsigned)_connection, (unsigned)encoder_connection); + @"proxy-conn 0x%x to-proxy 0x%x to-conn 0x%x\n", + localProxy->_handle, (gsaddr)localProxy->_connection, + proxy_target, (gsaddr)_connection); /* * It's remote here, so we need to tell other side where to form @@ -282,16 +277,16 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; at: &proxy_tag withName: @"Proxy remote for both sender and receiver"]; + [aRmc encodeValueOfCType: @encode(typeof(localProxy->_handle)) + at: &localProxy->_handle + withName: @"Intermediary target"]; + [aRmc encodeValueOfCType: @encode(typeof(proxy_target)) at: &proxy_target - withName: @"Proxy target"]; + withName: @"Original target"]; [aRmc encodeBycopyObject: proxy_connection_out_port - withName: @"Proxy outPort"]; - /* - * Make a note that we have passed this on to another process. - */ - _isVended = YES; + withName: @"Original port"]; } } @@ -341,13 +336,14 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; if (debug_proxy) NSLog(@"Receiving a proxy for local object 0x%x " - @"connection 0x%x\n", target, (unsigned)decoder_connection); + @"connection 0x%x\n", target, (gsaddr)decoder_connection); if (![[decoder_connection class] includesLocalTarget: target]) { [self release]; [NSException raise: @"ProxyDecodedBadTarget" - format: @"No local object with given address"]; + format: @"No local object with given target (0x%x)", + target]; } else { @@ -357,7 +353,7 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; if (debug_proxy) { NSLog(@"Local object is 0x%x (0x%x)\n", - (unsigned)o, (unsigned)o ? o->_object : 0); + (gsaddr)o, (gsaddr)o ? o->_object : 0); } [self release]; return o ? [o->_object retain] : nil; @@ -376,7 +372,7 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; withName: NULL]; if (debug_proxy) NSLog(@"Receiving a proxy, was local 0x%x connection 0x%x\n", - (unsigned)target, (unsigned)decoder_connection); + target, (gsaddr)decoder_connection); [self release]; return [[NSDistantObject proxyWithTarget: target connection: decoder_connection] retain]; @@ -398,7 +394,29 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; NSDistantObject *result; NSConnection *proxy_connection; NSPort *proxy_connection_out_port = nil; + unsigned intermediary; + /* + * There is an object on the intermediary host that is keeping + * that hosts proxy for the original object retained, thus + * ensuring that the original is not released. We create a + * proxy for that intermediate proxy. When we release this + * proxy, the intermediary will be free to release it's proxy + * and the original can then be released. Of course, by that + * time we will have obtained our own proxy for the original + * object ... + */ + [aCoder decodeValueOfCType: @encode(typeof(intermediary)) + at: &intermediary + withName: NULL]; + [NSDistantObject proxyWithTarget: intermediary + connection: decoder_connection]; + + /* + * Now we get the target number and port for the orignal object + * and (if necessary) get the originating process to retain the + * object for us. + */ [aCoder decodeValueOfCType: @encode(typeof(target)) at: &target withName: NULL]; @@ -407,9 +425,10 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; withName: NULL]; NSAssert(proxy_connection_out_port, NSInternalInconsistencyException); - /* xxx - if there already exists a connection for talking to the - * out port, we use that one rather than creating a new one from - * our listening port. + /* + # If there already exists a connection for talking to the + * out port, we use that one rather than creating a new one from + * our listening port. * * First we try for a connection from our receive port, * Then we try any connection to the send port @@ -423,21 +442,28 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; outPort: proxy_connection_out_port]; if (proxy_connection == nil) - proxy_connection = [[decoder_connection class] - connectionByOutPort: - proxy_connection_out_port]; + { + proxy_connection = [[decoder_connection class] + connectionByOutPort: proxy_connection_out_port]; + } if (proxy_connection == nil) - proxy_connection = [[decoder_connection class] + { + proxy_connection = [[decoder_connection class] newForInPort: [decoder_connection receivePort] - outPort: proxy_connection_out_port - ancestorConnection: decoder_connection]; + outPort: proxy_connection_out_port + ancestorConnection: decoder_connection]; + [proxy_connection setNotOwned]; + [proxy_connection autorelease]; + } if (debug_proxy) NSLog(@"Receiving a triangle-connection proxy 0x%x " - @"connection 0x%x\n", target, (unsigned)proxy_connection); + @"connection 0x%x\n", target, (gsaddr)proxy_connection); - NSAssert(proxy_connection != decoder_connection, NSInternalInconsistencyException); - NSAssert([proxy_connection isValid], NSInternalInconsistencyException); + NSAssert(proxy_connection != decoder_connection, + NSInternalInconsistencyException); + NSAssert([proxy_connection isValid], + NSInternalInconsistencyException); /* * If we don't already have a proxy for the object on the @@ -447,9 +473,17 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; if ([proxy_connection includesProxyForTarget: target] == NO) [proxy_connection retainTarget: target]; - [self release]; result = [[NSDistantObject proxyWithTarget: target connection: proxy_connection] retain]; + [self release]; + + /* + * Finally - we have a proxy via a direct connection to the + * originating server. We have also created a proxy to an + * intermediate object - but this proxy has not been retained + * and will therefore go away when the current autorelease + * pool is destroyed. + */ return result; } @@ -475,26 +509,26 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; */ if ((new_proxy = [aConnection localForObject: anObject])) { - [self dealloc]; + [self release]; return [new_proxy retain]; } - _connection = aConnection; + /* + * We don't need to retain the oibject here - the connection + * will retain the proxies local object if necessary (and release it + * when all proxies referring to it have been released). + */ + _object = anObject; /* - * We retain our target object so it can't disappear while a remote - * application wants to use it. - */ - _object = [anObject retain]; - - /* - * We register this object with the connection using it. + * We register this proxy with the connection using it. */ + _connection = [aConnection retain]; [_connection addLocalObject: self]; if (debug_proxy) - NSLog(@"Created new local=0x%x object 0x%x connection 0x%x\n", - (unsigned)self, (unsigned)_object, (unsigned)_connection); + NSLog(@"Created new local=0x%x object 0x%x target 0x%x connection 0x%x\n", + (gsaddr)self, (gsaddr)_object, _handle, (gsaddr)_connection); return self; } @@ -511,7 +545,7 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; */ if ((new_proxy = [aConnection proxyForTarget: target])) { - [self dealloc]; + [self release]; return [new_proxy retain]; } @@ -530,8 +564,8 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; [_connection addProxy: self]; if (debug_proxy) - NSLog(@"Created new proxy=0x%x object 0x%x connection 0x%x\n", - (unsigned)self, (unsigned)_object, (unsigned)_connection); + NSLog(@"Created new proxy=0x%x target 0x%x connection 0x%x\n", + (gsaddr)self, _handle, (gsaddr)_connection); return self; } @@ -583,36 +617,36 @@ format: @"NSDistantObject objects only encode with PortEncoder class"]; _protocol = aProtocol; } -- (void) release -{ - if ([self retainCount] == 2) - { - if (_object == nil) - { - /* - * If the only thing retaining us after this release is our - * connection we must be removed from the connection. - * Bracket that removal with a retain and release to ensure - * that we don't have problems when the connection releases us. - */ - [super retain]; - [_connection removeProxy: self]; - [super release]; - } - } - [super release]; -} - @end @implementation NSDistantObject(GNUstepExtensions) -+ newForRemoteTarget: (unsigned)target connection: (NSConnection*)conn +- (void) gcFinalize { - return [[NSDistantObject alloc] initWithTarget: target connection: conn]; + if (_connection) + { + if (debug_proxy > 3) + NSLog(@"retain count for connection (0x%x) is now %u\n", + (gsaddr)_connection, [_connection retainCount]); + /* + * A proxy for local object does not retain it's target - the + * NSConnection class does that for us - so we need not release it. + * For a local object the connection also retains this proxy, so we + * can't be deallocated unless we are already removed from the + * connection. + * + * A proxy retains it's connection so that the connection will + * continue to exist as long as there is a something to use it. + * So we release our reference to the connection here just as soon + * as we have removed ourself from the connection. + */ + if (_object == nil) + [_connection removeProxy: self]; + [_connection release]; + } } -- awakeAfterUsingCoder: aDecoder +- (id) awakeAfterUsingCoder: (NSCoder*)aDecoder { return self; } @@ -629,157 +663,6 @@ static inline BOOL class_is_kind_of (Class self, Class aClassObject) -#if 0 -+ newWithCoder: aRmc -{ - gsu8 proxy_tag; - unsigned target; - id decoder_connection; - - if ([aRmc class] != [PortDecoder class]) - [NSException raise: NSGenericException -format: @"NSDistantObject objects only decode with PortDecoder class"]; - - decoder_connection = [aRmc connection]; - NSAssert(decoder_connection, NSInternalInconsistencyException); - - /* First get the tag, so we know what values need to be decoded. */ - [aRmc decodeValueOfCType: @encode(typeof(proxy_tag)) - at: &proxy_tag - withName: NULL]; - - switch (proxy_tag) - { - case PROXY_LOCAL_FOR_RECEIVER: - /* - * This was a proxy on the other side of the connection, but - * here it's local. - * Lookup the target handle to ensure that it exists here. - * Return a retained copy of the local target object. - */ - [aRmc decodeValueOfCType: @encode(typeof(target)) - at: &target - withName: NULL]; - - if (debug_proxy) - NSLog(@"Receiving a proxy for local object 0x%x " - @"connection 0x%x\n", target, (unsigned)decoder_connection); - - if (![[decoder_connection class] includesLocalTarget: target]) - [NSException raise: @"ProxyDecodedBadTarget" - format: @"No local object with given address"]; - else - { - NSDistantObject *o; - - o = [decoder_connection includesLocalTarget: target]; - if (debug_proxy) - { - NSLog(@"Local object is 0x%x (0x%x)\n", - (unsigned)o, (unsigned)o->_object); - } - [self release]; - return [o->_object retain]; - } - - case PROXY_LOCAL_FOR_SENDER: - /* - * This was a local object on the other side of the connection, - * but here it's a proxy object. Get the target address, and - * send [NSDistantObject +proxyWithTarget:connection:]; this will - * return the proxy object we already created for this target, or - * create a new proxy object if necessary. - */ - [aRmc decodeValueOfCType: @encode(typeof(target)) - at: &target - withName: NULL]; - if (debug_proxy) - NSLog(@"Receiving a proxy, was local 0x%x connection 0x%x\n", - (unsigned)target, (unsigned)decoder_connection); - return [[NSDistantObject proxyWithTarget: target - connection: decoder_connection] retain]; - - case PROXY_REMOTE_FOR_BOTH: - /* - * This was a proxy on the other side of the connection, and it - * will be a proxy on this side too; that is, the local version - * of this object is not on this host, not on the host the - * NSPortCoder is connected to, but on a *third* host. - * This is why I call this a "triangle connection". In addition - * to decoding the target, we decode the OutPort object that we - * will use to talk directly to this third host. We send - * [NSConnection +newForInPort:outPort:ancestorConnection:]; this - * will either return the connection already created for this - * inPort/outPort pair, or create a new connection if necessary. - */ - { - NSDistantObject *result; - NSConnection *proxy_connection; - NSPort* proxy_connection_out_port = nil; - - [aRmc decodeValueOfCType: @encode(typeof(target)) - at: &target - withName: NULL]; - - [aRmc decodeObjectAt: &proxy_connection_out_port - withName: NULL]; - - NSAssert(proxy_connection_out_port, NSInternalInconsistencyException); - /* xxx - if there already exists a connection for talking to the - * out port, we use that one rather than creating a new one from - * our listening port. - * - * First we try for a connection from our receive port, - * Then we try any connection to the send port - * Finally we resort to creating a new connection - we don't - * release the newly created connection - it will get released - * automatically when no proxies are left on it. - */ - proxy_connection = [[decoder_connection class] - connectionByInPort: - [decoder_connection receivePort] - outPort: - proxy_connection_out_port]; - if (proxy_connection == nil) - proxy_connection = [[decoder_connection class] - connectionByOutPort: - proxy_connection_out_port]; - if (proxy_connection == nil) - proxy_connection = [[decoder_connection class] - newForInPort: [decoder_connection receivePort] - outPort: proxy_connection_out_port - ancestorConnection: decoder_connection]; - - if (debug_proxy) - NSLog(@"Receiving a triangle-connection proxy 0x%x " - @"connection 0x%x\n", target, (unsigned)proxy_connection); - - NSAssert(proxy_connection != decoder_connection, NSInternalInconsistencyException); - NSAssert([proxy_connection isValid], NSInternalInconsistencyException); - - /* - * If we don't already have a proxy for the object on the - * remote system, we must tell the other end to retain its - * local object for our use. - */ - if ([proxy_connection includesProxyForTarget: target] == NO) - [proxy_connection retainTarget: target]; - - result = [[NSDistantObject proxyWithTarget: target - connection: proxy_connection] retain]; - return result; - } - - default: - /* xxx This should be something different than NSGenericException. */ - [NSException raise: NSGenericException - format: @"Bad proxy tag"]; - } - /* Not reached. */ - return nil; -} -#endif - - (const char *) selectorTypeForProxy: (SEL)selector { #if NeXT_runtime @@ -802,7 +685,7 @@ format: @"NSDistantObject objects only decode with PortDecoder class"]; #endif } -- forward: (SEL)aSel :(arglist_t)frame +- (id) forward: (SEL)aSel :(arglist_t)frame { if (debug_proxy) NSLog(@"NSDistantObject forwarding %s\n", sel_get_name(aSel)); @@ -819,22 +702,22 @@ format: @"NSDistantObject objects only decode with PortDecoder class"]; argFrame: frame]; } -- classForCoder +- (Class) classForCoder { return object_get_class (self); } -- classForPortCoder +- (Class) classForPortCoder { return object_get_class (self); } -- replacementObjectForCoder:(NSCoder*)aCoder +- (id) replacementObjectForCoder: (NSCoder*)aCoder { return self; } -- replacementObjectForPortCoder:(NSPortCoder*)aCoder +- (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder { return self; } diff --git a/Source/TcpPort.m b/Source/TcpPort.m index e68de24ae..2012632ec 100644 --- a/Source/TcpPort.m +++ b/Source/TcpPort.m @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -129,14 +130,15 @@ static int debug_tcp_port = 0; @interface TcpInPacket (Private) - (int) _fillFromSocket: (int)s; + (void) _getPacketSize: (int*)size - andReplyPort: (id*)rp - fromSocket: (int)s - inPort: ip; + andSendPort: (id*)sp + andReceivePort: (id*)rp + fromSocket: (int)s; @end @interface TcpOutPacket (Private) - (void) _writeToSocket: (int)s - withReplySockaddr: (struct sockaddr_in*)addr + withSendPort: (id)sp + withReceivePort: (id)rp timeout: (NSTimeInterval)t; @end @@ -428,9 +430,9 @@ static NSMapTable* port_number_2_port; will accept connection on any of the machine network addresses; most machine will have both an Internet address, and the "localhost" address (i.e. 127.0.0.1) */ - p->_listening_address.sin_addr.s_addr = htonl (INADDR_ANY); + p->_listening_address.sin_addr.s_addr = GSSwapHostI32ToBig (INADDR_ANY); p->_listening_address.sin_family = AF_INET; - p->_listening_address.sin_port = htons (n); + p->_listening_address.sin_port = GSSwapHostI16ToBig (n); /* N may be zero, in which case bind() will choose a port number for us. */ if (bind (p->_port_socket, @@ -448,7 +450,7 @@ static NSMapTable* port_number_2_port; for (count = 0; count < 10; count++) { memset(&p->_listening_address, 0, sizeof(p->_listening_address)); - p->_listening_address.sin_addr.s_addr = htonl (INADDR_ANY); + p->_listening_address.sin_addr.s_addr = GSSwapHostI32ToBig (INADDR_ANY); p->_listening_address.sin_family = AF_INET; if (bind (p->_port_socket, (struct sockaddr*) &(p->_listening_address), @@ -484,7 +486,7 @@ static NSMapTable* port_number_2_port; } NSAssert(p->_listening_address.sin_port, NSInternalInconsistencyException); - n = ntohs(p->_listening_address.sin_port); + n = GSSwapBigI16ToHost(p->_listening_address.sin_port); } /* Now change _LISTENING_ADDRESS to the specific network address of this @@ -692,11 +694,12 @@ static NSMapTable* port_number_2_port; /* First, get the packet size and reply port, (which is encoded in the first few bytes of the stream). */ int packet_size; - id reply_port; + id send_port; + id receive_port; [TcpInPacket _getPacketSize: &packet_size - andReplyPort: &reply_port - fromSocket: fd_index - inPort: self]; + andSendPort: &send_port + andReceivePort: &receive_port + fromSocket: fd_index]; /* If we got an EOF when trying to read the packet prefix, invalidate the port, and keep on waiting for incoming data on other sockets. */ @@ -710,8 +713,8 @@ static NSMapTable* port_number_2_port; { packet = [[TcpInPacket alloc] initForReceivingWithCapacity: packet_size - receivingInPort: self - replyOutPort: reply_port]; + receivingInPort: send_port + replyOutPort: receive_port]; if (packet == nil) [NSException raise: NSInternalInconsistencyException format: @"[TcpInPort _tryToGetPacketFromReadableFD:" @@ -864,7 +867,7 @@ static NSMapTable* port_number_2_port; - (int) portNumber { - return (int) ntohs (_listening_address.sin_port); + return (int) GSSwapBigI16ToHost (_listening_address.sin_port); } - (void) invalidate @@ -882,7 +885,7 @@ static NSMapTable* port_number_2_port; +newForReceivingFromPortNumber: from returning invalid sockets. */ NSMapRemove (socket_2_port, (void*)_port_socket); NSMapRemove (port_number_2_port, - (void*)(int) ntohs(_listening_address.sin_port)); + (void*)(int)GSSwapBigI16ToHost(_listening_address.sin_port)); for (i = 0; NSNextMapEnumeratorPair (&me, (void*)&sock, (void*)&out_port); @@ -943,7 +946,7 @@ static NSMapTable* port_number_2_port; object_get_class_name (self), is_valid ? ' ' : '-', (unsigned)self, - ntohs (_listening_address.sin_port), + GSSwapBigI16ToHost(_listening_address.sin_port), _port_socket]; } @@ -1259,7 +1262,7 @@ static NSMapTable *out_port_bag = NULL; /* Get the sockaddr_in address. */ memcpy (&addr.sin_addr, hp->h_addr, hp->h_length); addr.sin_family = AF_INET; - addr.sin_port = htons (n); + addr.sin_port = GSSwapHostI16ToBig (n); return [self newForSendingToSockaddr: &addr withAcceptedSocket: 0 @@ -1293,7 +1296,7 @@ static NSMapTable *out_port_bag = NULL; } NSAssert(size == sizeof (struct sockaddr_in), NSInternalInconsistencyException); /* xxx Perhaps I have to get peer name here!! */ - NSAssert(ntohs (addr.sin_port) != [p portNumber], NSInternalInconsistencyException); + NSAssert(GSSwapBigI16ToHost(addr.sin_port) != [p portNumber], NSInternalInconsistencyException); #elif 0 struct sockaddr_in in_port_address; c = read (s, &in_port_address, sizeof(struct sockaddr_in)); @@ -1339,12 +1342,14 @@ static NSMapTable *out_port_bag = NULL; /* Ask the packet to write it's bytes to the socket. The TcpPacket will also write a prefix, indicating the packet size - and the reply port address. If REPLY_PORT is nil, the second argument - to this call with be NULL, and __writeToSocket:withReplySockaddr:timeout: + and the port addresses. If REPLY_PORT is nil, the third argument + to this call with be NULL, and + __writeToSocket:withSendPort:withReceivePort:timeout: will know that there is no reply port. */ [packet _writeToSocket: _port_socket - withReplySockaddr: [reply_port _listeningSockaddr] - timeout: timeout]; + withSendPort: self + withReceivePort: reply_port + timeout: timeout]; return YES; } @@ -1355,7 +1360,7 @@ static NSMapTable *out_port_bag = NULL; - (int) portNumber { - return (int) ntohs (_remote_in_port_address.sin_port); + return (int) GSSwapBigI16ToHost (_remote_in_port_address.sin_port); } - (void) close @@ -1442,7 +1447,7 @@ static NSMapTable *out_port_bag = NULL; is_valid ? ' ' : '-', (unsigned)self, inet_ntoa (_remote_in_port_address.sin_addr), - ntohs (_remote_in_port_address.sin_port), + GSSwapBigI16ToHost(_remote_in_port_address.sin_port), _port_socket]; } @@ -1450,7 +1455,7 @@ static NSMapTable *out_port_bag = NULL; { NSAssert(is_valid, NSInternalInconsistencyException); NSAssert(!_polling_in_port - || (ntohs (_remote_in_port_address.sin_port) + || (GSSwapBigI16ToHost(_remote_in_port_address.sin_port) != [_polling_in_port portNumber]), NSInternalInconsistencyException); /* Encode these at bytes, not as C-variables, because they are already in "network byte-order". */ @@ -1462,7 +1467,7 @@ static NSMapTable *out_port_bag = NULL; withName: @"inet address"]; if (debug_tcp_port) NSLog(@"TcpOutPort encoded port %hd host %s\n", - ntohs (_remote_in_port_address.sin_port), + GSSwapBigI16ToHost(_remote_in_port_address.sin_port), inet_ntoa (_remote_in_port_address.sin_addr)); } @@ -1479,7 +1484,7 @@ static NSMapTable *out_port_bag = NULL; withName: NULL]; if (debug_tcp_port) NSLog(@"TcpOutPort decoded port %hd host %s\n", - ntohs (addr.sin_port), + GSSwapBigI16ToHost(addr.sin_port), inet_ntoa (addr.sin_addr)); return [TcpOutPort newForSendingToSockaddr: &addr withAcceptedSocket: 0 @@ -1491,28 +1496,30 @@ static NSMapTable *out_port_bag = NULL; /* In and Out Packet classes. */ -/* If you change this "unsigned long", you must change the use - of ntohl() and htonl() below. */ -#define PREFIX_LENGTH_TYPE unsigned long +#define PREFIX_LENGTH_TYPE gsu32 #define PREFIX_LENGTH_SIZE sizeof (PREFIX_LENGTH_TYPE) #define PREFIX_ADDRESS_TYPE struct sockaddr_in #define PREFIX_ADDRESS_SIZE sizeof (PREFIX_ADDRESS_TYPE) -#define PREFIX_SIZE (PREFIX_LENGTH_SIZE + PREFIX_ADDRESS_SIZE) +#define PREFIX_SP_OFF PREFIX_LENGTH_SIZE +#define PREFIX_RP_OFF (PREFIX_LENGTH_SIZE + PREFIX_ADDRESS_SIZE) +#define PREFIX_SIZE (PREFIX_LENGTH_SIZE + 2*PREFIX_ADDRESS_SIZE) @implementation TcpInPacket + (void) _getPacketSize: (int*)packet_size - andReplyPort: (id*)rp + andSendPort: (id*)sp + andReceivePort: (id*)rp fromSocket: (int)s - inPort: ip { - char prefix_buffer[PREFIX_SIZE]; - int c; + char prefix_buffer[PREFIX_SIZE]; + int c; c = tryRead (s, 3, prefix_buffer, PREFIX_SIZE); if (c <= 0) { - *packet_size = EOF; *rp = nil; + *packet_size = EOF; + *sp = nil; + *rp = nil; return; } if (c != PREFIX_SIZE) @@ -1522,27 +1529,48 @@ static NSMapTable *out_port_bag = NULL; we should treat it differently. */ fprintf (stderr, "[%s %s]: Got %d chars instead of full prefix\n", class_get_class_name (self), sel_get_name (_cmd), c); - *packet_size = EOF; *rp = nil; + *packet_size = EOF; + *sp = nil; + *rp = nil; return; } /* *size is the number of bytes in the packet, not including the PREFIX_SIZE-byte header. */ - *packet_size = ntohl (*(PREFIX_LENGTH_TYPE*) prefix_buffer); + *packet_size = GSSwapBigI32ToHost (*(PREFIX_LENGTH_TYPE*) prefix_buffer); NSAssert(packet_size, NSInternalInconsistencyException); /* If the reply address is non-zero, and the TcpOutPort for this socket doesn't already have its _address ivar set, then set it now. */ { struct sockaddr_in addr; - /* Do this memcpy instead of simply casting the pointer because + + /* Use memcpy instead of simply casting the pointer because some systems fail to do the cast correctly (due to alignment issues?) */ - memcpy (&addr, prefix_buffer + PREFIX_LENGTH_SIZE, sizeof (typeof (addr))); + + /* + * Get the senders send port (our receive port) + */ + memcpy (&addr, prefix_buffer + PREFIX_SP_OFF, sizeof (typeof (addr))); + if (addr.sin_family) + { + gsu16 pnum = GSSwapBigI16ToHost(addr.sin_port); + + *sp = [TcpInPort newForReceivingFromPortNumber: pnum]; + [(*sp) autorelease]; + } + else + *sp = nil; + + /* + * Now get the senders receive port (our send port) + */ + memcpy (&addr, prefix_buffer + PREFIX_RP_OFF, sizeof (typeof (addr))); if (addr.sin_family) { *rp = [TcpOutPort newForSendingToSockaddr: &addr - withAcceptedSocket: s - pollingInPort: ip]; + withAcceptedSocket: s + pollingInPort: *sp]; [(*rp) autorelease]; } else @@ -1574,26 +1602,39 @@ static NSMapTable *out_port_bag = NULL; } - (void) _writeToSocket: (int)s - withReplySockaddr: (struct sockaddr_in*)addr + withSendPort: (id)sp + withReceivePort: (id)rp timeout: (NSTimeInterval)timeout { - int c; + struct sockaddr_in *addr; + int c; if (debug_tcp_port > 1) NSLog(@"%s: Write to socket %d\n", object_get_class_name (self), s); /* Put the packet size in the first four bytes of the packet. */ NSAssert(prefix == PREFIX_SIZE, NSInternalInconsistencyException); - *(PREFIX_LENGTH_TYPE*)[data mutableBytes] = htonl (eof_position); + *(PREFIX_LENGTH_TYPE*)[data mutableBytes] = GSSwapHostI32ToBig(eof_position); + addr = [sp _remoteInPortSockaddr]; /* Put the sockaddr_in for replies in the next bytes of the prefix region. If there is no reply address specified, fill it with zeros. */ if (addr) /* Do this memcpy instead of simply casting the pointer because some systems fail to do the cast correctly (due to alignment issues?) */ - memcpy ([data mutableBytes]+PREFIX_LENGTH_SIZE, addr, PREFIX_ADDRESS_SIZE); + memcpy ([data mutableBytes]+PREFIX_SP_OFF, addr, PREFIX_ADDRESS_SIZE); else - memset ([data mutableBytes]+PREFIX_LENGTH_SIZE, 0, PREFIX_ADDRESS_SIZE); + memset ([data mutableBytes]+PREFIX_SP_OFF, 0, PREFIX_ADDRESS_SIZE); + + addr = [rp _listeningSockaddr]; + /* Put the sockaddr_in for the destination in the next bytes of the prefix + region. If there is no destination address specified, fill with zeros. */ + if (addr) + /* Do this memcpy instead of simply casting the pointer because + some systems fail to do the cast correctly (due to alignment issues?) */ + memcpy ([data mutableBytes]+PREFIX_RP_OFF, addr, PREFIX_ADDRESS_SIZE); + else + memset ([data mutableBytes]+PREFIX_RP_OFF, 0, PREFIX_ADDRESS_SIZE); /* Write the packet on the socket. */ c = tryWrite (s, (int)timeout, (unsigned char*)[data bytes], prefix + eof_position);