Updates for improved reference count management over DO.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@17983 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-Macdonald 2003-10-27 13:41:01 +00:00
parent 10e3d610a0
commit 393e822402
5 changed files with 345 additions and 439 deletions

View file

@ -1,3 +1,18 @@
Mon Oct 27 13:37:00 2003 Richard Frith-Macdonald <rfm@gnu.org>
* 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 <rfm@gnu.org>
* Source/GNUmakefile: build new lock classes

View file

@ -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.
* <p>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).
* </p>
* <p>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.
* </p>
* <p>If aProxy is not nil and there was no prior proxy with the same
* target, aProxy is added to the receiver and returned.
* </p>
*/
- (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;

View file

@ -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",

View file

@ -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)

View file

@ -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];