Proxy NSDistantObject on broken connection returns zero rather than throwing exceptions on OSX 10.5?
Proxy NSDistantObject on broken connection returns zero rather than throwing exceptions on OSX 10.5?
- Subject: Proxy NSDistantObject on broken connection returns zero rather than throwing exceptions on OSX 10.5?
- From: Dave Cox <email@hidden>
- Date: Mon, 10 Nov 2008 19:41:31 -0800
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 []] ",
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 (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