From: dillon@overload.Berkeley.CA.US (Matthew Dillon)
Newsgroups: comp.sys.next.programmer
Subject: NS3.0 Distributed objects questions / example code
Distribution: world
Date: 7 Sep 92 14:25:26 PST
Organization: Not an Organization

    This documentation is *AWEFUL*!!  You get little one paragraph
    descriptions that tell you very little about how things are supposed to
    work.  There were only 3 examples in the NeXT supplied documentation
    for distributed objects and the one I was interested in --
    multi-threading on the server side, is broken (it leaves threads lying
    around after the client exits).

    If anyone has any idea re: the following I would greatly appreciate an
    answer.  Please post answers to the net!

    Goal:

	I have a server process which registers a named
	NXConnection.  I have N client processes which wish
	to connect to this server process.

	I want the server to SILENTLY fork off a thread for each client
	that connects to it.

    Problems:

	Currently this does not seem possible without writing a bandaid
	called 'newThread' which the client must then call.  Unfortunately,
	this bandaid not only requires the client to KNOW that the server
	is doing something wierd (it should be invisible), but also causes
	the server to call its - connection method whenever any method is
	called using the original proxy.

	(1) The - connection method is described in all of one paragraph
	    in the NeXT documentation.	It does not give you any hint
	    on how the arguments are supposed to be used and how the
	    returned connection is interpreted.

	    In example program #1 below the - connection method is called
	    just once for each client no matter how many operations that
	    client performs on its proxy.  This works as expected

	(2) Normally the inPort for a new connection <newConn> is the same
	    as the inPort for the registration <conn>, meaning that you
	    cannot fork off a thread to handle that connection because all
	    the threads will try to read data from the same port.  That
	    is, you want a particular thread to process a particular
	    connection and no other.

	    So, in example #2 I tried to use connectToPort:withInPort: to
	    setup a separate inPort for the connection then run said
	    connection in a new thread.  Unfortunately, this didn't work
	    big time:

	    (a) The - connection method on the server is run EACH TIME
	    the client calls a method in its original proxy object (it
	    didn't do this in example #1!!).  This appears to be due to my
	    returning something other then <conn> in the - connection
	    method.

	    (b) Further, the client's proxy does not appear to access the
	    new port.

	    (c) If the client then calls the - newThread method it
	    obtains a new proxy object which DOES appear to use the
	    proper connection (the one that I tried to setup and fork
	    off to a separate thread).	This is the bandaid, but has
	    bad side effects as I described above.

	(3) registerForInvalidationNotification does not appear to work
	    at all.  It does not work with the NeXT provided examples.
	    When a client exits the server is never notified and the now
	    defunct thread sticks around. (example program #2)

	    use 'ps m' to list the threads

    I don't want alienate NeXT but I gotta say that you guys (NeXT people)
    MUST provide more SELF CONTAINED (i.e. compilable) examples to
    demonstrate all the quirks your documentation doesn't talk about.  It
    took me ALL DAY to just get as far as I did and half of what I learned
    isn't covered in any documentation anywhere.  The only thing of any use
    are the few examples in Examples/ ... most of the examples embedded in
    the documentation are totally useless because they assume you
    understand the 10,000 items that had to be setup before the particular
    lines shown in the example.  That's no example!

    Any Netter's out there who've figured this out would have my undying
    thanks if they posted the solution to the net!

				    Thanks,

				    -Matt

------------------------------- CUT HERE ---------------------------


/*
 *  MTHREAD1.C
 *
 *  Modified from developer examples
 *
 *  A simple multi-threaded example
 */

#import <objc/Object.h>
#import <remote/NXConnection.h>
#import <remote/NXProxy.h>
#import <mach/cthreads.h>
#import <machkit/NXPort.h>
#import <machkit/senderIsInvalid.h>

/*
 *  Our protocol
 */

@protocol ServerProtocol
- newThread;
- (int) doWork;
@end

/*
 *  Our object class which uses the protocol
 */

@interface MTS : Object <ServerProtocol, NXSenderIsInvalid>
@end

/*
 *  Our implementation
 */

@implementation MTS

/*
 *  Method called when a client connects to us.  connect provides us
 *  with <conn>, a connection which shares topConn's input port and
 *  has its own output port.  The associated object is <self> (i.e. the
 *  one we registered with the name sever)
 *
 *  connection in this case is only called once
 */

- connection: (NXConnection *)topConn didConnect:(NXConnection *)conn {
    printf("connect\n");
    [conn registerForInvalidationNotification:self];
    return(conn);
}

- senderIsInvalid: sender
{
    puts("died!");
}

- newThread {
    puts("new thread");
    return self;
}

- (int) doWork {
    puts("do work");
    return (int) cthread_self();
}

@end

/*
 *  Run the program twice... the first process will be the server, the second
 *  will be the client (the third will be a client, etc...)
 */

main()
{
    id hub;	// proxy object (client) or server object

    hub = [NXConnection connectToName:"MTS example"];

    printf("I am %d\n", cthread_self());

    objc_setMultithreaded(YES);

    if (hub) {
	id core;	// object to handle invalidation notification
	id sub;
	short i;

	/*
	 *  We are a client, <hub> is our proxy.  Create <core> to handle
	 *  invalidation notifcation since we can't use <hub>
	 */

	core = [[MTS alloc] init];
	[[hub connectionForProxy] registerForInvalidationNotification:core];
	[hub setProtocolForProxy:@protocol(ServerProtocol)];

	puts("client connect");

	printf("dowork is %d\n", [hub doWork]);
	printf("dowork is %d\n", [hub doWork]);
	printf("dowork is %d\n", [hub doWork]);
	printf("newThread is %08lx\n", [hub newThread]);
	printf("newThread is %08lx\n", [hub newThread]);
	printf("newThread is %08lx\n", [hub newThread]);
    } else {
	id topConn;			// our server connection

	/*
	 *  Make us a server, create hub and register for invalidation
	 *  notification
	 */

	hub = [MTS new];
	printf("self is %08lx\n", hub);
	topConn = [NXConnection registerRoot:hub withName:"MTS example"];
	[topConn registerForInvalidationNotification:hub];
	puts("server wait");
	[topConn setDelegate:hub];		// so hub gets connection msg
	[topConn run];
    }
    return(0);
}

------------------------------- CUT HERE ---------------------------


/*
 *  MTHREAD2.C
 *
 *  Modified from developer examples
 *
 *  A simple multi-threaded example, Each new client gets a dedicated
 *  thread.
 */

#import <objc/Object.h>
#import <remote/NXConnection.h>
#import <remote/NXProxy.h>
#import <mach/cthreads.h>
#import <machkit/NXPort.h>
#import <machkit/senderIsInvalid.h>

/*
 *  Our protocol
 */

@protocol ServerProtocol
- newThread;
- (int) doWork;
@end

/*
 *  Our object class which uses the protocol
 */

@interface MTS : Object <ServerProtocol, NXSenderIsInvalid>
@end

/*
 *  Our implementation
 */

@implementation MTS

/*
 *  Connections are handled by the main thread only.  Note that the
 *  inPort for the proxy object obtained by the client works under
 *  the 'third party server' paradigm, meaning that each accesses to
 *  it is a connection.  This occurs even if the 'server' is the same
 *  machine as the program which posted the named port.
 *
 *  topConn is the server place holder, conn is an instance connection.  Note
 *  that conn may/will have the same inPort as topConn.  Therefore, we cannot
 *  simply [conn runInNewThread] because it will compete with the main
 *  thread for data.
 */

- connection: (NXConnection *)topConn didConnect:(NXConnection *)conn {
    id threadConn;
    id proxy;
    id inPort = [NXPort new];
    id mts = [MTS new];

    puts("connect");
    proxy = [NXConnection connectToPort:[conn outPort] withInPort:inPort];
    threadConn = [proxy connectionForProxy];
    [conn free];
    [threadConn registerForInvalidationNotification:mts];
    [threadConn runInNewThread];
    [threadConn setRoot:mts];

    return (threadConn);
}

- senderIsInvalid: sender
{
    printf("Sender %08lx died!\n", sender);
    write(1, "XXX\n", 4);
}

/*
 *
 */

- newThread {
    printf("New Thread %08lx %d\n", self, cthread_self());
    return self;
}

- (int) doWork {
    printf("DoWork: %08lx %d\n", self, cthread_self());
    return (int) cthread_self();
}

@end

/*
 *  Run the program twice... the first process will be the server, the second
 *  will be a client.
 */

main()
{
    id hub;	// proxy object (client) or server object

    hub = [NXConnection connectToName:"MTS example"];

    printf("I am %d\n", cthread_self());

    objc_setMultithreaded(YES);

    if (hub) {
	id core;	// object to handle invalidation notification
	id sub;
	short i;

	/*
	 *  We are a client, <hub> is our proxy.  Create <core> to handle
	 *  invalidation notifcation since we can't use <hub>
	 */
	core = [[MTS alloc] init];
	[[hub connectionForProxy] registerForInvalidationNotification:core];
	[hub setProtocolForProxy:@protocol(ServerProtocol)];

	puts("client connect");

	for (i = 0; i < 5; ++i) {
	    sleep(1);
	    printf("test-doWork %d\n", [hub doWork]);
	}

	sub = [hub newThread];
	for (i = 0; i < 5; ++i) {
	    sleep(1);
	    printf("sub %d: %d\n", i, [sub doWork]);
	}
	for (i = 0; i < 5; ++i) {
	    sleep(1);
	    printf("test-doWork %d\n", [hub doWork]);
	}
	sub = [hub newThread];
	for (i = 0; i < 5; ++i) {
	    sleep(1);
	    printf("sub %d: %d\n", i, [sub doWork]);
	}
    } else {
	id topConn;			// our server connection

	/*
	 *  Make us a server, create hub and register for invalidation
	 *  notification
	 */

	hub = [MTS new];
	printf("self is %08lx\n", hub);
	topConn = [NXConnection registerRoot:hub withName:"MTS example"];
	[topConn registerForInvalidationNotification:hub];
	puts("server wait");
	[topConn setDelegate:hub];		// so hub gets connection msg
	[topConn run];
    }
    return(0);
}

------------------------------- CUT HERE ---------------------------


--

    Matthew Dillon	    dillon@Overload.Berkeley.CA.US
    891 Regal Rd.	    uunet.uu.net!overload!dillon
    Berkeley, Ca. 94708     ham: KC6LVW (no mail drop)
    USA 		    Sandel-Avery Engineering (702)831-8000