Distributed Objects, proxy NSDistantObjects over-retained on OSX 10.5?
Distributed Objects, proxy NSDistantObjects over-retained on OSX 10.5?
- Subject: Distributed Objects, proxy NSDistantObjects over-retained on OSX 10.5?
- From: Dave Cox <email@hidden>
- Date: Mon, 10 Nov 2008 13:21:45 -0800
I am writing an application that uses Distributed Objects for communications
among several processes. I developed and tested on OSX 10.4.11 for several
months, and all seemed well. A few weeks back I finally upgraded to 10.5.5, and
now I'm having a variety of problems that don't seem to be there on 10.4. The
app is complex and getting more so, so I'm writing simple programs to try to
demonstrate the problems I encounter and rule out errors in my own code obscured
by all the complexity.
First:
I declare a method in a protocol which returns as an out-param an ObjC object:
// server factory interface
@protocol IServerFactory <NSObject>
- (int) getServer: (out byref id<IServer>*) server;
@end
A "factory" object in the server process conforms to this protocol, and the
client process gets a proxy NSDistantObject to the factory object.
When the client calls the method on the proxy, the factory is invoked, it
creates a server object that conforms to IServer protocol, and returns it.
Another NSDistantObject, this a proxy to the server, is returned to the client.
On OS 10.4 the returned NSDistantObject has retainCount of 1, and when the
client releases the current autorelease pool, the retainCount goes to 0. The
proxy is dealloced and the server receives a release message. Apparently the
client caller did not own the returned proxy object. This seems the appropriate
behavior because there's nothing about the method to suggest the create rule
should be in effect.
On OS 10.5, however, the returned NSDistantObject has retainCount of 3, and when
I release the current autorelease pool in the client, the retainCount goes to 1.
Apparently the client caller owns the proxy object, and must release it for it
to be deallocated.
Why has the behavior changed, and where is it documented?
Here's code, and following that some representative output.
Note that the 'x' arg to the client causes it to perform an explicit release on
the proxy after the autorelease pool has released it.
--------------------------------------------------------------------------------
// shared.h
#ifndef __SHARED_H__
#define __SHARED_H__
#import <Foundation/NSObject.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSPort.h>
#import <Foundation/NSConnection.h>
#import <Foundation/NSPortNameServer.h>
#import <Foundation/NSRunLoop.h>
#import <Foundation/NSException.h>
#import <Foundation/NSString.h>
#define SERVER_NAME "OverRetainServer"
// server interface
@protocol IServer <NSObject>
- (int) getInt;
@end
// server factory interface
@protocol IServerFactory <NSObject>
- (int) getServer: (out byref id<IServer>*) server;
@end
#endif // ndef'd __SHARED_H__
--------------------------------------------------------------------------------
// server.mm
#import "shared.h"
#import "log.h"
// server class
@interface CServer : NSObject <IServer> {}
- (void) dealloc;
@end
// server factory class
@interface CServerFactory : NSObject <IServerFactory> {}
@end
@implementation CServer
- (int) getInt {
return 42;
}
- (void) dealloc {
log("server: CServer dealloc\n");
[super dealloc];
}
@end
@implementation CServerFactory
- (int) getServer: (out byref id<IServer>*) server {
*server = [[CServer alloc] init];
[*server autorelease];
return 1;
}
@end
int main (int argc, char * const argv[]) {
log("server...\n");
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSMachPort* port = nil;
NSConnection* connection = nil;
CServerFactory* factory = nil;
do {
// vend server object...
// port
port = [[NSMachPort alloc] init];
if (port == nil) {
log("server: error: port is nil\n");
break;
}
[port autorelease];
// connection
connection = [NSConnection connectionWithReceivePort:port sendPort:nil];
if (connection == nil) {
log("server: error: connection is nil\n");
break;
}
// create factory object to vend
factory = [[CServerFactory alloc] init];
if (factory == nil) {
log("server: error: factory is nil\n");
break;
}
[factory autorelease];
// vend object
[connection setRootObject: factory];
// publish name
NSMachBootstrapServer* namesrv = [NSMachBootstrapServer sharedInstance];
if ([namesrv registerPort: port name: @SERVER_NAME] == NO) {
log("server: error: port not registered\n");
break;
}
// run the server until terminated
while (true) {
NSAutoreleasePool* innerPool = [[NSAutoreleasePool alloc] init];
@try {
[ [NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
beforeDate: [NSDate distantFuture] ];
} @catch (id x) {
log("server: caught exception\n");
}
[innerPool release];
}
} while (false);
[port invalidate];
[pool release];
return 0;
}
--------------------------------------------------------------------------------
// client.mm
#import <unistd.h>
#import "shared.h"
#import "log.h"
int main (int argc, char * const argv[]) {
log("client...\n");
bool extraRelease = false;
if (argc > 1 && argv[1][0] == 'x') {
extraRelease = true;
}
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
id<IServerFactory> factory = nil;
do {
// lookup server factory object
id proxy = [NSConnection
rootProxyForConnectionWithRegisteredName: @SERVER_NAME
host: nil ];
if (proxy == nil) {
log("client: error: proxy is nil (server not found?)\n");
break;
}
factory = (id<IServerFactory>)proxy;
id<IServer> server;
char* activity;
// run the server until terminated
while (true) {
server = nil;
NSAutoreleasePool* innerPool = [[NSAutoreleasePool alloc] init];
@try {
// get a server object from factory
activity = "getServer";
int factoryResult = [factory getServer: &server];
if (factoryResult != 1) {
log("client: getServer returned %d\n", factoryResult);
break;
}
if (server == nil) {
log("client: getServer returned nil server\n");
break;
}
log( "client: server has retainCount %u\n",
[server retainCount] );
// call server object
activity = "getInt";
int serverResult = [server getInt];
if (serverResult != 42) {
log("client: getInt returned %d\n", serverResult);
break;
}
} @catch (NSException* x) {
log( "client: exception thrown from %s: %s\n",
activity,
[[x reason] UTF8String] );
}
[innerPool release];
if (extraRelease) {
// if out-param proxies are considered owned by the caller,
// then this would be appropriate:
[server release];
}
sleep(1);
}
} while (false);
[pool release];
return 0;
}
--------------------------------------------------------------------------------
// log.h
#ifndef __log_h__ // {
#define __log_h__
void log(const char* format, ...);
#endif // } ndef'd __log_h__
--------------------------------------------------------------------------------
// log.cpp
#include "log.h"
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>
static pid_t pid = getpid();
void log(const char* format, ...) {
time_t now = time(NULL);
struct tm* sNow = localtime(&now);
fprintf( stdout,
"%.4d%.2d%.2d %.2d:%.2d:%.2d []] ",
sNow->tm_year + 1900,
sNow->tm_mon + 1,
sNow->tm_mday,
sNow->tm_hour,
sNow->tm_min,
sNow->tm_sec,
pid );
va_list args;
va_start(args, format);
vfprintf(stdout, format, args);
va_end(args);
fflush(stdout);
}
--------------------------------------------------------------------------------
OverRetain Test OSX 10.4.11
Note that when the autorelease pool releases the proxy, the corresponding remote
server is released and dealloced.
------------------------------------------------------
./server &
[1] 236
20081110 12:08:33 [ 236] server...
./client
20081110 12:09:42 [ 241] client...
20081110 12:09:42 [ 241] client: server has retainCount 1
20081110 12:09:43 [ 236] server: CServer dealloc
20081110 12:09:43 [ 241] client: server has retainCount 1
20081110 12:09:44 [ 236] server: CServer dealloc
20081110 12:09:44 [ 241] client: server has retainCount 1
20081110 12:09:45 [ 236] server: CServer dealloc
20081110 12:09:45 [ 241] client: server has retainCount 1
20081110 12:09:46 [ 236] server: CServer dealloc
20081110 12:09:46 [ 241] client: server has retainCount 1
20081110 12:09:47 [ 236] server: CServer dealloc
20081110 12:09:47 [ 241] client: server has retainCount 1
^C // terminate client
20081110 12:09:48 [ 236] server: CServer dealloc
------------------------------------------------------
./client x
20081110 12:12:05 [ 247] client...
20081110 12:12:05 [ 247] client: server has retainCount 1
20081110 12:12:06 [ 236] server: CServer dealloc
Bus error // tried to release proxy that has already been destroyed
------------------------------------------------------
OverRetain Test OSX 10.5.5
Note that when the autorelease pool releases the proxy, the corresponding remote
server is not dealloced. All the server instances are leaked until the client
process terminates, invalidating the port/connection so the server-side of the
connection releases all its local objects.
------------------------------------------------------
./server &
[1] 15585
20081110 11:18:02 [15585] server...
./client
20081110 11:18:14 [15587] client...
20081110 11:18:14 [15587] client: server has retainCount 3
20081110 11:18:15 [15587] client: server has retainCount 3
20081110 11:18:16 [15587] client: server has retainCount 3
20081110 11:18:17 [15587] client: server has retainCount 3
20081110 11:18:18 [15587] client: server has retainCount 3
20081110 11:18:19 [15587] client: server has retainCount 3
20081110 11:18:20 [15587] client: server has retainCount 3
20081110 11:18:21 [15587] client: server has retainCount 3
20081110 11:18:22 [15587] client: server has retainCount 3
^C // terminate client
20081110 11:18:22 [15585] server: CServer dealloc
20081110 11:18:22 [15585] server: CServer dealloc
20081110 11:18:22 [15585] server: CServer dealloc
20081110 11:18:22 [15585] server: CServer dealloc
20081110 11:18:22 [15585] server: CServer dealloc
20081110 11:18:22 [15585] server: CServer dealloc
20081110 11:18:22 [15585] server: CServer dealloc
20081110 11:18:22 [15585] server: CServer dealloc
20081110 11:18:22 [15585] server: CServer dealloc
------------------------------------------------------
./client x
20081110 11:21:30 [15614] client...
20081110 11:21:30 [15614] client: server has retainCount 3
20081110 11:21:31 [15585] server: CServer dealloc
20081110 11:21:31 [15614] client: server has retainCount 3
20081110 11:21:32 [15585] server: CServer dealloc
20081110 11:21:32 [15614] client: server has retainCount 3
20081110 11:21:33 [15585] server: CServer dealloc
20081110 11:21:33 [15614] client: server has retainCount 3
20081110 11:21:34 [15585] server: CServer dealloc
20081110 11:21:34 [15614] client: server has retainCount 3
20081110 11:21:35 [15585] server: CServer dealloc
20081110 11:21:35 [15614] client: server has retainCount 3
20081110 11:21:36 [15585] server: CServer dealloc
20081110 11:21:36 [15614] client: server has retainCount 3
20081110 11:21:37 [15585] server: CServer dealloc
20081110 11:21:37 [15614] client: server has retainCount 3
^C // terminate client
20081110 11:21:38 [15585] server: CServer dealloc
------------------------------------------------------
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden