// If the connection to the debugger engine is currently active.
BOOL connected_;
+ // The thread on which network operations are performed. Weak.
+ NSThread* thread_;
+
// Reference to the message loop that the socket runs on. Weak.
NSRunLoop* runLoop_;
int packetSize_;
int currentPacketIndex_;
- // The delegate.
- id <DebuggerConnectionDelegate> delegate_;
+ // The delegate. All methods are executed on the main thread.
+ NSObject<DebuggerConnectionDelegate>* delegate_;
}
@property (readonly) NSUInteger port;
- (void)send:(NSString*)command;
+// This sends the given command format to the debugger. This method is thread
+// safe and schedules the request on the |runLoop_|.
- (NSNumber*)sendCommandWithFormat:(NSString*)format, ...;
- (NSString*)escapedURIPath:(NSString*)path;
- (void)handleResponse:(NSXMLDocument*)response;
- (void)handlePacket:(NSString*)packet;
+// Threadsafe wrappers for the delegate's methods.
- (void)errorEncountered:(NSString*)error;
+- (LogEntry*)recordSend:(NSString*)command;
+- (LogEntry*)recordReceive:(NSString*)command;
@end
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ thread_ = [NSThread currentThread];
runLoop_ = [NSRunLoop currentRunLoop];
// Pass ourselves to the callback so we don't have to use ugly globals.
[runLoop_ run];
+ thread_ = nil;
+ runLoop_ = nil;
+
[pool release];
}
va_end(args);
NSNumber* callbackKey = [NSNumber numberWithInt:transactionID++];
- [self send:[NSString stringWithFormat:@"%@ -i %@", [command autorelease], callbackKey]];
+ NSString* taggedCommand = [NSString stringWithFormat:@"%@ -i %@", [command autorelease], callbackKey];
+ [self performSelector:@selector(send:)
+ onThread:thread_
+ withObject:taggedCommand
+ waitUntilDone:YES];
return callbackKey;
}
// Private /////////////////////////////////////////////////////////////////////
#pragma mark Private
+// Delegate Thread-Safe Wrappers ///////////////////////////////////////////////
+
/**
* Receives errors from the SocketWrapper and updates the display
*/
- (void)errorEncountered:(NSString*)error
{
- [delegate_ errorEncountered:error];
+ [delegate_ performSelectorOnMainThread:@selector(errorEncountered:)
+ withObject:error
+ waitUntilDone:NO];
+}
+
+- (LogEntry*)recordSend:(NSString*)command
+{
+ LoggingController* logger = [(AppDelegate*)[NSApp delegate] loggingController];
+ [logger performSelectorOnMainThread:@selector(recordSend:)
+ withObject:command
+ waitUntilDone:NO];
+ return [logger.logEntries lastObject];
}
+- (LogEntry*)recordReceive:(NSString*)command
+{
+ LoggingController* logger = [(AppDelegate*)[NSApp delegate] loggingController];
+ [logger performSelectorOnMainThread:@selector(recordReceive:)
+ withObject:command
+ waitUntilDone:NO];
+ return [logger.logEntries lastObject];
+}
+
+// Stream Managers /////////////////////////////////////////////////////////////
+
/**
* Callback from the CFReadStream that there is data waiting to be read.
*/
}
// Log this receive event.
- LoggingController* logger = [(AppDelegate*)[NSApp delegate] loggingController];
- LogEntry* log = [logger recordReceive:currentPacket_];
+ LogEntry* log = [self recordReceive:currentPacket_];
log.error = error;
log.lastWrittenTransactionID = lastWrittenTransaction_;
log.lastReadTransactionID = lastReadTransaction_;
if ([error count] > 0)
{
NSLog(@"Xdebug error: %@", error);
- [delegate_ errorEncountered:[[[[error objectAtIndex:0] children] objectAtIndex:0] stringValue]];
+ NSString* errorMessage = [[[[error objectAtIndex:0] children] objectAtIndex:0] stringValue];
+ [self errorEncountered:errorMessage];
}
if ([[[response rootElement] name] isEqualToString:@"init"])
{
- [delegate_ handleInitialResponse:response];
+ [delegate_ performSelectorOnMainThread:@selector(handleInitialResponse:)
+ withObject:response
+ waitUntilDone:NO];
return;
}
if ([delegate_ respondsToSelector:@selector(handleResponse:)])
- [(NSObject*)delegate_ performSelectorOnMainThread:@selector(handleResponse:) withObject:response waitUntilDone:NO];
+ [delegate_ performSelectorOnMainThread:@selector(handleResponse:)
+ withObject:response
+ waitUntilDone:NO];
[self sendQueuedWrites];
}
}
// Log this trancation.
- LoggingController* logger = [(AppDelegate*)[NSApp delegate] loggingController];
- LogEntry* log = [logger recordSend:command];
+ LogEntry* log = [self recordSend:command];
log.lastWrittenTransactionID = lastWrittenTransaction_;
log.lastReadTransactionID = lastReadTransaction_;
}