58
56
#include "callframe.h"
61
#include <Foundation/NSPortCoder.h>
62
#include <base/DistributedObjects.h>
64
#include <Foundation/NSHashTable.h>
65
#include <Foundation/NSMapTable.h>
66
#include <Foundation/NSData.h>
67
#include <Foundation/NSRunLoop.h>
68
#include <Foundation/NSArray.h>
69
#include <Foundation/NSDictionary.h>
70
#include <Foundation/NSValue.h>
71
#include <Foundation/NSString.h>
72
#include <Foundation/NSDate.h>
73
#include <Foundation/NSException.h>
74
#include <Foundation/NSLock.h>
75
#include <Foundation/NSThread.h>
76
#include <Foundation/NSPort.h>
77
#include <Foundation/NSPortMessage.h>
78
#include <Foundation/NSPortNameServer.h>
79
#include <Foundation/NSNotification.h>
80
#include <Foundation/NSDebug.h>
81
#include <base/GSInvocation.h>
59
#include "Foundation/NSPortCoder.h"
60
#include "GNUstepBase/DistributedObjects.h"
62
#include "Foundation/NSHashTable.h"
63
#include "Foundation/NSMapTable.h"
64
#include "Foundation/NSData.h"
65
#include "Foundation/NSRunLoop.h"
66
#include "Foundation/NSArray.h"
67
#include "Foundation/NSDictionary.h"
68
#include "Foundation/NSValue.h"
69
#include "Foundation/NSString.h"
70
#include "Foundation/NSDate.h"
71
#include "Foundation/NSException.h"
72
#include "Foundation/NSLock.h"
73
#include "Foundation/NSThread.h"
74
#include "Foundation/NSPort.h"
75
#include "Foundation/NSPortMessage.h"
76
#include "Foundation/NSPortNameServer.h"
77
#include "Foundation/NSNotification.h"
78
#include "Foundation/NSDebug.h"
79
#include "GSInvocation.h"
85
extern NSRunLoop *GSRunLoopForThread(NSThread*);
83
87
#define F_LOCK(X) {NSDebugFLLog(@"GSConnection",@"Lock %@",X);[X lock];}
84
88
#define F_UNLOCK(X) {NSDebugFLLog(@"GSConnection",@"Unlock %@",X);[X unlock];}
85
89
#define M_LOCK(X) {NSDebugMLLog(@"GSConnection",@"Lock %@",X);[X lock];}
86
90
#define M_UNLOCK(X) {NSDebugMLLog(@"GSConnection",@"Unlock %@",X);[X unlock];}
92
NSString * const NSFailedAuthenticationException =
93
@"NSFailedAuthenticationExceptions";
94
NSString * const NSObjectInaccessibleException =
95
@"NSObjectInaccessibleException";
89
98
* Set up a type to permit us to have direct access into an NSDistantObject
138
* GSLocalCounter is a trivial class to keep track of how
139
* many different connections a particular local object is vended
140
* over. This is required so that we know when to remove an object
141
* from the global list when it is removed from the list of objects
142
* vended on a particular connection.
144
@interface GSLocalCounter : NSObject
151
+ (id) newWithObject: (id)ob;
154
@implementation GSLocalCounter
156
static unsigned local_object_counter = 0;
158
+ (id) newWithObject: (id)obj
160
GSLocalCounter *counter;
162
counter = (GSLocalCounter*)NSAllocateObject(self, 0, NSDefaultMallocZone());
164
counter->object = RETAIN(obj);
165
counter->target = ++local_object_counter;
171
NSDeallocateObject(self);
178
* CachedLocalObject is a trivial class to keep track of how
179
* many different connections a particular local object is vended
180
* over. This is required so that we know when to remove an object
181
* from the global list when it is removed from the list of objects
182
* vended on a particular connection.
148
* CachedLocalObject is a trivial class to keep track of local
149
* proxies which have been removed from their connections and
150
* need to persist a while in case another process needs them.
184
152
@interface CachedLocalObject : NSObject
154
NSDistantObject *obj;
189
157
- (BOOL) countdown;
191
+ (id) newWithObject: (id)o time: (int)t;
158
- (NSDistantObject*) obj;
159
+ (id) newWithObject: (NSDistantObject*)o time: (int)t;
194
162
@implementation CachedLocalObject
196
+ (id) newWithObject: (id)o time: (int)t
164
+ (id) newWithObject: (NSDistantObject*)o time: (int)t
198
166
CachedLocalObject *item;
330
300
F_UNLOCK(root_object_map_gate);
333
static NSMapTable *objectToCounter = NULL;
334
static NSMapTable *targetToCounter = NULL;
335
303
static NSMapTable *targetToCached = NULL;
336
static NSLock *global_proxies_gate = nil;
304
static NSLock *cached_proxies_gate = nil;
338
static BOOL multi_threaded = NO;
310
* NSConnection objects are used to manage communications between
311
* objects in different processes, in different machines, or in
342
314
@implementation NSConnection
345
* When the system becomes multithreaded, we set a flag to say so and
346
* make sure that connection locking is enabled.
317
* Returns an array containing all the NSConnection objects known to
318
* the system. These connections will be valid at the time that the
319
* array was created, but may be invalidated by other threads
320
* before you get to examine the array.
348
+ (void) _becomeThreaded: (NSNotification*)notification
350
if (multi_threaded == NO)
352
NSHashEnumerator enumerator;
355
multi_threaded = YES;
356
if (connection_table_gate == nil)
358
connection_table_gate = [NSLock new];
360
if (global_proxies_gate == nil)
362
global_proxies_gate = [NSLock new];
364
if (root_object_map_gate == nil)
366
root_object_map_gate = [NSLock new];
368
enumerator = NSEnumerateHashTable(connection_table);
369
while ((c = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil)
371
if (c->_refGate == nil)
373
c->_refGate = [NSRecursiveLock new];
377
[[NSNotificationCenter defaultCenter]
379
name: NSWillBecomeMultiThreadedNotification
383
322
+ (NSArray*) allConnections
528
515
root_object_map =
529
516
NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
530
517
NSObjectMapValueCallBacks, 0);
531
if ([NSThread isMultiThreaded])
533
[self _becomeThreaded: nil];
537
[[NSNotificationCenter defaultCenter]
539
selector: @selector(_becomeThreaded:)
540
name: NSWillBecomeMultiThreadedNotification
519
if (connection_table_gate == nil)
521
connection_table_gate = [GSLazyLock new];
523
if (cached_proxies_gate == nil)
525
cached_proxies_gate = [GSLazyLock new];
527
if (root_object_map_gate == nil)
529
root_object_map_gate = [GSLazyLock new];
533
* When any thread exits, we must check to see if we are using its
534
* runloop, and remove ourselves from it if necessary.
536
nc = [NSNotificationCenter defaultCenter];
537
[nc addObserver: self
538
selector: @selector(_threadWillExit:)
539
name: NSThreadWillExitNotification
545
* Undocumented feature for compatibility with OPENSTEP/MacOS-X
546
* +new returns the default connection.
549
* Undocumented feature of OPENSTEP/MacOS-X
550
* +new returns the default connection.
552
550
return RETAIN([self defaultConnection]);
555
* +rootProxyForConnectionWithRegisteredName:host:usingNameServer:
556
* to return a proxy for a root object on the remote connection with
557
* the send port registered under name n on host h.
555
559
+ (NSDistantObject*) rootProxyForConnectionWithRegisteredName: (NSString*)n
556
560
host: (NSString*)h
682
* Returns the delegate of the NSConnection.
661
686
return GS_GC_UNHIDE(_delegate);
690
* Sets the NSConnection configuration so that multiple threads may
691
* use the connection to send requests to the remote connection.<br />
692
* This option is inherited by child connections.<br />
693
* NB. A connection with multiple threads enabled will run slower than
694
* a normal connection.
664
696
- (void) enableMultipleThreads
666
698
_multipleThreads = YES;
702
* Returns YES if the NSConnection is configured to
703
* handle remote messages atomically, NO otherwise.<br />
704
* This option is inherited by child connections.
669
706
- (BOOL) independentConversationQueueing
671
708
return _independentQueueing;
712
* Return a connection able to act as a server receive incoming requests.
677
* Undocumented feature of OPENSTEP/MacOS-X
678
* -init returns the default connection.
681
return RETAIN([connectionClass defaultConnection]);
716
NSPort *port = [NSPort port];
718
self = [self initWithReceivePort: port sendPort: nil];
684
/* This is the designated initializer for NSConnection */
723
* Initialises an NSConnection with the receive port r and the
725
* Behavior varies with the port values as follows -
727
* <term>r is <code>nil</code></term>
729
* The NSConnection is released and the method returns
732
* <term>s is <code>nil</code></term>
734
* The NSConnection uses r as the send port as
735
* well as the receive port.
737
* <term>s is the same as r</term>
739
* The NSConnection is usable only for vending objects.
741
* <term>A connection with the same ports exists</term>
743
* The new connection is released and the old connection
744
* is retained and returned.
746
* <term>A connection with the same ports (swapped) exists</term>
748
* The new connection is initialised as normal, and will
749
* communicate with the old connection.
753
* If a connection exists whose send and receive ports are
754
* both the same as the new connections receive port, that
755
* existing connection is deemed to be the parent of the
756
* new connection. The new connection inherits configuration
757
* information from the parent, and the delegate of the
758
* parent has a chance to adjust ythe configuration of the
759
* new connection or veto its creation.
761
* NSConnectionDidInitializeNotification is posted once a new
762
* connection is initialised.
685
765
- (id) initWithReceivePort: (NSPort*)r
686
766
sendPort: (NSPort*)s
1284
1485
M_UNLOCK(_refGate);
1489
* Sets the time interval that the NSConnection will wait to send
1490
* one of its requests before raising an NSPortTimeoutException.
1287
1492
- (void) setRequestTimeout: (NSTimeInterval)to
1289
1494
_requestTimeout = to;
1498
* Sets the root object that is vended by the connection.
1292
1500
- (void) setRootObject: (id)anObj
1294
1502
setRootObjectForInPort(anObj, _receivePort);
1506
* Returns an object containing various statistics for the
1509
* On GNUstep the dictionary contains -
1511
* <term>NSConnectionRepliesReceived</term>
1513
* The number of messages replied to by the remote NSConnection.
1515
* <term>NSConnectionRepliesSent</term>
1517
* The number of replies sent to the remote NSConnection.
1519
* <term>NSConnectionRequestsReceived</term>
1521
* The number of messages recieved from the remote NSConnection.
1523
* <term>NSConnectionRequestsSent</term>
1525
* The number of messages sent to the remote NSConnection.
1527
* <term>NSConnectionLocalCount</term>
1529
* The number of local objects currently vended.
1531
* <term>NSConnectionProxyCount</term>
1533
* The number of remote objects currently in use.
1297
1537
- (NSDictionary*) statistics
1299
1539
NSMutableDictionary *d;
2255
2541
NSLog(@"looking to retain local object with target (0x%x) on (0x%x)",
2256
2542
target, (gsaddr)self);
2258
if ([self includesLocalTarget: target] == nil)
2260
GSLocalCounter *counter;
2262
M_LOCK(global_proxies_gate);
2263
counter = NSMapGet (targetToCounter, (void*)target);
2267
* If the target doesn't exist for any connection, but still
2268
* persists in the cache (ie it was recently released) then
2269
* we move it back from the cache to the main maps so we can
2270
* retain it on this connection.
2272
counter = NSMapGet (targetToCached, (void*)target);
2275
unsigned t = counter->target;
2276
id o = counter->object;
2278
NSMapInsert(objectToCounter, (void*)o, counter);
2279
NSMapInsert(targetToCounter, (void*)t, counter);
2280
NSMapRemove(targetToCached, (void*)t);
2281
if (debug_connection > 3)
2282
NSLog(@"target (0x%x) moved from cache", target);
2285
M_UNLOCK(global_proxies_gate);
2288
[op encodeObject: @"target not found anywhere"];
2289
if (debug_connection > 3)
2290
NSLog(@"target (0x%x) not found anywhere for retain", target);
2294
[distantObjectClass proxyWithLocal: counter->object
2296
[op encodeObject: nil];
2297
if (debug_connection > 3)
2298
NSLog(@"retained object (0x%x) target (0x%x) on connection(0x%x)",
2299
counter->object, counter->target, self);
2304
[op encodeObject: nil];
2305
if (debug_connection > 3)
2306
NSLog(@"target (0x%x) already retained on connection (0x%x)",
2544
M_LOCK(_proxiesGate);
2545
local = [self locateLocalTarget: target];
2548
response = @"target not found anywhere";
2552
((ProxyStruct*)local)->_counter++; // Vended on connection.
2554
M_UNLOCK(_proxiesGate);
2556
[op encodeObject: response];
2310
2557
[self _sendOutRmc: op type: RETAIN_REPLY];
2377
2624
- _getReplyRmc: (int)sn
2379
2626
NSPortCoder *rmc;
2627
GSIMapNode node = 0;
2381
2628
NSDate *timeout_date = nil;
2382
2629
NSTimeInterval last_interval = 0.0001;
2383
2630
NSTimeInterval delay_interval = last_interval;
2384
2631
NSDate *delay_date = nil;
2385
NSRunLoop *runLoop = [runLoopClass currentRunLoop];
2632
NSThread *thread = GSCurrentThread();
2633
NSRunLoop *runLoop = GSRunLoopForThread(thread);
2387
if (debug_connection > 5)
2388
NSLog(@"Waiting for reply sequence %d on %x:%x",
2389
sn, self, [NSThread currentThread]);
2391
while ((node = GSIMapNodeForKey(_replyMap, (GSIMapKey)sn)) != 0
2392
&& node->value.obj == dummyObject)
2637
* If we have sent out a request on a run loop that we don't already
2638
* know about, it must be on a new thread - so if we have multipleThreads
2639
* enabled, we must add the run loop of the new thread so that we can
2640
* get the reply in this thread.
2642
if ([_runLoops indexOfObjectIdenticalTo: runLoop] == NSNotFound)
2394
M_UNLOCK(_queueGate);
2395
if (timeout_date == nil)
2397
timeout_date = [dateClass allocWithZone: NSDefaultMallocZone()];
2399
= [timeout_date initWithTimeIntervalSinceNow: _replyTimeout];
2401
2644
if (_multipleThreads == YES)
2404
NSTimeInterval next_interval;
2646
[self addRunLoop: runLoop];
2650
[NSException raise: NSObjectInaccessibleException
2651
format: @"Waiting for reply in wrong thread"];
2407
* If multiple threads are using this connections, another
2408
* thread may read the reply we are waiting for - so we must
2409
* break out of the runloop frequently to check. We do this
2410
* by setting a small delay and increasing it each time round
2411
* so that this semi-busy wait doesn't consume too much
2412
* processor time (I hope).
2413
* We set an upper limit on the delay to avoid responsiveness
2416
RELEASE(delay_date);
2417
delay_date = [dateClass allocWithZone: NSDefaultMallocZone()];
2418
if (delay_interval < 1.0)
2657
if (debug_connection > 5)
2658
NSLog(@"Waiting for reply sequence %d on %x:%x",
2659
sn, self, [NSThread currentThread]);
2660
M_LOCK(_queueGate); isLocked = YES;
2661
while (_isValid == YES
2662
&& (node = GSIMapNodeForKey(_replyMap, (GSIMapKey)sn)) != 0
2663
&& node->value.obj == dummyObject)
2665
M_UNLOCK(_queueGate); isLocked = NO;
2666
if (timeout_date == nil)
2420
next_interval = last_interval + delay_interval;
2421
last_interval = delay_interval;
2422
delay_interval = next_interval;
2668
timeout_date = [dateClass allocWithZone: NSDefaultMallocZone()];
2670
= [timeout_date initWithTimeIntervalSinceNow: _replyTimeout];
2425
= [delay_date initWithTimeIntervalSinceNow: delay_interval];
2428
* We must not set a delay date that is further in the future
2429
* than the timeout date for the response to be returned.
2431
if ([timeout_date earlierDate: delay_date] == timeout_date)
2672
if (_multipleThreads == YES)
2433
limit_date = timeout_date;
2675
NSTimeInterval next_interval;
2678
* If multiple threads are using this connections, another
2679
* thread may read the reply we are waiting for - so we must
2680
* break out of the runloop frequently to check. We do this
2681
* by setting a small delay and increasing it each time round
2682
* so that this semi-busy wait doesn't consume too much
2683
* processor time (I hope).
2684
* We set an upper limit on the delay to avoid responsiveness
2687
RELEASE(delay_date);
2688
delay_date = [dateClass allocWithZone: NSDefaultMallocZone()];
2689
if (delay_interval < 1.0)
2691
next_interval = last_interval + delay_interval;
2692
last_interval = delay_interval;
2693
delay_interval = next_interval;
2696
= [delay_date initWithTimeIntervalSinceNow: delay_interval];
2699
* We must not set a delay date that is further in the future
2700
* than the timeout date for the response to be returned.
2702
if ([timeout_date earlierDate: delay_date] == timeout_date)
2704
limit_date = timeout_date;
2708
limit_date = delay_date;
2712
* If the runloop returns without having done anything, AND we
2713
* were waiting for the final timeout, then we must break out
2716
if ([runLoop runMode: NSConnectionReplyMode
2717
beforeDate: limit_date] == NO
2718
|| [timeout_date timeIntervalSinceNow] <= 0.0)
2720
if (limit_date == timeout_date)
2722
M_LOCK(_queueGate); isLocked = YES;
2723
node = GSIMapNodeForKey(_replyMap, (GSIMapKey)sn);
2437
limit_date = delay_date;
2441
* If the runloop returns without having done anything, AND we
2442
* were waiting for the final timeout, then we must break out
2445
if ([runLoop runMode: NSConnectionReplyMode
2446
beforeDate: limit_date] == NO)
2448
if (limit_date == timeout_date)
2731
* Normal operation - wait for data or for a timeout.
2733
if ([runLoop runMode: NSConnectionReplyMode
2734
beforeDate: timeout_date] == NO
2735
|| [timeout_date timeIntervalSinceNow] <= 0.0)
2737
M_LOCK(_queueGate); isLocked = YES;
2451
2738
node = GSIMapNodeForKey(_replyMap, (GSIMapKey)sn);
2742
M_LOCK(_queueGate); isLocked = YES;
2459
* Normal operation - wait for data to be recieved or for a timeout.
2461
if ([runLoop runMode: NSConnectionReplyMode
2462
beforeDate: timeout_date] == NO)
2465
node = GSIMapNodeForKey(_replyMap, (GSIMapKey)sn);
2477
rmc = node->value.obj;
2478
GSIMapRemoveKey(_replyMap, (GSIMapKey)sn);
2480
M_UNLOCK(_queueGate);
2481
TEST_RELEASE(delay_date);
2482
TEST_RELEASE(timeout_date);
2485
[NSException raise: NSInternalInconsistencyException
2486
format: @"no reply message available"];
2488
if (rmc == dummyObject)
2490
[NSException raise: NSPortTimeoutException
2491
format: @"timed out waiting for reply"];
2750
rmc = node->value.obj;
2751
GSIMapRemoveKey(_replyMap, (GSIMapKey)sn);
2753
M_UNLOCK(_queueGate); isLocked = NO;
2754
TEST_RELEASE(delay_date);
2755
TEST_RELEASE(timeout_date);
2758
[NSException raise: NSInternalInconsistencyException
2759
format: @"no reply message available"];
2761
if (rmc == dummyObject)
2763
if (_isValid == YES)
2765
[NSException raise: NSPortTimeoutException
2766
format: @"timed out waiting for reply"];
2770
[NSException raise: NSPortTimeoutException
2771
format: @"invalidated while awaiting reply"];
2777
if (isLocked == YES)
2779
M_UNLOCK(_queueGate);
2781
[localException raise];
2493
2785
NSDebugMLLog(@"NSConnection", @"Consuming reply RMC %d on %x", sn, self);
2758
3035
/* Managing objects and proxies. */
2759
3036
- (void) addLocalObject: (NSDistantObject*)anObj
3038
static unsigned local_object_counter = 0;
2762
3040
unsigned target;
2763
GSLocalCounter *counter;
2764
3041
GSIMapNode node;
2766
3043
M_LOCK(_proxiesGate);
2767
M_LOCK(global_proxies_gate);
2768
3044
NSParameterAssert (_isValid);
3046
object = ((ProxyStruct*)anObj)->_object;
3047
target = ((ProxyStruct*)anObj)->_handle;
3050
* If there is no target allocated to the proxy, we add one.
3054
((ProxyStruct*)anObj)->_handle = target = ++local_object_counter;
2771
3058
* Record the value in the _localObjects map, retaining it.
2773
object = ((ProxyStruct*)anObj)->_object;
2774
3060
node = GSIMapNodeForKey(_localObjects, (GSIMapKey)object);
2775
IF_NO_GC(RETAIN(anObj));
2778
GSIMapAddPair(_localObjects, (GSIMapKey)object, (GSIMapVal)anObj);
2782
RELEASE(node->value.obj);
2783
node->value.obj = anObj;
3061
NSAssert(node == 0, NSInternalInconsistencyException);
3062
node = GSIMapNodeForKey(_localTargets, (GSIMapKey)target);
3063
NSAssert(node == 0, NSInternalInconsistencyException);
2787
* Keep track of local objects accross all connections.
2789
counter = NSMapGet(objectToCounter, (void*)object);
2793
target = counter->target;
2797
counter = [localCounterClass newWithObject: object];
2798
target = counter->target;
2799
NSMapInsert(objectToCounter, (void*)object, counter);
2800
NSMapInsert(targetToCounter, (void*)target, counter);
2803
((ProxyStruct*)anObj)->_handle = target;
3066
GSIMapAddPair(_localObjects, (GSIMapKey)object, (GSIMapVal)anObj);
2804
3067
GSIMapAddPair(_localTargets, (GSIMapKey)target, (GSIMapVal)anObj);
2805
3069
if (debug_connection > 2)
2806
3070
NSLog(@"add local object (0x%x) target (0x%x) "
2807
@"to connection (0x%x) (ref %d)",
2808
(gsaddr)object, target, (gsaddr) self, counter->ref);
2809
M_UNLOCK(global_proxies_gate);
3071
@"to connection (0x%x)", (gsaddr)object, target, (gsaddr) self);
2810
3073
M_UNLOCK(_proxiesGate);
2813
- (NSDistantObject*) localForObject: (id)object
3076
- (NSDistantObject*) retainOrAddLocal: (NSDistantObject*)proxy
3077
forObject: (id)object
2815
3079
GSIMapNode node;
2816
3080
NSDistantObject *p;
2827
3091
p = node->value.obj;
3095
if (p == nil && proxy != nil)
3098
[self addLocalObject: p];
2829
3100
M_UNLOCK(_proxiesGate);
2830
NSParameterAssert(p == nil || [p connectionForProxy] == self);
2834
- (void) removeLocalObject: (id)anObj
3104
- (void) removeLocalObject: (NSDistantObject*)prox
2836
NSDistantObject *prox;
2838
GSLocalCounter *counter;
2842
M_LOCK(global_proxies_gate);
2843
3111
M_LOCK(_proxiesGate);
3112
anObj = ((ProxyStruct*)prox)->_object;
2845
3113
node = GSIMapNodeForKey(_localObjects, (GSIMapKey)anObj);
2852
prox = node->value.obj;
2854
target = ((ProxyStruct*)prox)->_handle;
2857
* If all references to a local proxy have gone - remove the
2858
* global reference as well.
3116
* The NSDistantObject concerned may not belong to this connection,
3117
* so we need to check that any matching proxy is identical to the
3118
* argument we were given.
2860
counter = NSMapGet(objectToCounter, (void*)anObj);
3120
if (node != 0 && node->value.obj == prox)
2864
if ((val = counter->ref) == 0)
3122
target = ((ProxyStruct*)prox)->_handle;
3125
* If this proxy has been vended onwards to another process
3126
* which has not myet released it, we need to keep a reference
3127
* to the local object around for a while in case that other
3130
if ((((ProxyStruct*)prox)->_counter) != 0)
2867
* If this proxy has been vended onwards by another process, we
2868
* need to keep a reference to the local object around for a
2869
* while in case that other process needs it.
3132
CachedLocalObject *item;
3134
(((ProxyStruct*)prox)->_counter) = 0;
3135
M_LOCK(cached_proxies_gate);
2876
timer = [NSTimer scheduledTimerWithTimeInterval: 1.0
2877
target: connectionClass
2878
selector: @selector(_timeout:)
2882
item = [CachedLocalObject newWithObject: counter time: 30];
2883
NSMapInsert(targetToCached, (void*)target, item);
2885
if (debug_connection > 3)
2886
NSLog(@"placed local object (0x%x) target (0x%x) in cache",
2887
(gsaddr)anObj, target);
3138
timer = [NSTimer scheduledTimerWithTimeInterval: 1.0
3139
target: connectionClass
3140
selector: @selector(_timeout:)
2889
NSMapRemove(objectToCounter, (void*)anObj);
2890
NSMapRemove(targetToCounter, (void*)target);
3144
item = [CachedLocalObject newWithObject: prox time: 5];
3145
NSMapInsert(targetToCached, (void*)target, item);
3146
M_UNLOCK(cached_proxies_gate);
3148
if (debug_connection > 3)
3149
NSLog(@"placed local object (0x%x) target (0x%x) in cache",
3150
(gsaddr)anObj, target);
3154
* Remove the proxy from _localObjects and release it.
3156
GSIMapRemoveKey(_localObjects, (GSIMapKey)anObj);
3160
* Remove the target info too - no release required.
3162
GSIMapRemoveKey(_localTargets, (GSIMapKey)target);
3164
if (debug_connection > 2)
3165
NSLog(@"removed local object (0x%x) target (0x%x) "
3166
@"from connection (0x%x) (ref %d)",
3167
(gsaddr)anObj, target, (gsaddr)self, val);
2895
* Remove the proxy from _localObjects and release it.
2897
GSIMapRemoveKey(_localObjects, (GSIMapKey)anObj);
2901
* Remove the target info too - no release required.
2903
GSIMapRemoveKey(_localTargets, (GSIMapKey)target);
2905
if (debug_connection > 2)
2906
NSLog(@"remove local object (0x%x) target (0x%x) "
2907
@"from connection (0x%x) (ref %d)",
2908
(gsaddr)anObj, target, (gsaddr)self, val);
2910
3169
M_UNLOCK(_proxiesGate);
2911
M_UNLOCK(global_proxies_gate);
2914
- (void) _release_targets: (unsigned*)list count: (unsigned)number
3172
- (void) _release_target: (unsigned)target count: (unsigned)number
2952
- (void) retainTarget: (unsigned)target
2957
* Tell the remote app that it must retain the local object
2958
* for the target on this connection.
2960
if (_receivePort && _isValid)
2967
op = [self _makeOutRmc: 0 generate: &seq_num reply: YES];
2968
[op encodeValueOfObjCType: @encode(typeof(target)) at: &target];
2969
[self _sendOutRmc: op type: PROXY_RETAIN];
2971
ip = [self _getReplyRmc: seq_num];
2972
[ip decodeValueOfObjCType: @encode(id) at: &result];
2973
[self _doneInRmc: ip];
2975
NSLog(@"failed to retain target - %@", result);
2980
NSLog(@"failed to retain target - %@", localException);
3210
- (NSDistantObject*) locateLocalTarget: (unsigned)target
3212
NSDistantObject *proxy = nil;
3215
M_LOCK(_proxiesGate);
3218
* Try a quick lookup to see if the target references a local object
3219
* belonging to the receiver ... usually it should.
3221
node = GSIMapNodeForKey(_localTargets, (GSIMapKey)target);
3224
proxy = node->value.obj;
3228
* If the target doesn't exist in the receiver, but still
3229
* persists in the cache (ie it was recently released) then
3230
* we move it back from the cache to the receiver.
3234
CachedLocalObject *cached;
3236
M_LOCK(cached_proxies_gate);
3237
cached = NSMapGet (targetToCached, (void*)target);
3240
proxy = [cached obj];
3242
* Found in cache ... add to this connection as the object
3243
* is no longer in use by any connection.
3245
ASSIGN(((ProxyStruct*)proxy)->_connection, self);
3246
[self addLocalObject: proxy];
3247
NSMapRemove(targetToCached, (void*)target);
3248
if (debug_connection > 3)
3249
NSLog(@"target (0x%x) moved from cache", target);
3251
M_UNLOCK(cached_proxies_gate);
3255
* If not found in the current connection or the cache of local references
3256
* of recently invalidated connections, try all other existing connections.
3260
NSHashEnumerator enumerator;
3263
M_LOCK(connection_table_gate);
3264
enumerator = NSEnumerateHashTable(connection_table);
3266
&& (c = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil)
3268
if (c != self && [c isValid] == YES)
3270
M_LOCK(c->_proxiesGate);
3271
node = GSIMapNodeForKey(c->_localTargets, (GSIMapKey)target);
3278
* We found the local object in use in another connection
3279
* so we create a new reference to the same object and
3280
* add it to our connection, adjusting the target of the
3281
* new reference to be the value we need.
3283
* We don't want to just share the NSDistantObject with
3284
* the other connection, since we might want to keep
3285
* track of information on a per-connection basis in
3286
* order to handle connection shutdown cleanly.
3288
proxy = node->value.obj;
3289
local = RETAIN(((ProxyStruct*)proxy)->_object);
3290
proxy = [NSDistantObject proxyWithLocal: local
3292
nTarget = ((ProxyStruct*)proxy)->_handle;
3293
GSIMapRemoveKey(_localTargets, (GSIMapKey)nTarget);
3294
((ProxyStruct*)proxy)->_handle = target;
3295
GSIMapAddPair(_localTargets, (GSIMapKey)target,
3298
M_UNLOCK(c->_proxiesGate);
3301
NSEndHashTableEnumeration(&enumerator);
3302
M_UNLOCK(connection_table_gate);
3305
M_UNLOCK(_proxiesGate);
3309
if (debug_connection > 3)
3310
NSLog(@"target (0x%x) not found anywhere", target);
3315
- (void) vendLocal: (NSDistantObject*)aProxy
3317
M_LOCK(_proxiesGate);
3318
((ProxyStruct*)aProxy)->_counter++;
3319
M_UNLOCK(_proxiesGate);
3322
- (void) aquireProxyForTarget: (unsigned)target
3324
NSDistantObject *found;
3327
/* Don't assert (_isValid); */
3328
M_LOCK(_proxiesGate);
3329
node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)target);
3336
found = node->value.obj;
3338
M_UNLOCK(_proxiesGate);
3344
* Tell the remote app that it must retain the local object
3345
* for the target on this connection.
3347
if (_receivePort && _isValid)
3354
op = [self _makeOutRmc: 0 generate: &seq_num reply: YES];
3355
[op encodeValueOfObjCType: @encode(typeof(target)) at: &target];
3356
[self _sendOutRmc: op type: PROXY_RETAIN];
3358
ip = [self _getReplyRmc: seq_num];
3359
[ip decodeValueOfObjCType: @encode(id) at: &result];
3360
[self _doneInRmc: ip];
3362
NSLog(@"failed to retain target - %@", result);
3363
else if (debug_connection > 3)
3364
NSLog(@"sending retain for target - %u", target);
3369
NSLog(@"failed to retain target - %@", localException);
3377
return [super retain];
2985
3380
- (void) removeProxy: (NSDistantObject*)aProxy
2988
3383
if (_isValid == YES)
2990
3385
unsigned target;
2991
3387
GSIMapNode node;
2993
3389
target = ((ProxyStruct*)aProxy)->_handle;
2994
3390
node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)target);
3393
* Only remove if the proxy for the target is the same as the
3394
* supplied argument.
3396
if (node != 0 && node->value.obj == aProxy)
2997
RELEASE(node->value.obj);
3398
count = ((ProxyStruct*)aProxy)->_counter;
2998
3399
GSIMapRemoveKey(_remoteProxies, (GSIMapKey)target);
3401
* Tell the remote application that we have removed our proxy and
3402
* it can release it's local object.
3404
[self _release_target: target count: count];
3001
* Tell the remote application that we have removed our proxy and
3002
* it can release it's local object.
3004
[self _release_targets: &target count: 1];
3006
3407
M_UNLOCK(_proxiesGate);
3009
- (NSDistantObject*) proxyForTarget: (unsigned)target
3412
* Private method used only when a remote process/thread has sent us a
3413
* target which we are decoding into a proxy in this process/thread.
3414
* <p>The argument aProxy may be nil, in which case an existing proxy
3415
* matching aTarget is retrieved retained, and returned (this is done
3416
* when a proxy target is sent to us by a remote process).
3418
* <p>If aProxy is not nil, but a proxy with the same target already
3419
* exists, then aProxy is released and the existing proxy is returned
3420
* as in the case where aProxy was nil.
3422
* <p>If aProxy is not nil and there was no prior proxy with the same
3423
* target, aProxy is added to the receiver and returned.
3426
- (NSDistantObject*) retainOrAddProxy: (NSDistantObject*)aProxy
3427
forTarget: (unsigned)aTarget
3011
3429
NSDistantObject *p;
3012
3430
GSIMapNode node;
3014
3432
/* Don't assert (_isValid); */
3433
NSParameterAssert(aTarget > 0);
3434
NSParameterAssert(aProxy==nil || aProxy->isa == distantObjectClass);
3435
NSParameterAssert(aProxy==nil || [aProxy connectionForProxy] == self);
3436
NSParameterAssert(aProxy==nil || aTarget == ((ProxyStruct*)aProxy)->_handle);
3015
3438
M_LOCK(_proxiesGate);
3016
node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)target);
3439
node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)aTarget);
3023
3446
p = node->value.obj;
3450
if (p == nil && aProxy != nil)
3453
GSIMapAddPair(_remoteProxies, (GSIMapKey)aTarget, (GSIMapVal)p);
3456
* Whether this is a new proxy or an existing proxy, this method is
3457
* only called for an object being vended by a remote process/thread.
3458
* We therefore need to increment the count of the number of times
3459
* the proxy has been vended.
3463
((ProxyStruct*)p)->_counter++;
3025
3465
M_UNLOCK(_proxiesGate);
3029
- (void) addProxy: (NSDistantObject*) aProxy
3034
M_LOCK(_proxiesGate);
3035
NSParameterAssert(_isValid);
3036
NSParameterAssert(aProxy->isa == distantObjectClass);
3037
NSParameterAssert([aProxy connectionForProxy] == self);
3038
target = ((ProxyStruct*)aProxy)->_handle;
3039
node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)target);
3042
M_UNLOCK(_proxiesGate);
3043
[NSException raise: NSGenericException
3044
format: @"Trying to add the same proxy twice"];
3046
GSIMapAddPair(_remoteProxies, (GSIMapKey)target, (GSIMapVal)aProxy);
3047
M_UNLOCK(_proxiesGate);
3050
- (id) includesProxyForTarget: (unsigned)target
3052
NSDistantObject *ret;
3055
/* Don't assert (_isValid); */
3056
M_LOCK(_proxiesGate);
3057
node = GSIMapNodeForKey(_remoteProxies, (GSIMapKey)target);
3064
ret = node->value.obj;
3066
M_UNLOCK(_proxiesGate);
3070
3469
- (id) includesLocalObject: (id)anObj
3072
3471
NSDistantObject *ret;
3110
/* Check all connections.
3111
Proxy needs to use this when decoding a local object in order to
3112
make sure the target address is a valid object. It is not enough
3113
for the Proxy to check the Proxy's connection only (using
3114
-includesLocalTarget), because the proxy may have come from a
3115
triangle connection. */
3116
+ (id) includesLocalTarget: (unsigned)target
3120
/* Don't assert (_isValid); */
3121
M_LOCK(global_proxies_gate);
3122
ret = NSMapGet(targetToCounter, (void*)target);
3123
M_UNLOCK(global_proxies_gate);
3128
/* Accessing ivars */
3131
3509
/* Prevent trying to encode the connection itself */
3133
3511
- (void) encodeWithCoder: (NSCoder*)anEncoder
3135
3513
[self shouldNotImplement: _cmd];
3138
3515
- (id) initWithCoder: (NSCoder*)aDecoder;
3140
3517
[self shouldNotImplement: _cmd];
3145
/* Shutting down and deallocating. */
3148
3522
* We register this method for a notification when a port dies.
3149
3523
* NB. It is possible that the death of a port could be notified
3150
3524
* to us after we are invalidated - in which case we must ignore it.
3152
- (void) portIsInvalid: (NSNotification*)notification
3526
- (void) _portIsInvalid: (NSNotification*)notification
3549
* On thread exit, we need all connections to be removed from the runloop
3550
* of the thread or they will retain that and cause a memory leak.
3552
+ (void) _threadWillExit: (NSNotification*)notification
3554
NSRunLoop *runLoop = GSRunLoopForThread([notification object]);
3558
NSHashEnumerator enumerator;
3561
M_LOCK(connection_table_gate);
3562
enumerator = NSEnumerateHashTable(connection_table);
3563
while ((c = (NSConnection*)NSNextHashEnumeratorItem(&enumerator)) != nil)
3565
[c removeRunLoop: runLoop];
3567
NSEndHashTableEnumeration(&enumerator);
3568
M_UNLOCK(connection_table_gate);
3577
* This category represents an informal protocol to which NSConnection
3578
* delegates may conform ... implementing these methods has the effect
3581
@implementation Object (NSConnectionDelegate)
3584
* This is not an NSConnection method, but is a method that may
3585
* be implemented by the delegate of an NSConnection object.
3588
* If the delegate implements this method, the NSConnection will
3589
* invoke the method for every message request or reply it receives
3590
* from the remote NSConnection. The delegate should use the
3591
* authentication data to check all the NSData objects
3592
* in the components array (ignoring NSPort objects),
3593
* and return YES if they are valid, NO otherwise.
3596
* If the method returns NO then an
3597
* NSFailedAuthentication exception will be raised.
3600
* In GNUstep the components array is mutable, allowing
3601
* you to replace the NSData objects with your own version.
3604
- (BOOL) authenticateComponents: (NSMutableArray*)components
3605
withData: (NSData*)authenticationData
3612
* This is not an NSConnection method, but is a method that may
3613
* be implemented by the delegate of an NSConnection object.
3616
* If the delegate implements this method, the NSConnection will
3617
* invoke the method for every message request ro reply it sends
3618
* to the remote NSConnection. The delegate should generate
3619
* authentication data by examining all the NSData objects
3620
* in the components array (ignoring NSPort objects),
3621
* and return the authentication data that can be used by the
3622
* remote NSConnection.
3625
* If the method returns nil then an
3626
* NSGenericException exception will be raised.
3629
* In GNUstep the components array is mutable, allowing
3630
* you to replace the NSData objects with your own version.
3633
- (NSData*) authenticationDataForComponents: (NSMutableArray*)components
3640
* This is not an NSConnection method, but is a method that may
3641
* be implemented by the delegate of an NSConnection object.
3644
* If the delegate implements this method, it will be called
3645
* whenever a new NSConnection is created that has this
3646
* NSConnection as its parent. The delegate may take this
3647
* opportunity to adjust the configuration of the new
3648
* connection and may return a boolean value to tell the
3649
* parent whether the creation of the new connection is to
3650
* be permitted or not.
3653
- (BOOL) connection: (NSConnection*)parent
3654
shouldMakeNewConnection: (NSConnection*)newConnection
3659
- (NSConnection*) connection: (NSConnection*)ancestorConn
3660
didConnect: (NSConnection*)newConn
3666
* An old fashioned synonym for -connection:shouldMakeNewConnection: -
3669
- (BOOL) makeNewConnection: (NSConnection*)newConnection
3670
sender: (NSConnection*)parent