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

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