I've been using CFMessagePort for interprocess communication for the last few 
weeks; made myself a couple little wrapper classes around 
CFMessagePortCreateLocal and CFMessagePortCreateRemote, implementing a "client" 
and "server".

I create a server only when needed and destroy it when done.  Typically, my 
other process will send an asychronous response.  So before sending a client 
message, I create a server to handle the response.  After the server's delegate 
receives the response, I release the server, which invalidates its port during 
dealloc. 

Today, after some major restructuring of my app, although such a server 
instance works fine in one instance, in another instance, the server does not 
receive its callback when I send messages to it.  I've tried it with both my 
regular "other" process and with a little I've written, both of which both 
worked yesterday.  So I think something is wrong with the way I've created my 
server in this particular instance.  The restructuring was done in that area.

I've logged and checked the client and server port names many times in the last 
couple hours.  They are correct.  And, oh, I use different port names "to" and 
"from" my app.  (Learned that the hard way.)

Since my app does some thread gymnastics, I was wondering if maybe the thread 
which should get the callback is blocked.  NSLog tells me that I am inserting 
this server's port into the run loop of the main thread, and I believe that the 
main thread is not blocked.  At least it is not by design; also, when I click 
"Pause" in the Xcode debugger and examine the call stack of 
Thread-1-<com.apple.main-thread>, I see the call stack below, which looks 
exactly the same as what I get when my app is idle.  Although I'm not a call 
stack guru, I'd say that this thread is waiting for run loop sources, as 
desired.

Also, NSLog tells me that my server is not being deallocced.

How can I troubleshoot this?

Is there any utility to maybe probe the Mach ports that my app has open?  I 
can't find anything like that.

Below, I've also pasted in the code for my server class and its delegate's 
protocol, although I don't think the problem is in there because, again, it 
works fine in one instance and worked fine in all instances until today.

Thanks,

Jerry Krinock

#0      0x9472a0fa in mach_msg_trap
#1      0x9472a867 in mach_msg
#2      0x91da637f in __CFRunLoopRun
#3      0x91da5464 in CFRunLoopRunSpecific
#4      0x91da5291 in CFRunLoopRunInMode
#5      0x99520f9c in RunCurrentEventLoopInMode
#6      0x99520d51 in ReceiveNextEventCommon
#7      0x99520bd6 in BlockUntilNextEventMatchingListInMode
#8      0x98c5378d in _DPSNextEvent
#9      0x98c52fce in -[NSApplication 
nextEventMatchingMask:untilDate:inMode:dequeue:]
#10     0x98c15247 in -[NSApplication run]
#11     0x98c0d2d9 in NSApplicationMain
#12     0x00001e52 in main at MainApp-Main.m:23


*** SSYInterappServer.h ***

#import <Cocoa/Cocoa.h>
#import "SSYInterappServerDelegate.h"

extern NSString* const SSYInterappServerErrorDomain ;

@class SSYInterappServer ;

/*!
 @brief    A server object which responds to a message sent by an 
 SSYInterAppClient object from another thread or process.
*/
@interface SSYInterappServer : NSObject {
    CFMessagePortRef m_port ;
    NSObject <SSYInterappServerDelegate> * m_delegate ;
    void* m_contextInfo ;
}

/*!
 @brief    A pointer which may be used to pass information to the
 delegate

 @details  To retrieve contextInfo in the delegate method
 -interappServer:didReceiveHeaderByte:data:, send
 -contextInfo to the interappServer.
*/
@property (nonatomic, assign) void* contextInfo ;

/*!
 @brief    Designated initializer for SSYInterappServer

 @details  Uses CFMessagePort under the hood.  This port will be added
 to the current run loop in its default mode.
 @param    portName  An arbitrary name you supply, which must be unique among
 CFMessagePorts on the computer.  Suggest using a reverse DNS identifier.
 Pass this same portName when sending a message to this server from the
 SSYInterappClient class.
 @param    delegate  An object to which will be sent Objective-C messages 
whenever
 an interapp message is received by the receiver from a SSYInterAppClient.
 @result   The SSYInterappServer object.  When this object is released, its
 port (CFMessagePort) will be invalidated and released.
*/
- (id)initWithPortName:(NSString*)portName
              delegate:(NSObject <SSYInterappServerDelegate> *)delegate ;

@end


*** SSYInterappServer.m ***

#import "SSYInterappServer.h"


NSString* const SSYInterappServerErrorDomain =
                        @"SSYInterappServerErrorDomain" ;

@interface SSYInterappServer () 

@property (assign, nonatomic) NSObject <SSYInterappServerDelegate> * delegate ;

@end

