diff --git a/ChangeLog b/ChangeLog index 7207115e1..1103c9434 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +Mon Oct 27 13:37:00 2003 Richard Frith-Macdonald + + * Source/NSConnection.m: Rewrite/simplify methods dealing with DO + retain/release and handling interaction with NSDistantObject. + Attempts to make retain/release simpler and more reliable by + (among other things) counting the nujmber of times an object is + vended by reference. Much care taken to try to ensure that + backward compatibility is not broken ... hope it is ok. + * Source/NSDistantObject.m: Modified to use slightly simpler + interaction with NSConnecxtion, to include count of times an + object is vended by reference and to ensure thread safety. + * Testing/nsconnection_client.m: Make the -d option produce detailed + debug output. + * Testing/nsconnection_server.m: ditto. + Mon Oct 27 13:24:00 2003 Richard Frith-Macdonald * Source/GNUmakefile: build new lock classes diff --git a/Source/NSConnection.m b/Source/NSConnection.m index 805f95dcb..2b440131b 100644 --- a/Source/NSConnection.m +++ b/Source/NSConnection.m @@ -195,8 +195,7 @@ stringFromMsgType(int type) + (void) setDebug: (int)val; - (void) addLocalObject: (NSDistantObject*)anObj; -- (NSDistantObject*) localForObject: (id)object; -- (void) removeLocalObject: (id)anObj; +- (void) removeLocalObject: (NSDistantObject*)anObj; - (void) _doneInReply: (NSPortCoder*)c; - (void) _doneInRmc: (NSPortCoder*)c; @@ -1107,9 +1106,7 @@ static BOOL multi_threaded = NO; } while (i-- > 0) { - id t = ((ProxyStruct*)[targets objectAtIndex: i])->_object; - - [self removeLocalObject: t]; + [self removeLocalObject: [targets objectAtIndex: i]]; } RELEASE(targets); GSIMapEmptyMap(_localTargets); @@ -2533,9 +2530,17 @@ static void callEncoder (DOContext *ctxt) if (prox != nil) { if (debug_connection > 3) - NSLog(@"releasing object with target (0x%x) on (0x%x)", - target, (gsaddr)self); - [self removeLocalObject: ((ProxyStruct*)prox)->_object]; + NSLog(@"releasing object with target (0x%x) on (0x%x) counter %d", + target, (gsaddr)self, ((ProxyStruct*)prox)->_counter); +#if 1 + // FIXME thread safety + if (--(((ProxyStruct*)prox)->_counter) == 0) + { + [self removeLocalObject: prox]; + } +#else + [self removeLocalObject: prox]; +#endif } else if (debug_connection > 3) NSLog(@"releasing object with target (0x%x) on (0x%x) - nothing to do", @@ -2546,9 +2551,11 @@ static void callEncoder (DOContext *ctxt) - (void) _service_retain: (NSPortCoder*)rmc { - unsigned target; - NSPortCoder *op; - int sequence; + unsigned target; + NSPortCoder *op; + int sequence; + NSDistantObject *local; + NSString *response = nil; NSParameterAssert (_isValid); @@ -2562,27 +2569,19 @@ static void callEncoder (DOContext *ctxt) NSLog(@"looking to retain local object with target (0x%x) on (0x%x)", target, (gsaddr)self); - if ([self includesLocalTarget: target] == nil) + M_LOCK(_proxiesGate); + local = [self locateLocalTarget: target]; + if (local == nil) { - NSDistantObject *proxy = [self locateLocalTarget: target]; - - if (proxy == nil) - { - [op encodeObject: @"target not found anywhere"]; - } - else - { - [op encodeObject: nil]; // success - } + response = @"target not found anywhere"; } - else + else { - [op encodeObject: nil]; - if (debug_connection > 3) - NSLog(@"target (0x%x) already retained on connection (0x%x)", - target, self); + ((ProxyStruct*)local)->_counter++; // Vended on connection. } + M_UNLOCK(_proxiesGate); + [op encodeObject: response]; [self _sendOutRmc: op type: RETAIN_REPLY]; } @@ -3012,7 +3011,7 @@ static void callEncoder (DOContext *ctxt) M_LOCK(_refGate); /* - * We replace the code we have just used in the cache, and tell it not to + * We replace the coder we have just used in the cache, and tell it not to * retain this connection any more. */ if (cacheCoders == YES && _cachedEncoders != nil) @@ -3099,7 +3098,8 @@ static void callEncoder (DOContext *ctxt) M_UNLOCK(_proxiesGate); } -- (NSDistantObject*) localForObject: (id)object +- (NSDistantObject*) retainOrAddLocal: (NSDistantObject*)proxy + forObject: (id)object { GSIMapNode node; NSDistantObject *p; @@ -3114,78 +3114,87 @@ static void callEncoder (DOContext *ctxt) else { p = node->value.obj; + RETAIN(p); + DESTROY(proxy); + } + if (p == nil && proxy != nil) + { + p = proxy; + [self addLocalObject: p]; } M_UNLOCK(_proxiesGate); - NSParameterAssert(p == nil || [p connectionForProxy] == self); return p; } -- (void) removeLocalObject: (id)anObj +- (void) removeLocalObject: (NSDistantObject*)prox { - NSDistantObject *prox; - unsigned target; - unsigned val = 0; - GSIMapNode node; + id anObj; + unsigned target; + unsigned val = 0; + GSIMapNode node; M_LOCK(_proxiesGate); - + anObj = ((ProxyStruct*)prox)->_object; node = GSIMapNodeForKey(_localObjects, (GSIMapKey)anObj); - if (node == 0) - { - M_UNLOCK(_proxiesGate); - [NSException raise: NSInternalInconsistencyException - format: @"Attempt to remove non-existent local %@", anObj]; - } - prox = node->value.obj; - target = ((ProxyStruct*)prox)->_handle; - if (1) + /* + * The NSDistantObject concerned may not belong to this connection, + * so we need to check that any matching proxy is identical to the + * argument we were given. + */ + if (node != 0 && node->value.obj == prox) { - id item; + target = ((ProxyStruct*)prox)->_handle; - M_LOCK(cached_proxies_gate); /* - * 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 this proxy has been vended onwards to another process + * which has not myet released it, we need to keep a reference + * to the local object around for a while in case that other + * process needs it. */ - if (timer == nil) + if ((((ProxyStruct*)prox)->_counter) != 0) { - timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 - target: connectionClass - selector: @selector(_timeout:) - userInfo: nil - repeats: YES]; + CachedLocalObject *item; + + (((ProxyStruct*)prox)->_counter) = 0; + M_LOCK(cached_proxies_gate); + if (timer == nil) + { + timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 + target: connectionClass + selector: @selector(_timeout:) + userInfo: nil + repeats: YES]; + } + item = [CachedLocalObject newWithObject: prox time: 5]; + NSMapInsert(targetToCached, (void*)target, item); + M_UNLOCK(cached_proxies_gate); + RELEASE(item); + if (debug_connection > 3) + NSLog(@"placed local object (0x%x) target (0x%x) in cache", + (gsaddr)anObj, target); } - item = [CachedLocalObject newWithObject: prox time: 5]; - NSMapInsert(targetToCached, (void*)target, item); - M_UNLOCK(cached_proxies_gate); - RELEASE(item); - if (debug_connection > 3) - NSLog(@"placed local object (0x%x) target (0x%x) in cache", - (gsaddr)anObj, target); + + /* + * Remove the proxy from _localObjects and release it. + */ + GSIMapRemoveKey(_localObjects, (GSIMapKey)anObj); + RELEASE(prox); + + /* + * Remove the target info too - no release required. + */ + GSIMapRemoveKey(_localTargets, (GSIMapKey)target); + + if (debug_connection > 2) + NSLog(@"removed local object (0x%x) target (0x%x) " + @"from connection (0x%x) (ref %d)", + (gsaddr)anObj, target, (gsaddr)self, val); } - - /* - * Remove the proxy from _localObjects and release it. - */ - GSIMapRemoveKey(_localObjects, (GSIMapKey)anObj); - RELEASE(prox); - - /* - * Remove the target info too - no release required. - */ - GSIMapRemoveKey(_localTargets, (GSIMapKey)target); - - if (debug_connection > 2) - NSLog(@"remove local object (0x%x) target (0x%x) " - @"from connection (0x%x) (ref %d)", - (gsaddr)anObj, target, (gsaddr)self, val); - M_UNLOCK(_proxiesGate); } -- (void) _release_targets: (unsigned*)list count: (unsigned)number +- (void) _release_target: (unsigned)target count: (unsigned)number { NS_DURING { @@ -3206,10 +3215,10 @@ static void callEncoder (DOContext *ctxt) for (i = 0; i < number; i++) { - [op encodeValueOfObjCType: @encode(unsigned) at: &list[i]]; + [op encodeValueOfObjCType: @encode(unsigned) at: &target]; if (debug_connection > 3) NSLog(@"sending release for target (0x%x) on (0x%x)", - list[i], (gsaddr)self); + target, (gsaddr)self); } [self _sendOutRmc: op type: PROXY_RELEASE]; @@ -3229,6 +3238,11 @@ static void callEncoder (DOContext *ctxt) GSIMapNode node; M_LOCK(_proxiesGate); + + /* + * Try a quick lookup to see if the target references a local object + * belonging to the receiver ... usually it should. + */ node = GSIMapNodeForKey(_localTargets, (GSIMapKey)target); if (node != 0) { @@ -3236,8 +3250,35 @@ static void callEncoder (DOContext *ctxt) } /* - * If not found in the current connection, try all other existing - * connections. + * If the target doesn't exist in the receiver, but still + * persists in the cache (ie it was recently released) then + * we move it back from the cache to the receiver. + */ + if (proxy == nil) + { + CachedLocalObject *cached; + + M_LOCK(cached_proxies_gate); + cached = NSMapGet (targetToCached, (void*)target); + if (cached != nil) + { + proxy = [cached obj]; + /* + * Found in cache ... add to this connection as the object + * is no longer in use by any connection. + */ + ASSIGN(((ProxyStruct*)proxy)->_connection, self); + [self addLocalObject: proxy]; + NSMapRemove(targetToCached, (void*)target); + if (debug_connection > 3) + NSLog(@"target (0x%x) moved from cache", target); + } + M_UNLOCK(cached_proxies_gate); + } + + /* + * If not found in the current connection or the cache of local references + * of recently invalidated connections, try all other existing connections. */ if (proxy == nil) { @@ -3255,11 +3296,29 @@ static void callEncoder (DOContext *ctxt) node = GSIMapNodeForKey(c->_localTargets, (GSIMapKey)target); if (node != 0) { - proxy = node->value.obj; + id local; + unsigned nTarget; + /* - * Found in another connection ... add to this one. + * We found the local object in use in another connection + * so we create a new reference to the same object and + * add it to our connection, adjusting the target of the + * new reference to be the value we need. + * + * We don't want to just share the NSDistantObject with + * the other connection, since we might want to keep + * track of information on a per-connection basis in + * order to handle connection shutdown cleanly. */ - [self addLocalObject: proxy]; + proxy = node->value.obj; + local = RETAIN(((ProxyStruct*)proxy)->_object); + proxy = [NSDistantObject proxyWithLocal: local + connection: self]; + nTarget = ((ProxyStruct*)proxy)->_handle; + GSIMapRemoveKey(_localTargets, (GSIMapKey)nTarget); + ((ProxyStruct*)proxy)->_handle = target; + GSIMapAddPair(_localTargets, (GSIMapKey)target, + (GSIMapVal)proxy); } M_UNLOCK(c->_proxiesGate); } @@ -3268,33 +3327,6 @@ static void callEncoder (DOContext *ctxt) M_UNLOCK(connection_table_gate); } - if (proxy == nil) - { - CachedLocalObject *cached; - - /* - * 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 connection so we can - * retain it on this connection. - */ - M_LOCK(cached_proxies_gate); - cached = NSMapGet (targetToCached, (void*)target); - if (cached != nil) - { - proxy = [cached obj]; - /* - * Found in cache ... add to this connection. - */ - ASSIGN(((ProxyStruct*)proxy)->_connection, self); - [self addLocalObject: proxy]; - NSMapRemove(targetToCached, (void*)target); - if (debug_connection > 3) - NSLog(@"target (0x%x) moved from cache", target); - } - M_UNLOCK(cached_proxies_gate); - } - M_UNLOCK(_proxiesGate); if (proxy == nil) @@ -3305,39 +3337,69 @@ static void callEncoder (DOContext *ctxt) return proxy; } -- (void) retainTarget: (unsigned)target +- (void) vendLocal: (NSDistantObject*)aProxy { - NS_DURING + M_LOCK(_proxiesGate); + ((ProxyStruct*)aProxy)->_counter++; + M_UNLOCK(_proxiesGate); +} + +- (void) aquireProxyForTarget: (unsigned)target +{ + NSDistantObject *found; + GSIMapNode node; + + /* Don't assert (_isValid); */ + M_LOCK(_proxiesGate); + node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)target); + if (node == 0) { - /* - * Tell the remote app that it must retain the local object - * for the target on this connection. - */ - if (_receivePort && _isValid) + found = nil; + } + else + { + found = node->value.obj; + } + M_UNLOCK(_proxiesGate); + if (found == nil) + { + NS_DURING { - NSPortCoder *op; - id ip; - id result; - int seq_num; + /* + * Tell the remote app that it must retain the local object + * for the target on this connection. + */ + if (_receivePort && _isValid) + { + NSPortCoder *op; + id ip; + id result; + int seq_num; - op = [self _makeOutRmc: 0 generate: &seq_num reply: YES]; - [op encodeValueOfObjCType: @encode(typeof(target)) at: &target]; - [self _sendOutRmc: op type: PROXY_RETAIN]; + op = [self _makeOutRmc: 0 generate: &seq_num reply: YES]; + [op encodeValueOfObjCType: @encode(typeof(target)) at: &target]; + [self _sendOutRmc: op type: PROXY_RETAIN]; - ip = [self _getReplyRmc: seq_num]; - [ip decodeValueOfObjCType: @encode(id) at: &result]; - [self _doneInRmc: ip]; - if (result != nil) - NSLog(@"failed to retain target - %@", result); - else if (debug_connection > 3) - NSLog(@"sending retain for target - %u", target); + ip = [self _getReplyRmc: seq_num]; + [ip decodeValueOfObjCType: @encode(id) at: &result]; + [self _doneInRmc: ip]; + if (result != nil) + NSLog(@"failed to retain target - %@", result); + else if (debug_connection > 3) + NSLog(@"sending retain for target - %u", target); + } } + NS_HANDLER + { + NSLog(@"failed to retain target - %@", localException); + } + NS_ENDHANDLER } - NS_HANDLER - { - NSLog(@"failed to retain target - %@", localException); - } - NS_ENDHANDLER +} + +- (id) retain +{ + return [super retain]; } - (void) removeProxy: (NSDistantObject*)aProxy @@ -3346,31 +3408,60 @@ static void callEncoder (DOContext *ctxt) if (_isValid == YES) { unsigned target; + unsigned count = 1; GSIMapNode node; target = ((ProxyStruct*)aProxy)->_handle; node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)target); - if (node != 0) - { - GSIMapRemoveKey(_remoteProxies, (GSIMapKey)target); - } + /* - * Tell the remote application that we have removed our proxy and - * it can release it's local object. + * Only remove if the proxy for the target is the same as the + * supplied argument. */ - [self _release_targets: &target count: 1]; + if (node != 0 && node->value.obj == aProxy) + { + count = ((ProxyStruct*)aProxy)->_counter; + GSIMapRemoveKey(_remoteProxies, (GSIMapKey)target); + /* + * Tell the remote application that we have removed our proxy and + * it can release it's local object. + */ + [self _release_target: target count: count]; + } } M_UNLOCK(_proxiesGate); } -- (NSDistantObject*) proxyForTarget: (unsigned)target + +/** + * Private method used only when a remote process/thread has sent us a + * target which we are decoding into a proxy in this process/thread. + *

The argument aProxy may be nil, in which case an existing proxy + * matching aTarget is retrieved retained, and returned (this is done + * when a proxy target is sent to us by a remote process). + *

+ *

If aProxy is not nil, but a proxy with the same target already + * exists, then aProxy is released and the existing proxy is returned + * as in the case where aProxy was nil. + *

+ *

If aProxy is not nil and there was no prior proxy with the same + * target, aProxy is added to the receiver and returned. + *

+ */ +- (NSDistantObject*) retainOrAddProxy: (NSDistantObject*)aProxy + forTarget: (unsigned)aTarget { NSDistantObject *p; GSIMapNode node; /* Don't assert (_isValid); */ + NSParameterAssert(aTarget > 0); + NSParameterAssert(aProxy==nil || aProxy->isa == distantObjectClass); + NSParameterAssert(aProxy==nil || [aProxy connectionForProxy] == self); + NSParameterAssert(aProxy==nil || aTarget == ((ProxyStruct*)aProxy)->_handle); + M_LOCK(_proxiesGate); - node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)target); + node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)aTarget); if (node == 0) { p = nil; @@ -3378,52 +3469,28 @@ static void callEncoder (DOContext *ctxt) else { p = node->value.obj; + RETAIN(p); + DESTROY(aProxy); + } + if (p == nil && aProxy != nil) + { + p = aProxy; + GSIMapAddPair(_remoteProxies, (GSIMapKey)aTarget, (GSIMapVal)p); + } + /* + * Whether this is a new proxy or an existing proxy, this method is + * only called for an object being vended by a remote process/thread. + * We therefore need to increment the count of the number of times + * the proxy has been vended. + */ + if (p != nil) + { + ((ProxyStruct*)p)->_counter++; } M_UNLOCK(_proxiesGate); return p; } -- (void) addProxy: (NSDistantObject*)aProxy -{ - unsigned target; - GSIMapNode node; - - M_LOCK(_proxiesGate); - NSParameterAssert(_isValid); - NSParameterAssert(aProxy->isa == distantObjectClass); - NSParameterAssert([aProxy connectionForProxy] == self); - target = ((ProxyStruct*)aProxy)->_handle; - node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)target); - if (node != 0) - { - M_UNLOCK(_proxiesGate); - [NSException raise: NSGenericException - format: @"Trying to add the same proxy twice"]; - } - GSIMapAddPair(_remoteProxies, (GSIMapKey)target, (GSIMapVal)aProxy); - M_UNLOCK(_proxiesGate); -} - -- (id) includesProxyForTarget: (unsigned)target -{ - NSDistantObject *ret; - GSIMapNode node; - - /* Don't assert (_isValid); */ - M_LOCK(_proxiesGate); - node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)target); - if (node == 0) - { - ret = nil; - } - else - { - ret = node->value.obj; - } - M_UNLOCK(_proxiesGate); - return ret; -} - - (id) includesLocalObject: (id)anObj { NSDistantObject *ret; diff --git a/Source/NSDistantObject.m b/Source/NSDistantObject.m index c2f5356c0..fdc1ab75c 100644 --- a/Source/NSDistantObject.m +++ b/Source/NSDistantObject.m @@ -55,10 +55,13 @@ typedef struct { } NSDO; @interface NSConnection (DistantObjectHacks) -- (BOOL) includesProxyForTarget: (unsigned)target; -- (void) addLocalObject: (id)obj; -- (void) addProxy: (id)obj; +- (void) aquireProxyForTarget: (unsigned)target; +- (NSDistantObject*) retainOrAddLocal: (NSDistantObject*)aProxy + forObject: (id)anObject; +- (NSDistantObject*) retainOrAddProxy: (NSDistantObject*)aProxy + forTarget: (unsigned)aTarget; - (void) removeProxy: (id)obj; +- (void) vendLocal: (NSDistantObject*)aProxy; @end /* This is the proxy tag; it indicates where the local object is, @@ -171,7 +174,7 @@ enum NSLog(@"Local object is 0x%x (0x%x)\n", (gsaddr)o, (gsaddr)o ? ((NSDO*)o)->_object : 0); } - return o ? RETAIN(((NSDO*)o)->_object) : nil; + return RETAIN(((NSDO*)o)->_object); } case PROXY_LOCAL_FOR_SENDER: @@ -187,8 +190,9 @@ enum 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]; + o = [self initWithTarget: target + connection: decoder_connection]; + return o; case PROXY_REMOTE_FOR_BOTH: /* @@ -260,12 +264,11 @@ enum 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. + * We may not already have a proxy for the object on the + * remote system, we must tell the connection to make sure + * the other end knows we are creating one. */ - if ([proxy_connection includesProxyForTarget: target] == NO) - [proxy_connection retainTarget: target]; + [proxy_connection aquireProxyForTarget: target]; /* * Finally - we get a proxy via a direct connection to the @@ -274,8 +277,9 @@ enum * and will therefore go away when the current autorelease * pool is destroyed. */ - return [self initWithTarget: target - connection: proxy_connection]; + o = [self initWithTarget: target + connection: proxy_connection]; + return o; } default: @@ -297,14 +301,14 @@ enum * If there already is a local proxy for this target/connection * combination, don't create a new one, just return the old one. */ - if ((proxy = [aConnection localForObject: anObject])) + proxy = [aConnection retainOrAddLocal: nil forObject: anObject]; + if (proxy == nil) { - return RETAIN(proxy); - } - - proxy = (NSDistantObject*)NSAllocateObject(distantObjectClass, + proxy = (NSDistantObject*)NSAllocateObject(distantObjectClass, 0, NSDefaultMallocZone()); - return [proxy initWithLocal: anObject connection: aConnection]; + proxy = [proxy initWithLocal: anObject connection: aConnection]; + } + return proxy; } + (id) initWithTarget: (unsigned)target connection: (NSConnection*)aConnection @@ -314,17 +318,17 @@ enum NSAssert([aConnection isValid], NSInternalInconsistencyException); /* - * If there already is a local proxy for this target/connection - * combination, don't create a new one, just return the old one. + * If there already is a local proxy for this target/connection + * combination, don't create a new one, just return the old one. */ - if ((proxy = [aConnection proxyForTarget: target])) + proxy = [aConnection retainOrAddProxy: nil forTarget: target]; + if (proxy == nil) { - return RETAIN(proxy); - } - - proxy = (NSDistantObject*)NSAllocateObject(distantObjectClass, + proxy = (NSDistantObject*)NSAllocateObject(distantObjectClass, 0, NSDefaultMallocZone()); - return [proxy initWithTarget: target connection: aConnection]; + proxy = [proxy initWithTarget: target connection: aConnection]; + } + return proxy; } @end @@ -441,6 +445,10 @@ enum [aRmc encodeValueOfObjCType: @encode(typeof(proxy_target)) at: &proxy_target]; + /* + * Tell connection this object is being vended. + */ + [_connection vendLocal: self]; } else { @@ -478,10 +486,10 @@ enum 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. + * 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]; @@ -493,8 +501,8 @@ enum proxy_target, (gsaddr)_connection); /* - * It's remote here, so we need to tell other side where to form - * triangle connection to + * It's remote here, so we need to tell other side where to form + * triangle connection to */ [aRmc encodeValueOfObjCType: @encode(typeof(proxy_tag)) at: &proxy_tag]; @@ -506,6 +514,11 @@ enum at: &proxy_target]; [aRmc encodeBycopyObject: proxy_connection_out_port]; + + /* + * Tell connection that localProxy is being vended. + */ + [encoder_connection vendLocal: localProxy]; } } @@ -529,179 +542,9 @@ enum - (id) initWithCoder: (NSCoder*)aCoder { - gsu8 proxy_tag; - unsigned target; - id decoder_connection; - NSDistantObject *o; - -/* - if ([aCoder isKindOfClass: [NSPortCoder class]] == NO) - { - RELEASE(self); - [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); - - o = [decoder_connection locateLocalTarget: target]; - if (o == nil) - { - RELEASE(self); - [NSException raise: @"ProxyDecodedBadTarget" - format: @"No local object with given target (0x%x)", - target]; - } - else - { - if (debug_proxy) - { - NSLog(@"Local object is 0x%x (0x%x)\n", - (gsaddr)o, (gsaddr)o ? o->_object : 0); - } - RELEASE(self); - return o ? RETAIN(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); - RELEASE(self); - return RETAIN([NSDistantObject proxyWithTarget: 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. - */ - { - 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 decodeValueOfObjCType: @encode(typeof(intermediary)) - at: &intermediary]; - [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 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]; - - result = RETAIN([NSDistantObject proxyWithTarget: target - connection: proxy_connection]); - RELEASE(self); - - /* - * 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; - } - - default: - /* xxx This should be something different than NSGenericException. */ - RELEASE(self); - [NSException raise: NSGenericException - format: @"Bad proxy tag"]; - } - /* Not reached. */ + DESTROY(self); + [NSException raise: NSGenericException + format: @"NSDistantObject decodes from placeholder"]; return nil; } @@ -711,28 +554,12 @@ enum */ - (id) initWithLocal: (id)anObject connection: (NSConnection*)aConnection { - NSDistantObject *new_proxy; - NSAssert([aConnection isValid], NSInternalInconsistencyException); - /* - * If there already is a local proxy for this target/connection - * combination, don't create a new one, just return the old one. - */ - if ((new_proxy = [aConnection localForObject: anObject])) - { - RETAIN(new_proxy); - RELEASE(self); - return new_proxy; - } - _object = RETAIN(anObject); - - /* - * We register this proxy with the connection using it. - */ + _handle = 0; _connection = RETAIN(aConnection); - [_connection addLocalObject: self]; + self = [_connection retainOrAddLocal: self forObject: anObject]; if (debug_proxy) NSLog(@"Created new local=0x%x object 0x%x target 0x%x connection 0x%x\n", @@ -748,34 +575,17 @@ enum */ - (id) initWithTarget: (unsigned)target connection: (NSConnection*)aConnection { - NSDistantObject *new_proxy; - NSAssert([aConnection isValid], NSInternalInconsistencyException); - /* - * If there already is a proxy for this target/connection combination, - * don't create a new one, just return the old one. - */ - if ((new_proxy = [aConnection proxyForTarget: target])) - { - RETAIN(new_proxy); - RELEASE(self); - return new_proxy; - } - _object = nil; _handle = target; - - /* - * We retain our connection so it can't disappear while the app - * may want to use it. - */ _connection = RETAIN(aConnection); /* - * We register this object with the connection using it. + * We register this object with the connection using it. + * Conceivably this could result in self being changed. */ - [_connection addProxy: self]; + self = [_connection retainOrAddProxy: self forTarget: target]; if (debug_proxy) NSLog(@"Created new proxy=0x%x target 0x%x connection 0x%x\n", diff --git a/Testing/nsconnection_client.m b/Testing/nsconnection_client.m index 3a1f87a23..6edc5c25e 100644 --- a/Testing/nsconnection_client.m +++ b/Testing/nsconnection_client.m @@ -433,15 +433,16 @@ con_callback (id prx) localObj = [CallbackClient new]; [prx registerClient: localObj]; - k = 10000; + k = 1000; for (j = 0; j < k; j++) { + CREATE_AUTORELEASE_POOL(arp); [prx unregisterClient: localObj]; [prx registerClient: localObj]; [prx tryClientCallback]; if (j < 10 || j %10 == 0) printf("repeated client registration and callback %d\n", j); - + RELEASE(arp); } printf("repeated client registration and callback %d\n", j); RELEASE(localObj); @@ -493,7 +494,7 @@ int main (int argc, char *argv[], char **env) switch (c) { case 'd': - debug = 1; + debug++; break; case 't': type_test = TYPE_TEST; @@ -538,6 +539,13 @@ int main (int argc, char *argv[], char **env) else connect_attempts = 1; + if (debug > 0) + { + [NSConnection setDebug: debug]; + [NSDistantObject setDebug: debug]; + [NSObject enableDoubleReleaseCheck: YES]; + } + while (connect_attempts-- > 0) { if (optind < argc) diff --git a/Testing/nsconnection_server.m b/Testing/nsconnection_server.m index c1d9fb1d5..520d391e7 100644 --- a/Testing/nsconnection_server.m +++ b/Testing/nsconnection_server.m @@ -476,7 +476,7 @@ int main(int argc, char *argv[], char **env) switch (i) { case 'd': - debug = 1; + debug++; break; case 't': timeout = atoi(optarg);; @@ -497,6 +497,12 @@ int main(int argc, char *argv[], char **env) [NSDistantObject setProtocolForProxies:@protocol(AllProxies)]; #endif + if (debug > 0) + { + [NSDistantObject setDebug: debug]; + [NSConnection setDebug: debug]; + [NSObject enableDoubleReleaseCheck: YES]; + } c = [NSConnection defaultConnection]; [c setRootObject: l];