mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 09:04:13 +00:00
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:
parent
10e3d610a0
commit
393e822402
5 changed files with 345 additions and 439 deletions
15
ChangeLog
15
ChangeLog
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
Loading…
Reference in a new issue