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 [%5d] ", 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 (Cocoa-dev@lists.apple.com) 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: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]