diff --git a/ChangeLog b/ChangeLog index a91350594..0b05394f0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2000-07-04 Richard Frith-Macdonald + + * Source/GSConnection.m: Improved coder caching and added code for + multiple thread support. + * Source/GSPortCoder.m: Improved performance of class version + lookup and tidied. + * Source/GSTcpPort.m: Ensure we don't remain in the run loop any + longer than necessary. + * Source/NSArchiver.m: minor tidyup + * Source/NSDistantObject.m: minor performance tweak + * Source/NSUnarchiver.m: minor tidyup. + 2000-07-03 Richard Frith-Macdonald * Source/GSConnection.m: Cache coders fro performance. diff --git a/Headers/gnustep/base/GSConnection.h b/Headers/gnustep/base/GSConnection.h index bfb485d05..1c79f60a9 100644 --- a/Headers/gnustep/base/GSConnection.h +++ b/Headers/gnustep/base/GSConnection.h @@ -63,6 +63,7 @@ GS_EXPORT NSString *NSConnectionProxyCount; /* Objects received */ BOOL _independentQueueing; BOOL _authenticateIn; BOOL _authenticateOut; + BOOL _multipleThreads; NSPort *_receivePort; NSPort *_sendPort; unsigned _requestDepth; @@ -82,8 +83,8 @@ GS_EXPORT NSString *NSConnectionProxyCount; /* Objects received */ NSMutableArray *_requestQueue; id _delegate; NSRecursiveLock *_refGate; - NSPortCoder *_cachedDecoder; - NSPortCoder *_cachedEncoder; + NSMutableArray *_cachedDecoders; + NSMutableArray *_cachedEncoders; } + (NSArray*) allConnections; @@ -142,6 +143,9 @@ GS_EXPORT NSString *NSConnectionProxyCount; /* Objects received */ */ @interface NSConnection (GNUstepExtensions) ++ (NSConnection*) newRegisteringAtName: (NSString*)n + withRootObject: (id)anObject; + - (void) gcFinalize; - (retval_t) forwardForProxy: (NSDistantObject*)object diff --git a/Headers/gnustep/base/GSPortCoder.h b/Headers/gnustep/base/GSPortCoder.h index 15cb185d7..f21645bd4 100644 --- a/Headers/gnustep/base/GSPortCoder.h +++ b/Headers/gnustep/base/GSPortCoder.h @@ -72,7 +72,7 @@ #ifndef _IN_PORT_CODER_M #undef GSIArray #endif - NSMutableArray *_cInfo; /* Class version information. */ + NSMutableDictionary *_cInfo; /* Class version information. */ unsigned _cursor; /* Position in data buffer. */ unsigned _version; /* Version of archiver used. */ NSZone *_zone; /* Zone for allocating objs. */ diff --git a/Headers/gnustep/base/NSArchiver.h b/Headers/gnustep/base/NSArchiver.h index ea97958a7..080a32991 100644 --- a/Headers/gnustep/base/NSArchiver.h +++ b/Headers/gnustep/base/NSArchiver.h @@ -30,6 +30,7 @@ @interface NSArchiver : NSCoder { +@private NSMutableData *_data; /* Data to write into. */ id _dst; /* Serialization destination. */ IMP _serImp; /* Method to serialize with. */ @@ -137,6 +138,7 @@ @interface NSUnarchiver : NSCoder { +@private NSData *data; /* Data to write into. */ Class dataClass; /* What sort of data is it? */ id src; /* Deserialization source. */ diff --git a/Source/GSConnection.m b/Source/GSConnection.m index e1bda2ac2..bbe5f0ba6 100644 --- a/Source/GSConnection.m +++ b/Source/GSConnection.m @@ -56,6 +56,12 @@ #define M_LOCK(X) {NSDebugMLLog(@"GSConnection",@"Lock %@",X);[X lock];} #define M_UNLOCK(X) {NSDebugMLLog(@"GSConnection",@"Unlock %@",X);[X unlock];} +static Class connectionClass; +static Class dateClass; +static Class distantObjectClass; +static Class portCoderClass; +static Class runLoopClass; + static NSString* stringFromMsgType(int type) { @@ -121,14 +127,14 @@ stringFromMsgType(int type) unsigned target; id object; } -+ (GSLocalCounter*) newWithObject: (id)ob; ++ (id) newWithObject: (id)ob; @end @implementation GSLocalCounter static unsigned local_object_counter = 0; -+ (GSLocalCounter*) newWithObject: (id)obj ++ (id) newWithObject: (id)obj { GSLocalCounter *counter; @@ -141,7 +147,7 @@ static unsigned local_object_counter = 0; - (void) dealloc { RELEASE(object); - [super dealloc]; + NSDeallocateObject(self); } @end @@ -161,24 +167,25 @@ static unsigned local_object_counter = 0; } - (BOOL)countdown; - (id) obj; -+ (CachedLocalObject*) itemWithObject: (id)o time: (int)t; ++ (id) newWithObject: (id)o time: (int)t; @end @implementation CachedLocalObject -+ (CachedLocalObject*) itemWithObject: (id)o time: (int)t ++ (id) newWithObject: (id)o time: (int)t { - CachedLocalObject *item = [[self alloc] init]; + CachedLocalObject *item; + item = (CachedLocalObject*)NSAllocateObject(self, 0, NSDefaultMallocZone()); item->obj = RETAIN(o); item->time = t; - return AUTORELEASE(item); + return item; } - (void) dealloc { RELEASE(obj); - [super dealloc]; + NSDeallocateObject(self); } - (BOOL) countdown @@ -198,11 +205,12 @@ static unsigned local_object_counter = 0; @interface NSConnection (Private) -+ (void) setDebug: (int)val; - (void) handlePortMessage: (NSPortMessage*)msg; +- (void) _runInNewThread; ++ (void) setDebug: (int)val; -- _getReplyRmc: (int)n; - (void) _doneInRmc: (NSPortCoder*)c; +- (NSPortCoder*) _getReplyRmc: (int)sn; - (NSPortCoder*) _makeInRmc: (NSMutableArray*)components; - (NSPortCoder*) _makeOutRmc: (int)sequence; - (int) _newMsgNumber; @@ -300,14 +308,6 @@ static NSMapTable *all_connections_local_targets = NULL; static NSMapTable *all_connections_local_cached = NULL; static NSLock *global_proxies_gate; -/* rmc handling */ -static NSMutableArray *received_request_rmc_queue; -static NSLock *received_request_rmc_queue_gate; -static NSMutableArray *received_reply_rmc_queue; -static NSLock *received_reply_rmc_queue_gate; - -static int messages_received_count; - @@ -413,29 +413,33 @@ static int messages_received_count; + (void) initialize { - connection_table = - NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0); - connection_table_gate = [NSLock new]; - /* xxx When NSHashTable's are working, change this. */ - all_connections_local_objects = - NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, - NSObjectMapValueCallBacks, 0); - all_connections_local_targets = - NSCreateMapTable(NSIntMapKeyCallBacks, - NSNonOwnedPointerMapValueCallBacks, 0); - all_connections_local_cached = - NSCreateMapTable(NSIntMapKeyCallBacks, - NSObjectMapValueCallBacks, 0); - global_proxies_gate = [NSLock new]; - received_request_rmc_queue = [[NSMutableArray alloc] initWithCapacity: 32]; - received_request_rmc_queue_gate = [NSLock new]; - received_reply_rmc_queue = [[NSMutableArray alloc] initWithCapacity: 32]; - received_reply_rmc_queue_gate = [NSLock new]; - root_object_map = - NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, - NSObjectMapValueCallBacks, 0); - root_object_map_gate = [NSLock new]; - messages_received_count = 0; + if (self == [NSConnection class]) + { + connectionClass = self; + dateClass = [NSDate class]; + distantObjectClass = [NSDistantObject class]; + portCoderClass = [NSPortCoder class]; + runLoopClass = [NSRunLoop class]; + + connection_table = + NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0); + connection_table_gate = [NSLock new]; + /* xxx When NSHashTable's are working, change this. */ + all_connections_local_objects = + NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, + NSObjectMapValueCallBacks, 0); + all_connections_local_targets = + NSCreateMapTable(NSIntMapKeyCallBacks, + NSNonOwnedPointerMapValueCallBacks, 0); + all_connections_local_cached = + NSCreateMapTable(NSIntMapKeyCallBacks, + NSObjectMapValueCallBacks, 0); + global_proxies_gate = [NSLock new]; + root_object_map = + NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, + NSObjectMapValueCallBacks, 0); + root_object_map_gate = [NSLock new]; + } } + (id) new @@ -558,7 +562,7 @@ static int messages_received_count; - (void) enableMultipleThreads { - [self notImplemented: _cmd]; + _multipleThreads = YES; } - (BOOL) independentConversationQueueing @@ -573,7 +577,7 @@ static int messages_received_count; * -init returns the default connection. */ RELEASE(self); - return RETAIN([NSConnection defaultConnection]); + return RETAIN([connectionClass defaultConnection]); } /* This is the designated initializer for NSConnection */ @@ -649,6 +653,12 @@ static int messages_received_count; _repInCount = 0; _reqInCount = 0; + /* + * These arrays cache NSPortCoder objects + */ + _cachedDecoders = [NSMutableArray new]; + _cachedEncoders = [NSMutableArray new]; + /* * This is used to queue up incoming NSPortMessages representing requests * that can't immediately be dealt with. @@ -710,7 +720,7 @@ static int messages_received_count; * Set up request modes array and make sure the receiving port is * added to the run loop to get data. */ - loop = [NSRunLoop currentRunLoop]; + loop = [runLoopClass currentRunLoop]; _runLoops = [[NSMutableArray alloc] initWithObjects: &loop count: 1]; _requestModes = [[NSMutableArray alloc] initWithCapacity: 2]; [self addRequestMode: NSDefaultRunLoopMode]; @@ -871,8 +881,7 @@ static int messages_received_count; - (BOOL) multipleThreadsEnabled { - [self notImplemented: _cmd]; - return NO; + return _multipleThreads; } - (NSPort*) receivePort @@ -988,7 +997,7 @@ static int messages_received_count; - (NSArray*) requestModes { - return [[_requestModes copy] autorelease]; + return AUTORELEASE([_requestModes copy]); } - (NSTimeInterval) requestTimeout @@ -1023,7 +1032,10 @@ static int messages_received_count; - (void) runInNewThread { - [self notImplemented: _cmd]; + [self removeRunLoop: [runLoopClass currentRunLoop]]; + [NSThread detachNewThreadSelector: @selector(_runInNewThread) + toTarget: self + withObject: nil]; } - (NSPort*) sendPort @@ -1106,10 +1118,6 @@ static int messages_received_count; [d setObject: o forKey: NSConnectionLocalCount]; o = [NSNumber numberWithUnsignedInt: NSCountMapTable(_remoteProxies)]; [d setObject: o forKey: NSConnectionProxyCount]; - M_LOCK(received_request_rmc_queue_gate); - o = [NSNumber numberWithUnsignedInt: [received_request_rmc_queue count]]; - M_UNLOCK(received_request_rmc_queue_gate); - [d setObject: o forKey: @"Pending packets"]; M_UNLOCK(_refGate); @@ -1122,6 +1130,21 @@ static int messages_received_count; @implementation NSConnection (GNUstepExtensions) ++ (NSConnection*) newRegisteringAtName: (NSString*)name + withRootObject: (id)anObject +{ + NSConnection *conn; + + conn = [[self alloc] initWithReceivePort: [NSPort port] + sendPort: nil]; + [conn setRootObject: anObject]; + if ([conn registerName: name] == NO) + { + DESTROY(conn); + } + return conn; +} + - (void) gcFinalize { CREATE_AUTORELEASE_POOL(arp); @@ -1190,8 +1213,8 @@ static int messages_received_count; _replyMap = 0; } - DESTROY(_cachedDecoder); - DESTROY(_cachedEncoder); + DESTROY(_cachedDecoders); + DESTROY(_cachedEncoders); DESTROY(_refGate); RELEASE(arp); @@ -1368,19 +1391,19 @@ static int messages_received_count; /* Class-wide stats and collections. */ -+ (int) messagesReceived -{ - return messages_received_count; -} - + (unsigned) connectionsCount { - return NSCountHashTable(connection_table); + unsigned result; + + M_LOCK(connection_table_gate); + result = NSCountHashTable(connection_table); + M_UNLOCK(connection_table_gate); + return result; } + (unsigned) connectionsCountWithInPort: (NSPort*)aPort { - unsigned count = 0; + unsigned count = 0; NSHashEnumerator enumerator; NSConnection *o; @@ -1419,7 +1442,7 @@ static int messages_received_count; { NSLog(@"handling packet of type %d (%@)", type, stringFromMsgType(type)); } - conn = [NSConnection connectionWithReceivePort: rp sendPort: sp]; + conn = [connectionClass connectionWithReceivePort: rp sendPort: sp]; if (conn == nil) { NSLog(@"received port message for unknown connection - %@", msg); @@ -1542,6 +1565,14 @@ static int messages_received_count; } } +- (void) _runInNewThread +{ + NSRunLoop *loop = [runLoopClass currentRunLoop]; + + [self addRunLoop: loop]; + [loop run]; +} + + (void) setDebug: (int)val { debug_connection = val; @@ -1767,8 +1798,8 @@ static int messages_received_count; } else { - [NSDistantObject proxyWithLocal: counter->object - connection: self]; + [distantObjectClass proxyWithLocal: counter->object + connection: self]; [op encodeObject: nil]; if (debug_connection > 3) NSLog(@"retained object (0x%x) target (0x%x) on connection(0x%x)", @@ -1843,17 +1874,6 @@ static int messages_received_count; } -/* Running the connection, getting/sending requests/replies. */ - -- (void) runConnectionUntilDate: date -{ - [NSRunLoop runUntilDate: date]; -} - -- (void) runConnection -{ - [self runConnectionUntilDate: [NSDate distantFuture]]; -} /* * Check the queue, then try to get it from the network by waiting @@ -1870,11 +1890,12 @@ static int messages_received_count; { if (timeout_date == nil) { - timeout_date = [NSDate dateWithTimeIntervalSinceNow: _replyTimeout]; + timeout_date + = [dateClass dateWithTimeIntervalSinceNow: _replyTimeout]; } M_UNLOCK(_queueGate); - if ([NSRunLoop runOnceBeforeDate: timeout_date - forMode: NSConnectionReplyMode] == NO) + if ([runLoopClass runOnceBeforeDate: timeout_date + forMode: NSConnectionReplyMode] == NO) { [NSException raise: NSPortTimeoutException format: @"timed out waiting for reply"]; @@ -1904,33 +1925,30 @@ static int messages_received_count; - (void) _doneInRmc: (NSPortCoder*)c { M_LOCK(_refGate); - if (_cachedDecoder == nil) - { - _cachedDecoder = c; - [c dispatch]; /* make coder release connection */ - } - else - { - RELEASE(c); - } + [_cachedDecoders addObject: c]; + [c dispatch]; /* Tell NSPortCoder to release the connection. */ + RELEASE(c); M_UNLOCK(_refGate); } - (NSPortCoder*) _makeInRmc: (NSMutableArray*)components { - NSPortCoder *coder; + NSPortCoder *coder; + unsigned count; NSParameterAssert(_isValid); M_LOCK(_refGate); - if (_cachedDecoder == nil) + count = [_cachedDecoders count]; + if (count > 0) { - coder = [NSPortCoder allocWithZone: NSDefaultMallocZone()]; + coder = [_cachedDecoders objectAtIndex: --count]; + RETAIN(coder); + [_cachedDecoders removeObjectAtIndex: count]; } else { - coder = _cachedDecoder; - _cachedDecoder = nil; + coder = [portCoderClass allocWithZone: NSDefaultMallocZone()]; } M_UNLOCK(_refGate); @@ -1942,19 +1960,22 @@ static int messages_received_count; - (NSPortCoder*) _makeOutRmc: (int)sequence { - NSPortCoder *coder; + NSPortCoder *coder; + unsigned count; NSParameterAssert(_isValid); M_LOCK(_refGate); - if (_cachedEncoder == nil) + count = [_cachedEncoders count]; + if (count > 0) { - coder = [NSPortCoder allocWithZone: NSDefaultMallocZone()]; + coder = [_cachedEncoders objectAtIndex: --count]; + RETAIN(coder); + [_cachedEncoders removeObjectAtIndex: count]; } else { - coder = _cachedEncoder; - _cachedEncoder = nil; + coder = [portCoderClass allocWithZone: NSDefaultMallocZone()]; } M_UNLOCK(_refGate); @@ -1970,6 +1991,7 @@ static int messages_received_count; NSDate *limit; BOOL sent = NO; BOOL raiseException = NO; + BOOL needsReply = NO; NSMutableArray *components = [c _components]; if (_authenticateOut == YES) @@ -1985,7 +2007,30 @@ static int messages_received_count; } [components addObject: d]; } - limit = [NSDate dateWithTimeIntervalSinceNow: [self requestTimeout]]; + + switch (msgid) + { + case PROXY_RETAIN: + needsReply = YES; + case CONNECTION_SHUTDOWN: + case METHOD_REPLY: + case ROOTPROXY_REPLY: + case METHODTYPE_REPLY: + case PROXY_RELEASE: + case RETAIN_REPLY: + raiseException = NO; + break; + + case METHOD_REQUEST: + case ROOTPROXY_REQUEST: + case METHODTYPE_REQUEST: + default: + raiseException = YES; + needsReply = YES; + break; + } + + limit = [dateClass dateWithTimeIntervalSinceNow: _requestTimeout]; sent = [_sendPort sendBeforeDate: limit msgid: msgid components: components @@ -1993,41 +2038,35 @@ static int messages_received_count; reserved: [_sendPort reservedSpaceLength]]; M_LOCK(_refGate); - if (_cachedEncoder == nil) + /* + * If we have sent out a request on a run loop that we don't already + * know about, it must be on a new thread - so if we have multipleThreads + * enabled, we must add the run loop of the new thread so that we can + * get the reply in this thread. + */ + if (_multipleThreads == YES && needsReply == YES) { - _cachedEncoder = c; - [c dispatch]; /* make coder release connection */ - } - else - { - RELEASE(c); + NSRunLoop *loop = [runLoopClass currentRunLoop]; + + if ([_runLoops indexOfObjectIdenticalTo: loop] == NSNotFound) + { + [self addRunLoop: loop]; + } } + + /* + * We replace the code we have just used in the cache, and tell it not to + * retain this connection any more. + */ + [_cachedEncoders addObject: c]; + [c dispatch]; /* Tell NSPortCoder to release the connection. */ + RELEASE(c); M_UNLOCK(_refGate); if (sent == NO) { - NSString *text; + NSString *text = stringFromMsgType(msgid); - switch (msgid) - { - case CONNECTION_SHUTDOWN: - case METHOD_REPLY: - case ROOTPROXY_REPLY: - case METHODTYPE_REPLY: - case PROXY_RELEASE: - case PROXY_RETAIN: - case RETAIN_REPLY: - raiseException = YES; - break; - - case METHOD_REQUEST: - case ROOTPROXY_REQUEST: - case METHODTYPE_REQUEST: - default: - raiseException = YES; - break; - } - text = stringFromMsgType(msgid); if (raiseException == YES) { [NSException raise: NSPortTimeoutException format: text]; @@ -2085,7 +2124,7 @@ static int messages_received_count; target = counter->target; NSMapInsert(all_connections_local_objects, (void*)object, counter); NSMapInsert(all_connections_local_targets, (void*)target, counter); - [counter release]; + RELEASE(counter); } [anObj setProxyTarget: target]; NSMapInsert(_localTargets, (void*)target, anObj); @@ -2156,13 +2195,14 @@ static int messages_received_count; if (timer == nil) { timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 - target: [NSConnection class] - selector: @selector(_timeout: ) + target: connectionClass + selector: @selector(_timeout:) userInfo: nil repeats: YES]; } - item = [CachedLocalObject itemWithObject: counter time: 30]; + item = [CachedLocalObject newWithObject: counter time: 30]; NSMapInsert(all_connections_local_cached, (void*)target, item); + RELEASE(item); if (debug_connection > 3) NSLog(@"placed local object (0x%x) target (0x%x) in cache", (gsaddr)anObj, target); @@ -2289,7 +2329,7 @@ static int messages_received_count; unsigned target = (unsigned int)[aProxy targetForProxy]; NSParameterAssert (_isValid); - NSParameterAssert(aProxy->isa == [NSDistantObject class]); + NSParameterAssert(aProxy->isa == distantObjectClass); NSParameterAssert([aProxy connectionForProxy] == self); M_LOCK(_proxiesGate); if (NSMapGet(_remoteProxies, (void*)target)) diff --git a/Source/GSPortCoder.m b/Source/GSPortCoder.m index e61579b4a..3c8ddb9df 100644 --- a/Source/GSPortCoder.m +++ b/Source/GSPortCoder.m @@ -278,7 +278,7 @@ typeCheck(char t1, char t2) -@interface NSPortCoder (Private) +@interface NSPortCoder (Headers) - (void) _deserializeHeaderAt: (unsigned*)pos version: (unsigned*)v classes: (unsigned*)c @@ -294,6 +294,22 @@ typeCheck(char t1, char t2) @implementation NSPortCoder +static Class connectionClass; +static Class mutableArrayClass; +static Class mutableDataClass; +static Class mutableDictionaryClass; + ++ (void) initialize +{ + if (self == [NSPortCoder class]) + { + connectionClass = [NSConnection class]; + mutableArrayClass = [NSMutableArray class]; + mutableDataClass = [NSMutableData class]; + mutableDictionaryClass = [NSMutableDictionary class]; + } +} + + (NSPortCoder*) portCoderWithReceivePort: (NSPort*)recv sendPort: (NSPort*)send components: (NSArray*)comp; @@ -326,6 +342,12 @@ typeCheck(char t1, char t2) } if (_clsAry != 0) { + unsigned count = GSIArrayCount(_clsAry); + + while (count-- > 0) + { + RELEASE(GSIArrayItemAtIndex(_clsAry, count).obj); + } GSIArrayClear(_clsAry); GSIArrayClear(_objAry); GSIArrayClear(_ptrAry); @@ -422,7 +444,7 @@ typeCheck(char t1, char t2) } else if (pos == -2) { - return [NSData data]; + return [mutableDataClass data]; } else { @@ -646,8 +668,6 @@ typeCheck(char t1, char t2) format: @"decoded nil class"]; } classInfo = [GSClassInfo newWithClass: c andVersion: cver]; - [_cInfo addObject: classInfo]; - RELEASE(classInfo); GSIArrayAddItem(_clsAry, (GSIArrayItem)classInfo); *(Class*)address = classInfo->class; /* @@ -925,7 +945,7 @@ typeCheck(char t1, char t2) #if GS_HAVE_I64 bigval = val; #else - val = GSSwapBigI64ToHost(val); + bigval = GSSwapBigI64ToHost(val); #endif break; } @@ -1232,7 +1252,7 @@ typeCheck(char t1, char t2) (*_eValImp)(self, eValSel, @encode(Class), &cls); [obj encodeWithCoder: self]; } - else if (!_initialPass) + else { (*_xRefImp)(_dst, xRefSel, _GSC_ID | _GSC_XREF, node->value.uint); } @@ -1592,7 +1612,8 @@ typeCheck(char t1, char t2) case _C_ULNG_LNG: (*_eTagImp)(_dst, eTagSel, _GSC_ULNG_LNG | _GSC_S_LNG_LNG); - (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(unsigned long long), nil); + (*_eSerImp)(_dst, eSerSel, (void*)buf, @encode(unsigned long long), + nil); return; case _C_FLT: @@ -1621,8 +1642,8 @@ typeCheck(char t1, char t2) { BOOL firstTime; - _conn = RETAIN([NSConnection connectionWithReceivePort: recv - sendPort: send]); + _conn = RETAIN([connectionClass connectionWithReceivePort: recv + sendPort: send]); if (_comp == nil) { firstTime = YES; @@ -1657,8 +1678,8 @@ typeCheck(char t1, char t2) * the start for use by the port when the encoded data is sent. * Make the data item the first component of the array. */ - _comp = [NSMutableArray new]; - _dst = [_fastCls._NSMutableDataMalloc allocWithZone: _zone]; + _comp = [mutableArrayClass new]; + _dst = [mutableDataClass allocWithZone: _zone]; _dst = [_dst initWithLength: _cursor]; [_comp addObject: _dst]; RELEASE(_dst); @@ -1744,12 +1765,7 @@ typeCheck(char t1, char t2) * _cInfo is a dictionary of objects for keeping track of the * version numbers that the classes were encoded with. */ - if (firstTime == YES) - { - _cInfo - = [[NSMutableArray allocWithZone: _zone] initWithCapacity: 16]; - } - else + if (firstTime == NO) { [_cInfo removeAllObjects]; } @@ -1778,12 +1794,18 @@ typeCheck(char t1, char t2) } else { + unsigned count = GSIArrayCount(_clsAry); + + while (count-- > 0) + { + RELEASE(GSIArrayItemAtIndex(_clsAry, count).obj); + } GSIArrayRemoveAllItems(_clsAry); GSIArrayRemoveAllItems(_objAry); GSIArrayRemoveAllItems(_ptrAry); } - GSIArrayAddItem(_clsAry, (GSIArrayItem)0); - GSIArrayAddItem(_objAry, (GSIArrayItem)0); + GSIArrayAddItem(_clsAry, (GSIArrayItem)nil); + GSIArrayAddItem(_objAry, (GSIArrayItem)nil); GSIArrayAddItem(_ptrAry, (GSIArrayItem)0); } NS_HANDLER @@ -1824,18 +1846,31 @@ typeCheck(char t1, char t2) - (unsigned) versionForClassName: (NSString*)className { - GSClassInfo *info; + GSClassInfo *info = nil; unsigned version = NSNotFound; - unsigned count = [_cInfo count]; + unsigned count = GSIArrayCount(_clsAry); - while (--count > 0) + /* + * Lazy ... we construct a dictionary of all the class information in + * the request the first time that class version info is asked for. + */ + if (_cInfo == nil) { - info = [_cInfo objectAtIndex: count]; - if ([[info className] isEqual: className] == YES) + _cInfo = [[mutableDictionaryClass alloc] initWithCapacity: count]; + } + if ([_cInfo count] == 0) + { + while (count-- > 0) { - version = info->version; + info = GSIArrayItemAtIndex(_clsAry, count).obj; + [_cInfo setObject: info forKey: NSStringFromClass(info->class)]; } } + info = [_cInfo objectForKey: className]; + if (info != nil) + { + version = info->version; + } return version; } @@ -1850,6 +1885,10 @@ typeCheck(char t1, char t2) return _comp; } +@end + +@implementation NSPortCoder (Headers) + - (void) _deserializeHeaderAt: (unsigned*)pos version: (unsigned*)v classes: (unsigned*)c diff --git a/Source/GSTcpPort.m b/Source/GSTcpPort.m index 9bbce5aef..fc03b9576 100644 --- a/Source/GSTcpPort.m +++ b/Source/GSTcpPort.m @@ -51,8 +51,13 @@ extern int errno; +#if 0 #define DO_LOCK(X) {NSDebugMLLog(@"GSTcpHandle",@"lock %@",X); [X lock];} #define DO_UNLOCK(X) {NSDebugMLLog(@"GSTcpHandle",@"unlock %@",X); [X unlock];} +#else +#define DO_LOCK(X) {[X lock];} +#define DO_UNLOCK(X) {[X unlock];} +#endif #define GS_CONNECTION_MSG 0 #define NETBLOCK 8192 @@ -305,6 +310,11 @@ newDataWithEncodedPort(GSTcpPort *port) @implementation GSTcpHandle +static Class mutableArrayClass; +static Class mutableDataClass; +static Class portMessageClass; +static Class runLoopClass; + + (id) allocWithZone: (NSZone*)zone { [NSException raise: NSGenericException @@ -344,6 +354,17 @@ newDataWithEncodedPort(GSTcpPort *port) return AUTORELEASE(handle); } ++ (void) initialize +{ + if (self == [GSTcpHandle class]) + { + mutableArrayClass = [NSMutableArray class]; + mutableDataClass = [NSMutableData class]; + portMessageClass = [NSPortMessage class]; + runLoopClass = [NSRunLoop class]; + } +} + - (BOOL) connectToPort: (GSTcpPort*)aPort beforeDate: (NSDate*)when { NSArray *addrs; @@ -428,7 +449,7 @@ newDataWithEncodedPort(GSTcpPort *port) type: ET_WDESC watcher: self forMode: NSDefaultRunLoopMode]; - while (state == GS_H_TRYCON && [when timeIntervalSinceNow] > 0) + while (state == GS_H_TRYCON && [when timeIntervalSinceNow] > 0) { [l runMode: NSDefaultRunLoopMode beforeDate: when]; } @@ -487,15 +508,16 @@ newDataWithEncodedPort(GSTcpPort *port) - (void) dispatch { NSPortMessage *pm; + GSTcpPort *rp = [self recvPort]; - pm = [NSPortMessage allocWithZone: NSDefaultMallocZone()]; + pm = [portMessageClass allocWithZone: NSDefaultMallocZone()]; pm = [pm initWithSendPort: [self sendPort] - receivePort: [self recvPort] + receivePort: rp components: rItems]; [pm setMsgid: rId]; rId = 0; DESTROY(rItems); - [[self recvPort] handlePortMessage: pm]; + [rp handlePortMessage: pm]; RELEASE(pm); } @@ -536,15 +558,15 @@ newDataWithEncodedPort(GSTcpPort *port) extra: (void*)extra forMode: (NSString*)mode { - NSDebugMLLog(@"GSTcpHandle", @"received %s event", - type == ET_RPORT ? "read" : "write"); + NSDebugMLLog(@"GSTcpHandle", @"received %s event on 0x%x", + type == ET_RPORT ? "read" : "write", self); /* * If we have been invalidated (desc < 0) then we should ignore this * event and remove ourself from the runloop. */ if (desc < 0) { - NSRunLoop *l = [NSRunLoop currentRunLoop]; + NSRunLoop *l = [runLoopClass currentRunLoop]; [l removeEvent: data type: ET_WDESC @@ -565,7 +587,7 @@ newDataWithEncodedPort(GSTcpPort *port) */ if (rData == nil) { - rData = [[NSMutableData alloc] initWithLength: NETBLOCK]; + rData = [[mutableDataClass alloc] initWithLength: NETBLOCK]; rWant = sizeof(GSPortItemHeader); rLength = 0; want = NETBLOCK; @@ -648,7 +670,7 @@ newDataWithEncodedPort(GSTcpPort *port) memcpy(bytes, bytes + rWant, rLength); } rWant = sizeof(GSPortItemHeader); - d = [NSData new]; + d = [mutableDataClass new]; [rItems addObject: d]; RELEASE(d); if (nItems == [rItems count]) @@ -686,7 +708,9 @@ newDataWithEncodedPort(GSTcpPort *port) rId = GSSwapBigI32ToHost(h->mId); nItems = GSSwapBigI32ToHost(h->nItems); NSAssert(nItems >0, NSInternalInconsistencyException); - rItems = [[NSMutableArray alloc] initWithCapacity: nItems]; + rItems + = [mutableArrayClass allocWithZone: NSDefaultMallocZone()]; + rItems = [rItems initWithCapacity: nItems]; if (rWant > sizeof(GSPortMsgHeader)) { NSData *d; @@ -696,7 +720,7 @@ newDataWithEncodedPort(GSTcpPort *port) * the header - so add it to the rItems array. */ rWant -= sizeof(GSPortMsgHeader); - d = [NSData alloc]; + d = [mutableDataClass alloc]; d = [d initWithBytes: bytes + sizeof(GSPortMsgHeader) length: rWant]; [rItems addObject: d]; @@ -733,7 +757,8 @@ newDataWithEncodedPort(GSTcpPort *port) NSData *d; rType = GSP_NONE; - d = [[NSData alloc] initWithBytes: bytes length: rWant]; + d = [mutableDataClass allocWithZone: NSDefaultMallocZone()]; + d = [d initWithBytes: bytes length: rWant]; [rItems addObject: d]; RELEASE(d); rLength -= rWant; @@ -838,7 +863,7 @@ newDataWithEncodedPort(GSTcpPort *port) } else { - NSLog(@"No messages to write."); + NSLog(@"No messages to write on 0x%x.", self); return; } } @@ -856,7 +881,8 @@ newDataWithEncodedPort(GSTcpPort *port) } else { - NSDebugMLLog(@"GSTcpHandle", @"wrote %d bytes", res); + NSDebugMLLog(@"GSTcpHandle", @"wrote %d bytes on 0x%x", + res, self); wLength += res; if (wLength == l) { @@ -877,12 +903,21 @@ newDataWithEncodedPort(GSTcpPort *port) } else { + NSRunLoop *l = [runLoopClass currentRunLoop]; + /* * message completed - remove from list. */ + NSDebugMLLog(@"GSTcpHandle", @"completed 0x%x on 0x%x", + components, self); wData = nil; wItem = 0; [wMsgs removeObjectAtIndex: 0]; + + [l removeEvent: data + type: ET_WDESC + forMode: mode + all: NO]; } } } @@ -896,10 +931,11 @@ newDataWithEncodedPort(GSTcpPort *port) BOOL sent = NO; NSAssert([components count] > 0, NSInternalInconsistencyException); - NSDebugMLLog(@"GSTcpHandle", @"Sending message before %@", when); + NSDebugMLLog(@"GSTcpHandle", @"Sending message 0x%x on 0x%x(%d) before %@", + components, self, desc, when); [wMsgs addObject: components]; - l = [NSRunLoop currentRunLoop]; + l = [runLoopClass currentRunLoop]; RETAIN(self); @@ -912,16 +948,17 @@ newDataWithEncodedPort(GSTcpPort *port) { [l runMode: NSDefaultRunLoopMode beforeDate: when]; } - [l removeEvent: (void*)(gsaddr)desc - type: ET_WDESC - forMode: NSDefaultRunLoopMode - all: NO]; + /* + * NB. We will remove ourself from the run loop when the message send + * is completed, so we don't need to do it here. + */ if ([wMsgs indexOfObjectIdenticalTo: components] == NSNotFound) { sent = YES; } RELEASE(self); - NSDebugMLLog(@"GSTcpHandle", @"Message send %d", sent); + NSDebugMLLog(@"GSTcpHandle", @"Message send 0x%x on 0x%x status %d", + components, self, sent); return sent; } @@ -1575,7 +1612,7 @@ static NSMapTable *tcpPortMap = 0; */ if (length == 0 && rl != 0) { - header = [[NSMutableData alloc] initWithCapacity: NETBLOCK]; + header = [[mutableDataClass alloc] initWithCapacity: NETBLOCK]; [header setLength: rl]; [components insertObject: header atIndex: 0]; diff --git a/Source/NSArchiver.m b/Source/NSArchiver.m index 4115912d1..c1e3589ae 100644 --- a/Source/NSArchiver.m +++ b/Source/NSArchiver.m @@ -359,7 +359,8 @@ static SEL eValSel = @selector(encodeValueOfObjCType:at:); /* * Second pass, write a cross-reference number. */ - (*_xRefImp)(_dst, xRefSel, _GSC_PTR|_GSC_XREF, node->value.uint); + (*_xRefImp)(_dst, xRefSel, _GSC_PTR|_GSC_XREF, + node->value.uint); } } return; @@ -425,8 +426,8 @@ static SEL eValSel = @selector(encodeValueOfObjCType:at:); * of its subclasses are decoded and call * [super initWithCoder:ccc] */ - if (s == c || s == 0 || - GSIMapNodeForKey(_clsMap, (GSIMapKey)(void*)s) != 0) + if (s == c || s == 0 + || GSIMapNodeForKey(_clsMap, (GSIMapKey)(void*)s) != 0) { done = YES; } @@ -458,7 +459,7 @@ static SEL eValSel = @selector(encodeValueOfObjCType:at:); if (node == 0) { node = GSIMapAddPair(_ptrMap, - (GSIMapKey)(void*)s, (GSIMapVal)++_xRefP); + (GSIMapKey)(void*)s, (GSIMapVal)++_xRefP); (*_xRefImp)(_dst, xRefSel, _GSC_SEL, node->value.uint); /* * Encode selector. @@ -467,7 +468,8 @@ static SEL eValSel = @selector(encodeValueOfObjCType:at:); } else { - (*_xRefImp)(_dst, xRefSel, _GSC_SEL|_GSC_XREF, node->value.uint); + (*_xRefImp)(_dst, xRefSel, _GSC_SEL|_GSC_XREF, + node->value.uint); } } return; @@ -784,7 +786,7 @@ static SEL eValSel = @selector(encodeValueOfObjCType:at:); (*_eValImp)(self, eValSel, @encode(Class), &cls); [obj encodeWithCoder: self]; } - else if(!_initialPass) + else { (*_xRefImp)(_dst, xRefSel, _GSC_ID | _GSC_XREF, node->value.uint); } diff --git a/Source/NSDistantObject.m b/Source/NSDistantObject.m index 4c7b2da52..7aee7f6f1 100644 --- a/Source/NSDistantObject.m +++ b/Source/NSDistantObject.m @@ -34,6 +34,20 @@ static int debug_proxy = 0; static Class placeHolder = 0; static Class distantObjectClass = 0; +typedef struct { + @defs(NSDistantObject) +} NSDO; + +/* This is the proxy tag; it indicates where the local object is, + and determines whether the reply port to the Connection-where-the- + proxy-is-local needs to encoded/decoded or not. */ +enum +{ + PROXY_LOCAL_FOR_RECEIVER = 0, + PROXY_LOCAL_FOR_SENDER, + PROXY_REMOTE_FOR_BOTH +}; + /* @@ -77,11 +91,172 @@ static Class distantObjectClass = 0; + (id) initWithCoder: (NSCoder*)aCoder { - NSDistantObject *proxy; + gsu8 proxy_tag; + unsigned target; + id decoder_connection; - proxy = (NSDistantObject*)NSAllocateObject(distantObjectClass, - 0, NSDefaultMallocZone()); - return [proxy initWithCoder: aCoder]; +/* + if ([aCoder isKindOfClass: [NSPortCoder class]] == NO) + { + [NSException raise: NSGenericException + format: @"NSDistantObject objects only decode with " + @"NSPortCoder class"]; + } +*/ + + decoder_connection = [(NSPortCoder*)aCoder connection]; + NSAssert(decoder_connection, NSInternalInconsistencyException); + + /* First get the tag, so we know what values need to be decoded. */ + [aCoder decodeValueOfObjCType: @encode(typeof(proxy_tag)) + at: &proxy_tag]; + + 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. + */ + [aCoder decodeValueOfObjCType: @encode(typeof(target)) + at: &target]; + + if (debug_proxy) + NSLog(@"Receiving a proxy for local object 0x%x " + @"connection 0x%x\n", target, (gsaddr)decoder_connection); + + if (![[decoder_connection class] includesLocalTarget: target]) + { + [NSException raise: @"ProxyDecodedBadTarget" + format: @"No local object with given target (0x%x)", + target]; + } + else + { + NSDistantObject *o; + + o = [decoder_connection includesLocalTarget: target]; + if (debug_proxy) + { + NSLog(@"Local object is 0x%x (0x%x)\n", + (gsaddr)o, (gsaddr)o ? ((NSDO*)o)->_object : 0); + } + return o ? RETAIN(((NSDO*)o)->_object) : nil; + } + + 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. + */ + [aCoder decodeValueOfObjCType: @encode(typeof(target)) + at: &target]; + if (debug_proxy) + NSLog(@"Receiving a proxy, was local 0x%x connection 0x%x\n", + target, (gsaddr)decoder_connection); + return [self initWithTarget: target + connection: decoder_connection]; + + 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. + */ + { + 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 decodeValueOfObjCType: @encode(typeof(intermediary)) + at: &intermediary]; + AUTORELEASE([self initWithTarget: 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 decodeValueOfObjCType: @encode(typeof(target)) + at: &target]; + + [aCoder decodeValueOfObjCType: @encode(id) + at: &proxy_connection_out_port]; + + NSAssert(proxy_connection_out_port, NSInternalInconsistencyException); + /* + # 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] + connectionWithReceivePort: [decoder_connection receivePort] + sendPort: proxy_connection_out_port]; + + if (debug_proxy) + NSLog(@"Receiving a triangle-connection proxy 0x%x " + @"connection 0x%x\n", target, (gsaddr)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]; + + /* + * Finally - we get 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 [self initWithTarget: target + connection: proxy_connection]; + } + + default: + /* xxx This should be something different than NSGenericException. */ + [NSException raise: NSGenericException + format: @"Bad proxy tag"]; + } + /* Not reached. */ + return nil; } + (id) initWithLocal: (id)anObject connection: (NSConnection*)aConnection @@ -138,16 +313,6 @@ static Class distantObjectClass = 0; @implementation NSDistantObject -/* This is the proxy tag; it indicates where the local object is, - and determines whether the reply port to the Connection-where-the- - proxy-is-local needs to encoded/decoded or not. */ -enum -{ - PROXY_LOCAL_FOR_RECEIVER = 0, - PROXY_LOCAL_FOR_SENDER, - PROXY_REMOTE_FOR_BOTH -}; - + (void) initialize { if (self == [NSDistantObject class]) diff --git a/Source/NSUnarchiver.m b/Source/NSUnarchiver.m index 78cf80443..f081fa799 100644 --- a/Source/NSUnarchiver.m +++ b/Source/NSUnarchiver.m @@ -881,7 +881,7 @@ mapClassName(NSUnarchiverObjectInfo *info) #if GS_HAVE_I64 bigval = val; #else - val = GSSwapBigI64ToHost(val); + bigval = GSSwapBigI64ToHost(val); #endif break; }