Inter-thread communication with NSPort

Tima <[email protected]>
Newsgroups gmane.comp.lib.gnustep.general,gmane.comp.lib.gnustep.user
Message-ID <63924b6370378e837eb5703dad647363@tv>
Hi,

I'm trying to make a client-server application in GNUstep.
Right now it seems that the best way to do the client is to
make it a two-threaded application, with a main thread processing
GUI events and a "worker" thread waiting and reading messages
from socket-related NSInputStream.

I want the worker thread to notify the main one when the message is 
ready.
After reading Apple documentation for some time I got an impression
that I need to send message to main thread's NSPort, it will be 
delivered
via -handlePortMessage: method of the port's delegate.

First of all, is it really the intended way to do communication
between threads?

Secondly, I tried to do this myself, but my message does not seem
to propagate from worker thread into the main one: the
-handlePortMessage: wasn't called.

Could you tell me what's wrong in the following program?

I apologize for rather big size of it, but that's the minimal size of
the test case I could get.

Thank you,
Tima

---------------------------- begin thread.m 
---------------------------------
#include <AppKit/AppKit.h>

@interface WorkerThread : NSObject
{
     NSPort * _remotePort;
}

+ (void) startThreadWithObject: (id) object;

- (void) confirmLaunchToPort: (NSPort *) remotePort;
- (void) handlePortMessage:(NSPortMessage *) portMessage;

@end


@implementation WorkerThread

- (id ) init
{
     _remotePort = NULL;
     return [super init];
}

- (void) dealloc
{
     [_remotePort release];
     [super dealloc];
}

+ (void) startThreadWithObject: (id) inputObject
{
     NSAutoreleasePool * pool = [NSAutoreleasePool new];

     NSPort * remotePort = [[NSMessagePortNameServer sharedInstance]
                               portForName: @"MainThread"];

     if ( remotePort )
     {
         WorkerThread * worker = [self new];

         [worker confirmLaunchToPort: remotePort];

         //[remotePort release];

         NSLog( @"WorkerThread: starting message loop" );

         [[NSRunLoop currentRunLoop] run];

         [worker release];
         [pool release];
     }

     NSLog( @"WorkerThread: exiting thread" );
}

- (void) confirmLaunchToPort: (NSPort *) remotePort
{
     // Save remote port
     _remotePort = remotePort;
     [_remotePort retain];

     // Configure local port
     NSPort * localPort = [[[NSMessagePort alloc] init] retain];

     [[NSMessagePortNameServer sharedInstance] registerPort:localPort
                                               forName: 
@"WorkerThread"];
     [localPort setDelegate: self];

     [[NSRunLoop currentRunLoop] addPort: localPort
                                 forMode: NSDefaultRunLoopMode];

     // Create confirmation message

     NSArray * comps =
         [NSArray arrayWithObject: [NSData dataWithBytes: "0" length: 1 
]];

     NSLog( @"WorkerThread: -confirmLaunch: localPort=0x%x, 
remotePort=0x%x",
            localPort, _remotePort );

     NSPortMessage * message =
         [[NSPortMessage alloc] initWithSendPort: _remotePort
                                receivePort: localPort
                                components: comps];

     if ( message )
     {
         BOOL res = [message sendBeforeDate:[NSDate distantFuture]];
         NSLog( @"WorkerThread: sendBeforeDate: %s", res ? "ok" : 
"FAIL" );
     }
}

- (void) handlePortMessage:(NSPortMessage *) portMessage
{
     unsigned int messageID = [portMessage msgid];
     NSLog( @"WorkerThread: port message, ID = %d", messageID );
}

@end /* WorkerThread */

@interface AppController : NSObject

- (void) applicationWillFinishLaunching: (NSNotification *)not;
- (void) createMenu;
- (void) startWorkerThread;
- (void) handlePortMessage:(NSPortMessage *) portMessage;

@end

@implementation AppController

- (void) startWorkerThread
{
     NSLog( @"AppController: Starting thread" );

     NSPort * localPort = [[[NSMessagePort alloc] init] retain];
     NSLog( @"AppController: localPort=0x%x", localPort );
     if ( localPort )
     {
         // Register local NSMessagePort
         [[NSMessagePortNameServer sharedInstance] 
registerPort:localPort
                                                   forName: 
@"MainThread"];

         // Set delegate
         [localPort setDelegate: self];

         // Add port to run loop
         [[NSRunLoop currentRunLoop] addPort: localPort
                                     forMode: NSDefaultRunLoopMode];

         // Launch the thread
         [NSThread 
detachNewThreadSelector:@selector(startThreadWithObject:)
                   toTarget:[WorkerThread class] withObject: 0];
     }
}

// Handle responses from the worker thread.

- (void) createMenu
{
     NSMenu * menu = [[NSMenu new] autorelease];
     [NSApp setMainMenu: menu];

     [menu addItemWithTitle:@"Quit"
           action:@selector(terminate:)
           keyEquivalent:@"q"];
}

- (void) applicationWillFinishLaunching: (NSNotification *)not
{
     [self createMenu];
     [self startWorkerThread];
}

- (void) handlePortMessage:(NSPortMessage *) portMessage
{
     unsigned int messageID = [portMessage msgid];
     NSLog( @"AppController: port message, ID = %d", messageID );
}

@end /* AppController */

int main (int argc, const char **argv)
{
     [NSApplication sharedApplication];
     [NSApp setDelegate: [AppController new]];

     return NSApplicationMain (argc, argv);
}
----------------------- end thread.m 
---------------------------------------
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.