This is the second question in a series. 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.
A server process vends an object. A client process connects (establishes a proxy NSDistantObject) to the vended object. The client invokes a method, implemented by the server object, on the proxy. The method is declared with return-value type int (or, alternatively, id).
If the server process terminates unexpectedly before the client calls the method, obviously, the method cannot execute in the server process. In the client, Distributed Objects should indicate an error dispatching the invocation to the remote object.
On OS 10.4, in my experience, an exception is always thrown when calling such a proxy. But on OS 10.5, an exception is not always thrown. In some cases, the method invocation returns 0 (or nil). This doesn't seem appropriate. Distributed Objects cannot know whether 0/nil is a reasonable value to return, considering the application-specific semantics of the method are defined by the developer. Often, zero is understood to mean no error!
Can someone point me to documentation of the correct behavior? Can the 10.5 behavior be considered correct? It is troubling, as I will have to design my methods and client to account for the possibility of zero/nil returned to indicate communications error.
Here's code, and following that some representative output. -------------------------------------------------------------------------------- // 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" @protocol IServer <NSObject> - (int) getInt; @end @protocol IServerFactory <NSObject> - (int) getServer: (out byref id<IServer>*) server; @end #endif // ndef'd __SHARED_H__ -------------------------------------------------------------------------------- // server.mm #import "shared.h" #import "log.h" @interface CServer : NSObject <IServer> {} - (void) dealloc; @end @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 = [[NSMachPort alloc] init]; if (port == nil) { log("server: error: port is nil\n"); break; } [port autorelease]; 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; // call server in loop 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); } -------------------------------------------------------------------------------- Test run on OSX 10.4.11 > ./server & [1] 257 20081110 12:14:21 [ 257] server... > ./client 20081110 12:14:34 [ 259] client... 20081110 12:14:34 [ 259] client: server has retainCount 1 20081110 12:14:35 [ 257] server: CServer dealloc 20081110 12:14:35 [ 259] client: server has retainCount 1 20081110 12:14:36 [ 257] server: CServer dealloc 20081110 12:14:36 [ 259] client: server has retainCount 1 20081110 12:14:37 [ 257] server: CServer dealloc 20081110 12:14:37 [ 259] client: server has retainCount 1 20081110 12:14:38 [ 257] server: CServer dealloc 20081110 12:14:38 [ 259] client: server has retainCount 1 20081110 12:14:39 [ 257] server: CServer dealloc 20081110 12:14:39 [ 259] client: server has retainCount 1 // kill server 20081110 12:14:40 [ 259] client: exception thrown from getServer: [NSMachPort sendBeforeDate:] destination port invalid 20081110 12:14:41 [ 259] client: exception thrown from getServer: NSDistantObject is invalid 20081110 12:14:42 [ 259] client: exception thrown from getServer: NSDistantObject is invalid 20081110 12:14:43 [ 259] client: exception thrown from getServer: NSDistantObject is invalid ^C // terminate client [1] + Terminated ./server ------------------------------------------------------ Test run on OSX 10.5.5 > ./server & [1] 15832 20081110 11:49:48 [15832] server... > ./client 20081110 11:50:02 [15834] client... 20081110 11:50:02 [15834] client: server has retainCount 3 20081110 11:50:03 [15834] client: server has retainCount 3 20081110 11:50:04 [15834] client: server has retainCount 3 20081110 11:50:05 [15834] client: server has retainCount 3 20081110 11:50:06 [15834] client: server has retainCount 3 20081110 11:50:07 [15834] client: server has retainCount 3 // kill server 20081110 11:50:08 [15834] client: exception thrown from getServer: [NSMachPort sendBeforeDate:] destination port invalid 20081110 11:50:09 [15834] client: getServer returned 0 [1] + Terminated ./server ------------------------------------------------------ _______________________________________________ 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]