diff --git a/ChangeLog b/ChangeLog index 76d82aebf..25e086d19 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +Tue Oct 21 18:00:00 2003 Richard Frith-Macdonald + + * 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 * Testing/nsconnection_client.m: Add -r option. diff --git a/Headers/Foundation/NSDistantObject.h b/Headers/Foundation/NSDistantObject.h index 5538abcec..5d7d82aef 100644 --- a/Headers/Foundation/NSDistantObject.h +++ b/Headers/Foundation/NSDistantObject.h @@ -36,6 +36,7 @@ id _object; unsigned _handle; Protocol *_protocol; + unsigned _counter; } + (NSDistantObject*) proxyWithLocal: (id)anObject diff --git a/Source/NSConnection.m b/Source/NSConnection.m index ae2091a42..805f95dcb 100644 --- a/Source/NSConnection.m +++ b/Source/NSConnection.m @@ -104,7 +104,6 @@ static id dummyObject; static Class connectionClass; static Class dateClass; static Class distantObjectClass; -static Class localCounterClass; static Class sendCoderClass; static Class recvCoderClass; 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 - * 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. + * CachedLocalObject is a trivial class to keep track of local + * proxies which have been removed from their connections and + * need to persist a while in case another process needs them. */ @interface CachedLocalObject : NSObject { - id obj; - int time; + NSDistantObject *obj; + int time; } - (BOOL) countdown; -- (id) obj; -+ (id) newWithObject: (id)o time: (int)t; +- (NSDistantObject*) obj; ++ (id) newWithObject: (NSDistantObject*)o time: (int)t; @end @implementation CachedLocalObject -+ (id) newWithObject: (id)o time: (int)t ++ (id) newWithObject: (NSDistantObject*)o time: (int)t { CachedLocalObject *item; @@ -221,7 +180,7 @@ static unsigned local_object_counter = 0; return NO; } -- (id) obj +- (NSDistantObject*) obj { return obj; } @@ -338,10 +297,8 @@ setRootObjectForInPort(id anObj, NSPort *aPort) F_UNLOCK(root_object_map_gate); } -static NSMapTable *objectToCounter = NULL; -static NSMapTable *targetToCounter = NULL; static NSMapTable *targetToCached = NULL; -static NSLock *global_proxies_gate = nil; +static NSLock *cached_proxies_gate = nil; static BOOL multi_threaded = NO; @@ -370,9 +327,9 @@ static BOOL multi_threaded = NO; { 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) { @@ -579,7 +536,6 @@ static BOOL multi_threaded = NO; connectionClass = self; dateClass = [NSDate class]; distantObjectClass = [NSDistantObject class]; - localCounterClass = [GSLocalCounter class]; sendCoderClass = [NSPortCoder class]; recvCoderClass = [NSPortCoder class]; runLoopClass = [NSRunLoop class]; @@ -589,12 +545,6 @@ static BOOL multi_threaded = NO; connection_table = NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0); - objectToCounter = - NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, - NSObjectMapValueCallBacks, 0); - targetToCounter = - NSCreateMapTable(NSIntMapKeyCallBacks, - NSNonOwnedPointerMapValueCallBacks, 0); targetToCached = NSCreateMapTable(NSIntMapKeyCallBacks, NSObjectMapValueCallBacks, 0); @@ -677,7 +627,7 @@ static BOOL multi_threaded = NO; NSArray *cached_locals; int i; - M_LOCK(global_proxies_gate); + M_LOCK(cached_proxies_gate); cached_locals = NSAllMapTableValues(targetToCached); for (i = [cached_locals count]; i > 0; i--) { @@ -685,8 +635,8 @@ static BOOL multi_threaded = NO; if ([item countdown] == NO) { - GSLocalCounter *counter = [item obj]; - NSMapRemove(targetToCached, (void*)counter->target); + NSDistantObject *obj = [item obj]; + NSMapRemove(targetToCached, (void*)((ProxyStruct*)obj)->_handle); } } if ([cached_locals count] == 0) @@ -694,7 +644,7 @@ static BOOL multi_threaded = NO; [t invalidate]; 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 - * are retained by the remote connection. + * Returns an array of all the local objects that have proxies at the + * remote end of the connection because they have been sent over the + * connection and not yet released by the far end. */ - (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. */ - (NSArray *) remoteObjects @@ -2495,6 +2446,10 @@ static void callEncoder (DOContext *ctxt) } NS_HANDLER { + if (debug_connection > 3) + NSLog(@"forwarding exception for (0x%x) - %@", + (gsaddr)self, localException); + /* Send the exception back to the client. */ if (_isValid == YES) { @@ -2672,7 +2627,7 @@ static void callEncoder (DOContext *ctxt) [rmc decodeValueOfObjCType: @encode(unsigned) at: &target]; [self _doneInRmc: rmc]; 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. */ /* 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. */ - (void) addLocalObject: (NSDistantObject*)anObj { + static unsigned local_object_counter = 0; id object; unsigned target; - GSLocalCounter *counter; GSIMapNode node; M_LOCK(_proxiesGate); - M_LOCK(global_proxies_gate); 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. */ - object = ((ProxyStruct*)anObj)->_object; node = GSIMapNodeForKey(_localObjects, (GSIMapKey)object); - IF_NO_GC(RETAIN(anObj)); - if (node == 0) - { - GSIMapAddPair(_localObjects, (GSIMapKey)object, (GSIMapVal)anObj); - } - else - { - RELEASE(node->value.obj); - node->value.obj = anObj; - } + NSAssert(node == 0, NSInternalInconsistencyException); + node = GSIMapNodeForKey(_localTargets, (GSIMapKey)target); + NSAssert(node == 0, NSInternalInconsistencyException); - /* - * Keep track of local objects accross all connections. - */ - 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; + RETAIN(anObj); + GSIMapAddPair(_localObjects, (GSIMapKey)object, (GSIMapVal)anObj); GSIMapAddPair(_localTargets, (GSIMapKey)target, (GSIMapVal)anObj); + if (debug_connection > 2) NSLog(@"add local object (0x%x) target (0x%x) " - @"to connection (0x%x) (ref %d)", - (gsaddr)object, target, (gsaddr) self, counter->ref); - M_UNLOCK(global_proxies_gate); + @"to connection (0x%x)", (gsaddr)object, target, (gsaddr) self); + M_UNLOCK(_proxiesGate); } @@ -3183,60 +3124,46 @@ static void callEncoder (DOContext *ctxt) { NSDistantObject *prox; unsigned target; - GSLocalCounter *counter; unsigned val = 0; GSIMapNode node; - M_LOCK(global_proxies_gate); M_LOCK(_proxiesGate); node = GSIMapNodeForKey(_localObjects, (GSIMapKey)anObj); if (node == 0) { M_UNLOCK(_proxiesGate); - M_UNLOCK(global_proxies_gate); [NSException raise: NSInternalInconsistencyException format: @"Attempt to remove non-existent local %@", anObj]; } prox = node->value.obj; target = ((ProxyStruct*)prox)->_handle; - /* - * If all references to a local proxy have gone - remove the - * global reference as well. - */ - counter = NSMapGet(objectToCounter, (void*)anObj); - if (counter) + if (1) { - counter->ref--; - if ((val = counter->ref) == 0) + id item; + + 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) - { - id item; - /* - * 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) - { - 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); + 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); } /* @@ -3256,7 +3183,6 @@ static void callEncoder (DOContext *ctxt) (gsaddr)anObj, target, (gsaddr)self, val); M_UNLOCK(_proxiesGate); - M_UNLOCK(global_proxies_gate); } - (void) _release_targets: (unsigned*)list count: (unsigned)number @@ -3302,64 +3228,79 @@ static void callEncoder (DOContext *ctxt) NSDistantObject *proxy = nil; GSIMapNode node; - M_LOCK(global_proxies_gate); + M_LOCK(_proxiesGate); node = GSIMapNodeForKey(_localTargets, (GSIMapKey)target); if (node != 0) { 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); - if (counter == nil) + M_LOCK(connection_table_gate); + enumerator = NSEnumerateHashTable(connection_table); + while (proxy == nil + && (c = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != 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 main maps so we can - * retain it on this connection. - */ - cached = NSMapGet (targetToCached, (void*)target); - if (cached != nil) + if (c != self && [c isValid] == YES) { - unsigned t; - id o; - - counter = [cached obj]; - - t = counter->target; - o = counter->object; - - NSMapInsert(objectToCounter, (void*)o, counter); - NSMapInsert(targetToCounter, (void*)t, counter); - NSMapRemove(targetToCached, (void*)t); - if (debug_connection > 3) - NSLog(@"target (0x%x) moved from cache", target); + M_LOCK(c->_proxiesGate); + node = GSIMapNodeForKey(c->_localTargets, (GSIMapKey)target); + if (node != 0) + { + proxy = node->value.obj; + /* + * Found in another connection ... add to this one. + */ + [self addLocalObject: proxy]; + } + M_UNLOCK(c->_proxiesGate); } } - RETAIN(counter); - M_UNLOCK(global_proxies_gate); - if (counter == nil) + NSEndHashTableEnumeration(&enumerator); + 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) { - if(debug_connection > 3) - NSLog(@"target (0x%x) not found anywhere", target); - } - else - { - NSAssert([counter isKindOfClass: [GSLocalCounter class]], - @"Local counter is wrong kind of class."); - proxy = [distantObjectClass proxyWithLocal: counter->object - connection: self]; + 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(@"retained object (0x%x) target (0x%x) on connection(0x%x)", - counter->object, counter->target, self); - RELEASE(counter); + NSLog(@"target (0x%x) moved from cache", target); } + 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; } @@ -3369,8 +3310,8 @@ static void callEncoder (DOContext *ctxt) NS_DURING { /* - * Tell the remote app that it must retain the local object - * for the target on this connection. + * Tell the remote app that it must retain the local object + * for the target on this connection. */ if (_receivePort && _isValid) { @@ -3442,7 +3383,7 @@ static void callEncoder (DOContext *ctxt) return p; } -- (void) addProxy: (NSDistantObject*) aProxy +- (void) addProxy: (NSDistantObject*)aProxy { unsigned target; GSIMapNode node; diff --git a/Source/NSDistantObject.m b/Source/NSDistantObject.m index 20033cb5e..c2f5356c0 100644 --- a/Source/NSDistantObject.m +++ b/Source/NSDistantObject.m @@ -726,15 +726,10 @@ enum return new_proxy; } - /* - * 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; + _object = RETAIN(anObject); /* - * We register this proxy with the connection using it. + * We register this proxy with the connection using it. */ _connection = RETAIN(aConnection); [_connection addLocalObject: self]; @@ -831,7 +826,7 @@ enum 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 * 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", (gsaddr)_connection, [_connection retainCount]); /* - * A proxy for local object does not retain it's target - the - * NSConnection class does that for us - so we need not release it. + * A proxy for local object retains its target - so we release it. * For a local object the connection also retains this proxy, so we * 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 - * continue to exist as long as there is a something to use it. + * A proxy has a nil local object, and retains it's connection so + * 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 * as we have removed ourself from the connection. */ if (_object == nil) [_connection removeProxy: self]; + else + DESTROY(_object); RELEASE(_connection); } }