Troubleshooting CFMessagePort
Troubleshooting CFMessagePort
- Subject: Troubleshooting CFMessagePort
- From: Jerry Krinock <email@hidden>
- Date: Thu, 6 Jan 2011 14:42:02 -0800
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 (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