Cleanups in preparation for experimental changes to retain/relase over DO.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@17949 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
CaS 2003-10-21 17:05:36 +00:00
parent 89b08c9c88
commit b08343d175
4 changed files with 159 additions and 201 deletions

View file

@ -1,3 +1,22 @@
Tue Oct 21 18:00:00 2003 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSDistantObject.m: Retain local objects on creation,
and release on deallocation ... don't depend on the connection
to do it.
* Source/NSConnection.m: Restructured handling of proxies ...
remove global tables of local object references and just look in all
connections and cache when a local object cannot be found in the
current connection.
Don't retain local objects ... depend on the NSDistantObject class
to do that.
Don't keep counters of the number of connections in which a local
object reference is used ... it's not needed if we don't need to
track whether to release the local object or not.
Shorten timeout for deferred deallocation of local object references
(temporary measure).
* Headers/Foundation/NSDistantObject.h: new ivar for future use
keeping track of number of times an object is vended.
Tue Oct 21 15:25:00 2003 Richard Frith-Macdonald <rfm@gnu.org> Tue Oct 21 15:25:00 2003 Richard Frith-Macdonald <rfm@gnu.org>
* Testing/nsconnection_client.m: Add -r option. * Testing/nsconnection_client.m: Add -r option.

View file

@ -36,6 +36,7 @@
id _object; id _object;
unsigned _handle; unsigned _handle;
Protocol *_protocol; Protocol *_protocol;
unsigned _counter;
} }
+ (NSDistantObject*) proxyWithLocal: (id)anObject + (NSDistantObject*) proxyWithLocal: (id)anObject

View file

