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]

Reply via email to