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:
CaS 2003-10-27 13:41:01 +00:00
parent 0a746f0769
commit ba1698ff9c
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> Mon Oct 27 13:24:00 2003 Richard Frith-Macdonald <rfm@gnu.org>
* Source/GNUmakefile: build new lock classes * Source/GNUmakefile: build new lock classes

View file

@ -195,8 +195,7 @@ stringFromMsgType(int type)
+ (void) setDebug: (int)val; + (void) setDebug: (int)val;
- (void) addLocalObject: (NSDistantObject*)anObj; - (void) addLocalObject: (NSDistantObject*)anObj;
- (NSDistantObject*) localForObject: (id)object; - (void) removeLocalObject: (NSDistantObject*)anObj;
- (void) removeLocalObject: (id)anObj;
- (void) _doneInReply: (NSPortCoder*)c; - (void) _doneInReply: (NSPortCoder*)c;
- (void) _doneInRmc: (NSPortCoder*)c; - (void) _doneInRmc: (NSPortCoder*)c;
@ -1107,9 +1106,7 @@ static BOOL multi_threaded = NO;
} }
while (i-- > 0) while (i-- > 0)
{ {
id t = ((ProxyStruct*)[targets objectAtIndex: i])->_object; [self removeLocalObject: [targets objectAtIndex: i]];
[self removeLocalObject: t];
} }
RELEASE(targets); RELEASE(targets);
GSIMapEmptyMap(_localTargets); GSIMapEmptyMap(_localTargets);
@ -2533,9 +2530,17 @@ static void callEncoder (DOContext *ctxt)
if (prox != nil) if (prox != nil)
{ {
if (debug_connection > 3) if (debug_connection > 3)
NSLog(@"releasing object with target (0x%x) on (0x%x)", NSLog(@"releasing object with target (0x%x) on (0x%x) counter %d",
target, (gsaddr)self); target, (gsaddr)self, ((ProxyStruct*)prox)->_counter);
[self removeLocalObject: ((ProxyStruct*)prox)->_object]; #if 1
// FIXME thread safety
if (--(((ProxyStruct*)prox)->_counter) == 0)
{
[self removeLocalObject: prox];
}
#else
[self removeLocalObject: prox];
#endif
} }
else if (debug_connection > 3) else if (debug_connection > 3)
NSLog(@"releasing object with target (0x%x) on (0x%x) - nothing to do", 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 - (void) _service_retain: (NSPortCoder*)rmc
{ {
unsigned target; unsigned target;
NSPortCoder *op; NSPortCoder *op;
int sequence; int sequence;
NSDistantObject *local;
NSString *response = nil;
NSParameterAssert (_isValid); NSParameterAssert (_isValid);
@ -2562,27 +2569,19 @@ static void callEncoder (DOContext *ctxt)
NSLog(@"looking to retain local object with target (0x%x) on (0x%x)", NSLog(@"looking to retain local object with target (0x%x) on (0x%x)",
target, (gsaddr)self); target, (gsaddr)self);
if ([self includesLocalTarget: target] == nil) M_LOCK(_proxiesGate);
local = [self locateLocalTarget: target];
if (local == nil)
{ {
NSDistantObject *proxy = [self locateLocalTarget: target]; response = @"target not found anywhere";
if (proxy == nil)
{
[op encodeObject: @"target not found anywhere"];
}
else
{
[op encodeObject: nil]; // success
}
} }
else else
{ {
[op encodeObject: nil]; ((ProxyStruct*)local)->_counter++; // Vended on connection.
if (debug_connection > 3)
NSLog(@"target (0x%x) already retained on connection (0x%x)",
target, self);
} }
M_UNLOCK(_proxiesGate);
[op encodeObject: response];
[self _sendOutRmc: op type: RETAIN_REPLY]; [self _sendOutRmc: op type: RETAIN_REPLY];
} }
@ -3012,7 +3011,7 @@ static void callEncoder (DOContext *ctxt)
M_LOCK(_refGate); 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. * retain this connection any more.
*/ */
if (cacheCoders == YES && _cachedEncoders != nil) if (cacheCoders == YES && _cachedEncoders != nil)
@ -3099,7 +3098,8 @@ static void callEncoder (DOContext *ctxt)
M_UNLOCK(_proxiesGate); M_UNLOCK(_proxiesGate);
} }
- (NSDistantObject*) localForObject: (id)object - (NSDistantObject*) retainOrAddLocal: (NSDistantObject*)proxy
forObject: (id)object
{ {
GSIMapNode node; GSIMapNode node;
NSDistantObject *p; NSDistantObject *p;
@ -3114,78 +3114,87 @@ static void callEncoder (DOContext *ctxt)
else else
{ {
p = node->value.obj; p = node->value.obj;
RETAIN(p);
DESTROY(proxy);
}
if (p == nil && proxy != nil)
{
p = proxy;
[self addLocalObject: p];
} }
M_UNLOCK(_proxiesGate); M_UNLOCK(_proxiesGate);
NSParameterAssert(p == nil || [p connectionForProxy] == self);
return p; return p;
} }
- (void) removeLocalObject: (id)anObj - (void) removeLocalObject: (NSDistantObject*)prox
{ {
NSDistantObject *prox; id anObj;
unsigned target; unsigned target;
unsigned val = 0; unsigned val = 0;
GSIMapNode node; GSIMapNode node;
M_LOCK(_proxiesGate); M_LOCK(_proxiesGate);
anObj = ((ProxyStruct*)prox)->_object;
node = GSIMapNodeForKey(_localObjects, (GSIMapKey)anObj); 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 * If this proxy has been vended onwards to another process
* need to keep a reference to the local object around for a * which has not myet released it, we need to keep a reference
* while in case that other process needs it. * 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 CachedLocalObject *item;
target: connectionClass
selector: @selector(_timeout:) (((ProxyStruct*)prox)->_counter) = 0;
userInfo: nil M_LOCK(cached_proxies_gate);
repeats: YES]; 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); * Remove the proxy from _localObjects and release it.
RELEASE(item); */
if (debug_connection > 3) GSIMapRemoveKey(_localObjects, (GSIMapKey)anObj);
NSLog(@"placed local object (0x%x) target (0x%x) in cache", RELEASE(prox);
(gsaddr)anObj, target);
/*
* 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); M_UNLOCK(_proxiesGate);
} }
- (void) _release_targets: (unsigned*)list count: (unsigned)number - (void) _release_target: (unsigned)target count: (unsigned)number
{ {
NS_DURING NS_DURING
{ {
@ -3206,10 +3215,10 @@ static void callEncoder (DOContext *ctxt)
for (i = 0; i < number; i++) for (i = 0; i < number; i++)
{ {
[op encodeValueOfObjCType: @encode(unsigned) at: &list[i]]; [op encodeValueOfObjCType: @encode(unsigned) at: &target];
if (debug_connection > 3) if (debug_connection > 3)
NSLog(@"sending release for target (0x%x) on (0x%x)", NSLog(@"sending release for target (0x%x) on (0x%x)",
list[i], (gsaddr)self); target, (gsaddr)self);
} }
[self _sendOutRmc: op type: PROXY_RELEASE]; [self _sendOutRmc: op type: PROXY_RELEASE];
@ -3229,6 +3238,11 @@ static void callEncoder (DOContext *ctxt)
GSIMapNode node; GSIMapNode node;
M_LOCK(_proxiesGate); 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); node = GSIMapNodeForKey(_localTargets, (GSIMapKey)target);
if (node != 0) if (node != 0)
{ {
@ -3236,8 +3250,35 @@ static void callEncoder (DOContext *ctxt)
} }
/* /*
* If not found in the current connection, try all other existing * If the target doesn't exist in the receiver, but still
* connections. * 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) if (proxy == nil)
{ {
@ -3255,11 +3296,29 @@ static void callEncoder (DOContext *ctxt)
node = GSIMapNodeForKey(c->_localTargets, (GSIMapKey)target); node = GSIMapNodeForKey(c->_localTargets, (GSIMapKey)target);
if (node != 0) 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); M_UNLOCK(c->_proxiesGate);
} }
@ -3268,33 +3327,6 @@ static void callEncoder (DOContext *ctxt)
M_UNLOCK(connection_table_gate); 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); M_UNLOCK(_proxiesGate);
if (proxy == nil) if (proxy == nil)
@ -3305,39 +3337,69 @@ static void callEncoder (DOContext *ctxt)
return proxy; 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)
{ {
/* found = nil;
* Tell the remote app that it must retain the local object }
* for the target on this connection. else
*/ {
if (_receivePort && _isValid) found = node->value.obj;
}
M_UNLOCK(_proxiesGate);
if (found == nil)
{
NS_DURING
{ {
NSPortCoder *op; /*
id ip; * Tell the remote app that it must retain the local object
id result; * for the target on this connection.
int seq_num; */
if (_receivePort && _isValid)
{
NSPortCoder *op;
id ip;
id result;
int seq_num;
op = [self _makeOutRmc: 0 generate: &seq_num reply: YES]; op = [self _makeOutRmc: 0 generate: &seq_num reply: YES];
[op encodeValueOfObjCType: @encode(typeof(target)) at: &target]; [op encodeValueOfObjCType: @encode(typeof(target)) at: &target];
[self _sendOutRmc: op type: PROXY_RETAIN]; [self _sendOutRmc: op type: PROXY_RETAIN];
ip = [self _getReplyRmc: seq_num]; ip = [self _getReplyRmc: seq_num];
[ip decodeValueOfObjCType: @encode(id) at: &result]; [ip decodeValueOfObjCType: @encode(id) at: &result];
[self _doneInRmc: ip]; [self _doneInRmc: ip];
if (result != nil) if (result != nil)
NSLog(@"failed to retain target - %@", result); NSLog(@"failed to retain target - %@", result);
else if (debug_connection > 3) else if (debug_connection > 3)
NSLog(@"sending retain for target - %u", target); 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); - (id) retain
} {
NS_ENDHANDLER return [super retain];
} }
- (void) removeProxy: (NSDistantObject*)aProxy - (void) removeProxy: (NSDistantObject*)aProxy
@ -3346,31 +3408,60 @@ static void callEncoder (DOContext *ctxt)
if (_isValid == YES) if (_isValid == YES)
{ {
unsigned target; unsigned target;
unsigned count = 1;
GSIMapNode node; GSIMapNode node;
target = ((ProxyStruct*)aProxy)->_handle; target = ((ProxyStruct*)aProxy)->_handle;
node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)target); node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)target);
if (node != 0)
{
GSIMapRemoveKey(_remoteProxies, (GSIMapKey)target);
}
/* /*
* Tell the remote application that we have removed our proxy and * Only remove if the proxy for the target is the same as the
* it can release it's local object. * 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); 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; NSDistantObject *p;
GSIMapNode node; GSIMapNode node;
/* Don't assert (_isValid); */ /* 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); M_LOCK(_proxiesGate);
node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)target); node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)aTarget);
if (node == 0) if (node == 0)
{ {
p = nil; p = nil;
@ -3378,52 +3469,28 @@ static void callEncoder (DOContext *ctxt)
else else
{ {
p = node->value.obj; 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); M_UNLOCK(_proxiesGate);
return p; 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 - (id) includesLocalObject: (id)anObj
{ {
NSDistantObject *ret; NSDistantObject *ret;

View file

@ -55,10 +55,13 @@ typedef struct {
} NSDO; } NSDO;
@interface NSConnection (DistantObjectHacks) @interface NSConnection (DistantObjectHacks)
- (BOOL) includesProxyForTarget: (unsigned)target; - (void) aquireProxyForTarget: (unsigned)target;
- (void) addLocalObject: (id)obj; - (NSDistantObject*) retainOrAddLocal: (NSDistantObject*)aProxy
- (void) addProxy: (id)obj; forObject: (id)anObject;
- (NSDistantObject*) retainOrAddProxy: (NSDistantObject*)aProxy
forTarget: (unsigned)aTarget;
- (void) removeProxy: (id)obj; - (void) removeProxy: (id)obj;
- (void) vendLocal: (NSDistantObject*)aProxy;
@end @end
/* This is the proxy tag; it indicates where the local object is, /* 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", NSLog(@"Local object is 0x%x (0x%x)\n",
(gsaddr)o, (gsaddr)o ? ((NSDO*)o)->_object : 0); (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: case PROXY_LOCAL_FOR_SENDER:
@ -187,8 +190,9 @@ enum
if (debug_proxy) if (debug_proxy)
NSLog(@"Receiving a proxy, was local 0x%x connection 0x%x\n", NSLog(@"Receiving a proxy, was local 0x%x connection 0x%x\n",
target, (gsaddr)decoder_connection); target, (gsaddr)decoder_connection);
return [self initWithTarget: target o = [self initWithTarget: target
connection: decoder_connection]; connection: decoder_connection];
return o;
case PROXY_REMOTE_FOR_BOTH: case PROXY_REMOTE_FOR_BOTH:
/* /*
@ -260,12 +264,11 @@ enum
NSInternalInconsistencyException); NSInternalInconsistencyException);
/* /*
* If we don't already have a proxy for the object on the * We may not already have a proxy for the object on the
* remote system, we must tell the other end to retain its * remote system, we must tell the connection to make sure
* local object for our use. * the other end knows we are creating one.
*/ */
if ([proxy_connection includesProxyForTarget: target] == NO) [proxy_connection aquireProxyForTarget: target];
[proxy_connection retainTarget: target];
/* /*
* Finally - we get a proxy via a direct connection to the * 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 * and will therefore go away when the current autorelease
* pool is destroyed. * pool is destroyed.
*/ */
return [self initWithTarget: target o = [self initWithTarget: target
connection: proxy_connection]; connection: proxy_connection];
return o;
} }
default: default:
@ -297,14 +301,14 @@ enum
* If there already is a local proxy for this target/connection * If there already is a local proxy for this target/connection
* combination, don't create a new one, just return the old one. * 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()); 0, NSDefaultMallocZone());
return [proxy initWithLocal: anObject connection: aConnection]; proxy = [proxy initWithLocal: anObject connection: aConnection];
}
return proxy;
} }
+ (id) initWithTarget: (unsigned)target connection: (NSConnection*)aConnection + (id) initWithTarget: (unsigned)target connection: (NSConnection*)aConnection
@ -314,17 +318,17 @@ enum
NSAssert([aConnection isValid], NSInternalInconsistencyException); NSAssert([aConnection isValid], NSInternalInconsistencyException);
/* /*
* If there already is a local proxy for this target/connection * If there already is a local proxy for this target/connection
* combination, don't create a new one, just return the old one. * 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()); 0, NSDefaultMallocZone());
return [proxy initWithTarget: target connection: aConnection]; proxy = [proxy initWithTarget: target connection: aConnection];
}
return proxy;
} }
@end @end
@ -441,6 +445,10 @@ enum
[aRmc encodeValueOfObjCType: @encode(typeof(proxy_target)) [aRmc encodeValueOfObjCType: @encode(typeof(proxy_target))
at: &proxy_target]; at: &proxy_target];
/*
* Tell connection this object is being vended.
*/
[_connection vendLocal: self];
} }
else else
{ {
@ -478,10 +486,10 @@ enum
proxy_tag = PROXY_REMOTE_FOR_BOTH; proxy_tag = PROXY_REMOTE_FOR_BOTH;
/* /*
* Get a proxy to refer to self - we send this to the other * Get a proxy to refer to self - we send this to the other
* side so we will be retained until the other side has * side so we will be retained until the other side has
* obtained a proxy to the original object via a connection * obtained a proxy to the original object via a connection
* to the original vendor. * to the original vendor.
*/ */
localProxy = [NSDistantObject proxyWithLocal: self localProxy = [NSDistantObject proxyWithLocal: self
connection: encoder_connection]; connection: encoder_connection];
@ -493,8 +501,8 @@ enum
proxy_target, (gsaddr)_connection); proxy_target, (gsaddr)_connection);
/* /*
* It's remote here, so we need to tell other side where to form * It's remote here, so we need to tell other side where to form
* triangle connection to * triangle connection to
*/ */
[aRmc encodeValueOfObjCType: @encode(typeof(proxy_tag)) [aRmc encodeValueOfObjCType: @encode(typeof(proxy_tag))
at: &proxy_tag]; at: &proxy_tag];
@ -506,6 +514,11 @@ enum
at: &proxy_target]; at: &proxy_target];
[aRmc encodeBycopyObject: proxy_connection_out_port]; [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 - (id) initWithCoder: (NSCoder*)aCoder
{ {
gsu8 proxy_tag; DESTROY(self);
unsigned target; [NSException raise: NSGenericException
id decoder_connection; format: @"NSDistantObject decodes from placeholder"];
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. */
return nil; return nil;
} }
@ -711,28 +554,12 @@ enum
*/ */
- (id) initWithLocal: (id)anObject connection: (NSConnection*)aConnection - (id) initWithLocal: (id)anObject connection: (NSConnection*)aConnection
{ {
NSDistantObject *new_proxy;
NSAssert([aConnection isValid], NSInternalInconsistencyException); 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); _object = RETAIN(anObject);
_handle = 0;
/*
* We register this proxy with the connection using it.
*/
_connection = RETAIN(aConnection); _connection = RETAIN(aConnection);
[_connection addLocalObject: self]; self = [_connection retainOrAddLocal: self forObject: anObject];
if (debug_proxy) if (debug_proxy)
NSLog(@"Created new local=0x%x object 0x%x target 0x%x connection 0x%x\n", 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 - (id) initWithTarget: (unsigned)target connection: (NSConnection*)aConnection
{ {
NSDistantObject *new_proxy;
NSAssert([aConnection isValid], NSInternalInconsistencyException); 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; _object = nil;
_handle = target; _handle = target;
/*
* We retain our connection so it can't disappear while the app
* may want to use it.
*/
_connection = RETAIN(aConnection); _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) if (debug_proxy)
NSLog(@"Created new proxy=0x%x target 0x%x connection 0x%x\n", 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]; localObj = [CallbackClient new];
[prx registerClient: localObj]; [prx registerClient: localObj];
k = 10000; k = 1000;
for (j = 0; j < k; j++) for (j = 0; j < k; j++)
{ {
CREATE_AUTORELEASE_POOL(arp);
[prx unregisterClient: localObj]; [prx unregisterClient: localObj];
[prx registerClient: localObj]; [prx registerClient: localObj];
[prx tryClientCallback]; [prx tryClientCallback];
if (j < 10 || j %10 == 0) if (j < 10 || j %10 == 0)
printf("repeated client registration and callback %d\n", j); printf("repeated client registration and callback %d\n", j);
RELEASE(arp);
} }
printf("repeated client registration and callback %d\n", j); printf("repeated client registration and callback %d\n", j);
RELEASE(localObj); RELEASE(localObj);
@ -493,7 +494,7 @@ int main (int argc, char *argv[], char **env)
switch (c) switch (c)
{ {
case 'd': case 'd':
debug = 1; debug++;
break; break;
case 't': case 't':
type_test = TYPE_TEST; type_test = TYPE_TEST;
@ -538,6 +539,13 @@ int main (int argc, char *argv[], char **env)
else else
connect_attempts = 1; connect_attempts = 1;
if (debug > 0)
{
[NSConnection setDebug: debug];
[NSDistantObject setDebug: debug];
[NSObject enableDoubleReleaseCheck: YES];
}
while (connect_attempts-- > 0) while (connect_attempts-- > 0)
{ {
if (optind < argc) if (optind < argc)

View file

@ -476,7 +476,7 @@ int main(int argc, char *argv[], char **env)
switch (i) switch (i)
{ {
case 'd': case 'd':
debug = 1; debug++;
break; break;
case 't': case 't':
timeout = atoi(optarg);; timeout = atoi(optarg);;
@ -497,6 +497,12 @@ int main(int argc, char *argv[], char **env)
[NSDistantObject setProtocolForProxies:@protocol(AllProxies)]; [NSDistantObject setProtocolForProxies:@protocol(AllProxies)];
#endif #endif
if (debug > 0)
{
[NSDistantObject setDebug: debug];
[NSConnection setDebug: debug];
[NSObject enableDoubleReleaseCheck: YES];
}
c = [NSConnection defaultConnection]; c = [NSConnection defaultConnection];
[c setRootObject: l]; [c setRootObject: l];