@ -104,7 +104,6 @@ static id dummyObject;
static Class connectionClass; static Class connectionClass;
static Class dateClass; static Class dateClass;
static Class distantObjectClass; static Class distantObjectClass;
static Class localCounterClass;
static Class sendCoderClass; static Class sendCoderClass;
static Class recvCoderClass; static Class recvCoderClass;
static Class runLoopClass; static Class runLoopClass;
@ -139,66 +138,26 @@ stringFromMsgType(int type)
} }
} }
/*
* GSLocalCounter is a trivial class to keep track of how
* many different connections a particular local object is vended
* over. This is required so that we know when to remove an object
* from the global list when it is removed from the list of objects
* vended on a particular connection.
*/
@interface GSLocalCounter : NSObject
{
@public
unsigned ref;
unsigned target;
id object;
}
+ (id) newWithObject: (id)ob;
@end
@implementation GSLocalCounter
static unsigned local_object_counter = 0;
+ (id) newWithObject: (id)obj
{
GSLocalCounter *counter;
counter = (GSLocalCounter*)NSAllocateObject(self, 0, NSDefaultMallocZone());
counter->ref = 1;
counter->object = RETAIN(obj);
counter->target = ++local_object_counter;
return counter;
}
- (void) dealloc
{
RELEASE(object);
NSDeallocateObject(self);
}
@end
/* /*
* CachedLocalObject is a trivial class to keep track of how * CachedLocalObject is a trivial class to keep track of local
* many different connections a particular local object is vended * proxies which have been removed from their connections and
* over. This is required so that we know when to remove an object * need to persist a while in case another process needs them.
* from the global list when it is removed from the list of objects
* vended on a particular connection.
*/ */
@interface CachedLocalObject : NSObject @interface CachedLocalObject : NSObject
{ {
id obj; NSDistantObject *obj;
int time; int time;
} }
- (BOOL) countdown; - (BOOL) countdown;
- (id) obj; - (NSDistantObject*) obj;
+ (id) newWithObject: (id)o time: (int)t; + (id) newWithObject: (NSDistantObject*)o time: (int)t;
@end @end
@implementation CachedLocalObject @implementation CachedLocalObject
+ (id) newWithObject: (id)o time: (int)t + (id) newWithObject: (NSDistantObject*)o time: (int)t
{ {
CachedLocalObject *item; CachedLocalObject *item;
@ -221,7 +180,7 @@ static unsigned local_object_counter = 0;
return NO; return NO;
} }
- (id) obj - (NSDistantObject*) obj
{ {
return obj; return obj;
} }
@ -338,10 +297,8 @@ setRootObjectForInPort(id anObj, NSPort *aPort)
F_UNLOCK(root_object_map_gate); F_UNLOCK(root_object_map_gate);
} }
static NSMapTable *objectToCounter = NULL;
static NSMapTable *targetToCounter = NULL;
static NSMapTable *targetToCached = NULL; static NSMapTable *targetToCached = NULL;
static NSLock *global_proxies_gate = nil; static NSLock *cached_proxies_gate = nil;
static BOOL multi_threaded = NO; static BOOL multi_threaded = NO;
@ -370,9 +327,9 @@ static BOOL multi_threaded = NO;
{ {
connection_table_gate = [NSLock new]; connection_table_gate = [NSLock new];
} }
if (global_proxies_gate == nil) if (cached_proxies_gate == nil)
{ {
global_proxies_gate = [NSLock new]; cached_proxies_gate = [NSLock new];
} }
if (root_object_map_gate == nil) if (root_object_map_gate == nil)
{ {
@ -579,7 +536,6 @@ static BOOL multi_threaded = NO;
connectionClass = self; connectionClass = self;
dateClass = [NSDate class]; dateClass = [NSDate class];
distantObjectClass = [NSDistantObject class]; distantObjectClass = [NSDistantObject class];
localCounterClass = [GSLocalCounter class];
sendCoderClass = [NSPortCoder class]; sendCoderClass = [NSPortCoder class];
recvCoderClass = [NSPortCoder class]; recvCoderClass = [NSPortCoder class];
runLoopClass = [NSRunLoop class]; runLoopClass = [NSRunLoop class];
@ -589,12 +545,6 @@ static BOOL multi_threaded = NO;
connection_table = connection_table =
NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0); NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0);
objectToCounter =
NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
NSObjectMapValueCallBacks, 0);
targetToCounter =
NSCreateMapTable(NSIntMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 0);
targetToCached = targetToCached =
NSCreateMapTable(NSIntMapKeyCallBacks, NSCreateMapTable(NSIntMapKeyCallBacks,
NSObjectMapValueCallBacks, 0); NSObjectMapValueCallBacks, 0);
@ -677,7 +627,7 @@ static BOOL multi_threaded = NO;
NSArray *cached_locals; NSArray *cached_locals;
int i; int i;
M_LOCK(global_proxies_gate); M_LOCK(cached_proxies_gate);
cached_locals = NSAllMapTableValues(targetToCached); cached_locals = NSAllMapTableValues(targetToCached);
for (i = [cached_locals count]; i > 0; i--) for (i = [cached_locals count]; i > 0; i--)
{ {
@ -685,8 +635,8 @@ static BOOL multi_threaded = NO;
if ([item countdown] == NO) if ([item countdown] == NO)
{ {
GSLocalCounter *counter = [item obj]; NSDistantObject *obj = [item obj];
NSMapRemove(targetToCached, (void*)counter->target); NSMapRemove(targetToCached, (void*)((ProxyStruct*)obj)->_handle);
} }
} }
if ([cached_locals count] == 0) if ([cached_locals count] == 0)
@ -694,7 +644,7 @@ static BOOL multi_threaded = NO;
[t invalidate]; [t invalidate];
timer = nil; timer = nil;
} }
M_UNLOCK(global_proxies_gate); M_UNLOCK(cached_proxies_gate);
} }
/** /**
@ -1215,8 +1165,9 @@ static BOOL multi_threaded = NO;
} }
/** /**
* Returns an array of all the local proxies to objects that * Returns an array of all the local objects that have proxies at the
* are retained by the remote connection. * remote end of the connection because they have been sent over the
* connection and not yet released by the far end.
*/ */
- (NSArray*) localObjects - (NSArray*) localObjects
{ {
@ -1322,7 +1273,7 @@ static BOOL multi_threaded = NO;
} }
/** /**
* Returns an array of proxies to all the remote objects known to * Returns an array of proxies to all the remote objects known to
* the NSConnection. * the NSConnection.
*/ */
- (NSArray *) remoteObjects - (NSArray *) remoteObjects
@ -2495,6 +2446,10 @@ static void callEncoder (DOContext *ctxt)
} }
NS_HANDLER NS_HANDLER
{ {
if (debug_connection > 3)
NSLog(@"forwarding exception for (0x%x) - %@",
(gsaddr)self, localException);
/* Send the exception back to the client. */ /* Send the exception back to the client. */
if (_isValid == YES) if (_isValid == YES)
{ {
@ -2672,7 +2627,7 @@ static void callEncoder (DOContext *ctxt)
[rmc decodeValueOfObjCType: @encode(unsigned) at: &target]; [rmc decodeValueOfObjCType: @encode(unsigned) at: &target];
[self _doneInRmc: rmc]; [self _doneInRmc: rmc];
p = [self includesLocalTarget: target]; p = [self includesLocalTarget: target];
o = ((ProxyStruct*)p)->_object; o = (p != nil) ? ((ProxyStruct*)p)->_object : nil;
/* xxx We should make sure that TARGET is a valid object. */ /* xxx We should make sure that TARGET is a valid object. */
/* Not actually a Proxy, but we avoid the warnings "id" would have made. */ /* Not actually a Proxy, but we avoid the warnings "id" would have made. */
@ -3106,55 +3061,41 @@ static void callEncoder (DOContext *ctxt)
/* Managing objects and proxies. */ /* Managing objects and proxies. */
- (void) addLocalObject: (NSDistantObject*)anObj - (void) addLocalObject: (NSDistantObject*)anObj
{ {
static unsigned local_object_counter = 0;
id object; id object;
unsigned target; unsigned target;
GSLocalCounter *counter;
GSIMapNode node; GSIMapNode node;
M_LOCK(_proxiesGate); M_LOCK(_proxiesGate);
M_LOCK(global_proxies_gate);
NSParameterAssert (_isValid); NSParameterAssert (_isValid);
object = ((ProxyStruct*)anObj)->_object;
target = ((ProxyStruct*)anObj)->_handle;
/*
* If there is no target allocated to the proxy, we add one.
*/
if (target == 0)
{
((ProxyStruct*)anObj)->_handle = target = ++local_object_counter;
}
/* /*
* Record the value in the _localObjects map, retaining it. * Record the value in the _localObjects map, retaining it.
*/ */
object = ((ProxyStruct*)anObj)->_object;
node = GSIMapNodeForKey(_localObjects, (GSIMapKey)object); node = GSIMapNodeForKey(_localObjects, (GSIMapKey)object);
IF_NO_GC(RETAIN(anObj)); NSAssert(node == 0, NSInternalInconsistencyException);
if (node == 0) node = GSIMapNodeForKey(_localTargets, (GSIMapKey)target);
{ NSAssert(node == 0, NSInternalInconsistencyException);
GSIMapAddPair(_localObjects, (GSIMapKey)object, (GSIMapVal)anObj);
}
else
{
RELEASE(node->value.obj);
node->value.obj = anObj;
}
/* RETAIN(anObj);
* Keep track of local objects accross all connections. GSIMapAddPair(_localObjects, (GSIMapKey)object, (GSIMapVal)anObj);
*/
counter = NSMapGet(objectToCounter, (void*)object);
if (counter)
{
counter->ref++;
target = counter->target;
}
else
{
counter = [localCounterClass newWithObject: object];
target = counter->target;
NSMapInsert(objectToCounter, (void*)object, counter);
NSMapInsert(targetToCounter, (void*)target, counter);
RELEASE(counter);
}
((ProxyStruct*)anObj)->_handle = target;
GSIMapAddPair(_localTargets, (GSIMapKey)target, (GSIMapVal)anObj); GSIMapAddPair(_localTargets, (GSIMapKey)target, (GSIMapVal)anObj);
if (debug_connection > 2) if (debug_connection > 2)
NSLog(@"add local object (0x%x) target (0x%x) " NSLog(@"add local object (0x%x) target (0x%x) "
@"to connection (0x%x) (ref %d)", @"to connection (0x%x)", (gsaddr)object, target, (gsaddr) self);
(gsaddr)object, target, (gsaddr) self, counter->ref);
M_UNLOCK(global_proxies_gate);
M_UNLOCK(_proxiesGate); M_UNLOCK(_proxiesGate);
} }
@ -3183,60 +3124,46 @@ static void callEncoder (DOContext *ctxt)
{ {
NSDistantObject *prox; NSDistantObject *prox;
unsigned target; unsigned target;
GSLocalCounter *counter;
unsigned val = 0; unsigned val = 0;
GSIMapNode node; GSIMapNode node;
M_LOCK(global_proxies_gate);
M_LOCK(_proxiesGate); M_LOCK(_proxiesGate);
node = GSIMapNodeForKey(_localObjects, (GSIMapKey)anObj); node = GSIMapNodeForKey(_localObjects, (GSIMapKey)anObj);
if (node == 0) if (node == 0)
{ {
M_UNLOCK(_proxiesGate); M_UNLOCK(_proxiesGate);
M_UNLOCK(global_proxies_gate);
[NSException raise: NSInternalInconsistencyException [NSException raise: NSInternalInconsistencyException
format: @"Attempt to remove non-existent local %@", anObj]; format: @"Attempt to remove non-existent local %@", anObj];
} }
prox = node->value.obj; prox = node->value.obj;
target = ((ProxyStruct*)prox)->_handle; target = ((ProxyStruct*)prox)->_handle;
/* if (1)
* If all references to a local proxy have gone - remove the
* global reference as well.
*/
counter = NSMapGet(objectToCounter, (void*)anObj);
if (counter)
{ {
counter->ref--; id item;
if ((val = counter->ref) == 0)
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 (timer == nil)
{ {
if (1) timer = [NSTimer scheduledTimerWithTimeInterval: 1.0
{ target: connectionClass
id item; selector: @selector(_timeout:)
/* userInfo: nil
* If this proxy has been vended onwards by another process, we repeats: YES];
* need to keep a reference to the local object around for a
* while in case that other process needs it.
*/
if (timer == nil)
{
timer = [NSTimer scheduledTimerWithTimeInterval: 1.0
target: connectionClass
selector: @selector(_timeout:)
userInfo: nil
repeats: YES];
}
item = [CachedLocalObject newWithObject: counter time: 30];
NSMapInsert(targetToCached, (void*)target, item);
RELEASE(item);
if (debug_connection > 3)
NSLog(@"placed local object (0x%x) target (0x%x) in cache",
(gsaddr)anObj, target);
}
NSMapRemove(objectToCounter, (void*)anObj);
NSMapRemove(targetToCounter, (void*)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);
} }
/* /*
@ -3256,7 +3183,6 @@ static void callEncoder (DOContext *ctxt)
(gsaddr)anObj, target, (gsaddr)self, val); (gsaddr)anObj, target, (gsaddr)self, val);
M_UNLOCK(_proxiesGate); M_UNLOCK(_proxiesGate);
M_UNLOCK(global_proxies_gate);
} }
- (void) _release_targets: (unsigned*)list count: (unsigned)number - (void) _release_targets: (unsigned*)list count: (unsigned)number
@ -3302,64 +3228,79 @@ static void callEncoder (DOContext *ctxt)
NSDistantObject *proxy = nil; NSDistantObject *proxy = nil;
GSIMapNode node; GSIMapNode node;
M_LOCK(global_proxies_gate); M_LOCK(_proxiesGate);
node = GSIMapNodeForKey(_localTargets, (GSIMapKey)target); node = GSIMapNodeForKey(_localTargets, (GSIMapKey)target);
if (node != 0) if (node != 0)
{ {
proxy = node->value.obj; proxy = node->value.obj;
M_UNLOCK(global_proxies_gate);
} }
else
/*
* If not found in the current connection, try all other existing
* connections.
*/
if (proxy == nil)
{ {
GSLocalCounter *counter; NSHashEnumerator enumerator;
NSConnection *c;
counter = NSMapGet (targetToCounter, (void*)target); M_LOCK(connection_table_gate);
if (counter == nil) enumerator = NSEnumerateHashTable(connection_table);
while (proxy == nil
&& (c = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil)
{ {
CachedLocalObject *cached; if (c != self && [c isValid] == YES)
/*
* 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 main maps so we can
* retain it on this connection.
*/
cached = NSMapGet (targetToCached, (void*)target);
if (cached != nil)
{ {
unsigned t; M_LOCK(c->_proxiesGate);
id o; node = GSIMapNodeForKey(c->_localTargets, (GSIMapKey)target);
if (node != 0)
counter = [cached obj]; {
proxy = node->value.obj;
t = counter->target; /*
o = counter->object; * Found in another connection ... add to this one.
*/
NSMapInsert(objectToCounter, (void*)o, counter); [self addLocalObject: proxy];
NSMapInsert(targetToCounter, (void*)t, counter); }
NSMapRemove(targetToCached, (void*)t); M_UNLOCK(c->_proxiesGate);
if (debug_connection > 3)
NSLog(@"target (0x%x) moved from cache", target);
} }
} }
RETAIN(counter); NSEndHashTableEnumeration(&enumerator);
M_UNLOCK(global_proxies_gate); M_UNLOCK(connection_table_gate);
if (counter == nil) }
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)
{ {
if(debug_connection > 3) proxy = [cached obj];
NSLog(@"target (0x%x) not found anywhere", target); /*
} * Found in cache ... add to this connection.
else */
{ ASSIGN(((ProxyStruct*)proxy)->_connection, self);
NSAssert([counter isKindOfClass: [GSLocalCounter class]], [self addLocalObject: proxy];
@"Local counter is wrong kind of class."); NSMapRemove(targetToCached, (void*)target);
proxy = [distantObjectClass proxyWithLocal: counter->object
connection: self];
if (debug_connection > 3) if (debug_connection > 3)
NSLog(@"retained object (0x%x) target (0x%x) on connection(0x%x)", NSLog(@"target (0x%x) moved from cache", target);
counter->object, counter->target, self);
RELEASE(counter);
} }
M_UNLOCK(cached_proxies_gate);
}
M_UNLOCK(_proxiesGate);
if (proxy == nil)
{
if (debug_connection > 3)
NSLog(@"target (0x%x) not found anywhere", target);
} }
return proxy; return proxy;
} }
@ -3369,8 +3310,8 @@ static void callEncoder (DOContext *ctxt)
NS_DURING NS_DURING
{ {
/* /*
* Tell the remote app that it must retain the local object * Tell the remote app that it must retain the local object
* for the target on this connection. * for the target on this connection.
*/ */
if (_receivePort && _isValid) if (_receivePort && _isValid)
{ {
@ -3442,7 +3383,7 @@ static void callEncoder (DOContext *ctxt)
return p; return p;
} }
- (void) addProxy: (NSDistantObject*) aProxy - (void) addProxy: (NSDistantObject*)aProxy
{ {
unsigned target; unsigned target;
GSIMapNode node; GSIMapNode node;

View file

@ -726,15 +726,10 @@ enum
return new_proxy; return new_proxy;
} }
/* _object = RETAIN(anObject);
* We don't need to retain the object here - the connection
* will retain the proxies local object if necessary (and release it
* when all proxies referring to it have been released).
*/
_object = anObject;
/* /*
* We register this proxy with the connection using it. * We register this proxy with the connection using it.
*/ */
_connection = RETAIN(aConnection); _connection = RETAIN(aConnection);
[_connection addLocalObject: self]; [_connection addLocalObject: self];
@ -831,7 +826,7 @@ enum
return sig; return sig;
} }
/* /*
* Simlarly, when we fetch a method signature form the remote end, * Simlarly, when we fetch a method signature from the remote end,
* we get a proxy, and when we build a local signature we need to * we get a proxy, and when we build a local signature we need to
* ask the proxy for its types ... and must avoid recursion again. * ask the proxy for its types ... and must avoid recursion again.
*/ */
@ -986,19 +981,21 @@ enum
NSLog(@"retain count for connection (0x%x) is now %u\n", NSLog(@"retain count for connection (0x%x) is now %u\n",
(gsaddr)_connection, [_connection retainCount]); (gsaddr)_connection, [_connection retainCount]);
/* /*
* A proxy for local object does not retain it's target - the * A proxy for local object retains its target - so we release it.
* NSConnection class does that for us - so we need not release it.
* For a local object the connection also retains this proxy, so we * For a local object the connection also retains this proxy, so we
* can't be deallocated unless we are already removed from the * can't be deallocated unless we are already removed from the
* connection. * connection, and there is no need to remove self from connection.
* *
* A proxy retains it's connection so that the connection will * A proxy has a nil local object, and retains it's connection so
* continue to exist as long as there is a something to use it. * that the connection will continue to exist as long as there is
* a something to use it.
* So we release our reference to the connection here just as soon * So we release our reference to the connection here just as soon
* as we have removed ourself from the connection. * as we have removed ourself from the connection.
*/ */
if (_object == nil) if (_object == nil)
[_connection removeProxy: self]; [_connection removeProxy: self];
else
DESTROY(_object);
RELEASE(_connection); RELEASE(_connection);
} }
} }