CFDataRef SSYInterappServerCallBack(
                                    CFMessagePortRef port,
                                    SInt32 msgid,
                                    CFDataRef data,
                                    void* info) {
    NSLog(@"79051: Server received data on thread %@ %@",
      [NSThread currentThread],
       [[NSThread currentThread] isMainThread] ? @"main" : @"nonMain") ;
    // Unpack the data and send to delegate
    char headerByte = 0 ;
    if ([(NSData*)data length] > 0) {
        [(NSData*)data getBytes:&headerByte
                         length:1] ;
    }
    NSData* rxPayload = nil ;
    if ([(NSData*)data length] > 1) {
        rxPayload = [(NSData*)data subdataWithRange:
            NSMakeRange(1, [(NSData*)data length] - 1)] ;
    }
    
    SSYInterappServer* server = (SSYInterappServer*)info ;
    
    NSObject <SSYInterappServerDelegate> * delegate = [server delegate] ;
    [delegate interappServer:server
        didReceiveHeaderByte:headerByte
                        data:rxPayload] ;
    
    // Get response from delegate and return to Client
    NSMutableData* responseData = [[NSMutableData alloc] init];
    char responseHeaderByte = [delegate responseHeaderByte] ;
    [responseData appendBytes:(const void*)&responseHeaderByte
                       length:1] ;

    NSData* responsePayload = [delegate responsePayload] ;
    if (responsePayload) {
        [responseData appendData:responsePayload] ;
    }
    
    // From CFMessagePortCallBack documentation, we return the
    // "data to send back to the sender of the message.  The system
    // releases the returned CFData object."
    return (CFDataRef)responseData ;
}


@implementation SSYInterappServer

@synthesize delegate = m_delegate ;
@synthesize contextInfo = m_contextInfo ;

- (CFMessagePortRef)port {
    return m_port ;
}

- (void)dealloc {
    NSLog(@"79052: Dealloccing server %p on thread %@ %@",
        self,
        [NSThread currentThread],
        [[NSThread currentThread] isMainThread] ? @"main" : @"nonMain") ;
    CFMessagePortInvalidate(m_port) ;
    if (m_port) {
        // It is important not to leak a CFMessagePort, not only for
        // the usual reasons, but because, even though a port with a
        // given name has been invalidated, the system will still refuse
        // to create a new port with the same name until the
        // invalidated CFMessagePort has been *deallocated*.  If this
        // happens, the following message will be printed to console
        // upon invoking the next CFMessagePortCreateLocal() …
        // *** CFMessagePort: bootstrap_register(): failed 1103 (0x44f)
        // 'Service name already exists'
        // And, of course, CFMessagePortCreateLocal() will return NULL.
        CFRelease(m_port) ;
    }
    
    [super dealloc] ;
}

- (id)initWithPortName:(NSString*)portName
              delegate:(NSObject <SSYInterappServerDelegate> *)delegate {
    self = [super init] ;
    if (self) {
        CFMessagePortContext context ;
        context.version = 0 ;
        context.info = self ;
        context.retain = NULL ;
        context.release = NULL ;
        context.copyDescription = NULL ;
        
        m_port = CFMessagePortCreateLocal(
                                          NULL, 
                                          (CFStringRef)portName,
                                          SSYInterappServerCallBack,
                                          &context,
                                          NULL) ;
        if (m_port) {
            NSLog(@"79050: Starting server %p with port name: %@ on thread %@ 
%@",
               self, portName, [NSThread currentThread],
               [[NSThread currentThread] isMainThread] ? @"main" : @"nonMain") ;
            [self setDelegate:delegate] ;
            CFRunLoopSourceRef source = CFMessagePortCreateRunLoopSource(
                                                                         NULL, 
                                                                         m_port,
                                                                         0) ;
            CFRunLoopAddSource(
                               CFRunLoopGetCurrent(),
                               source, 
                               kCFRunLoopDefaultMode) ;
            // Note: Leaking 'source' will also leak m_port in an apparent 
retain cycle.
            CFRelease(source) ;
        }
        else {
            // See 
http://lists.apple.com/archives/Objc-language/2008/Sep/msg00133.html ...
            [super dealloc] ;
            self = nil ;
        }
    }
    
    return self ;
}

@end


*** SSYInterappServerDelgate.h (Formal Protocol) ***

#import <Cocoa/Cocoa.h>

@class SSYInterappServer ;


@protocol SSYInterappServerDelegate

/*!
 @brief    This message will be received from the delegating SSYInterappServer
 whenever an interapp message is received from the SSYInterappClient class
 on another thread or process.

 @details  Immediately upon return of your implementation, and on the same 
thread,
 -responseHeaderByte and -responsePayload will be invoked, and the values 
 you return in those methods will be used to construct a response to the 
interapp
 message.
 @param    server  The delegating SSYInterappServer which sent this message
 @param    headerByte  The header byte which was provided to the 
SSYInterappClient
 class when sending the message on the other thread or process
 @param    data  The payload data which was provided to the SSYInterappClient
 class when sending the message on the other thread or process
*/
- (void)interappServer:(SSYInterappServer*)server
  didReceiveHeaderByte:(char)headerByte
                                  data:(NSData*)data ;

/*!
 @brief    The header byte which will be sent back to an SSYInterappClient
 on another thread or process in the response to an interapp message

 @details  Typically, you will implement this as the getter of a
 property.  You will set this property during your implementation of
 -interappServer:didReceiveHeaderByte:data, after computing the appropriate
 response to the given interapp message.
*/
- (char)responseHeaderByte ;

/*!
 @brief    The payload data which will be sent back to an SSYInterappClient
 on another thread or process in the response to an interapp message
 
 @details  Typically, you will implement this as the getter of a
 property.  You will set this property during your implementation of
 -interappServer:didReceiveHeaderByte:data, after computing the appropriate
 response to the given interapp message.
 */
- (NSData*)responsePayload ;

@end

_______________________________________________

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 arch...@mail-archive.com

Reply via email to