From 5a079587677024b4fa73181cb8d4a71cebbc7d61 Mon Sep 17 00:00:00 2001
From: Robert Sesek <rsesek@bluestatic.org>
Date: Sat, 30 Oct 2010 20:11:01 -0400
Subject: [PATCH] Begin making DebuggerConnection more threadsafe.

---
 Source/DebuggerConnection.h |  9 ++++--
 Source/DebuggerConnection.m | 56 +++++++++++++++++++++++++++++++------
 2 files changed, 54 insertions(+), 11 deletions(-)

diff --git a/Source/DebuggerConnection.h b/Source/DebuggerConnection.h
index 89e8341..ca75b44 100644
--- a/Source/DebuggerConnection.h
+++ b/Source/DebuggerConnection.h
@@ -31,6 +31,9 @@
 	// 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_;
 
@@ -69,8 +72,8 @@
 	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;
@@ -84,6 +87,8 @@
 
 - (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;
diff --git a/Source/DebuggerConnection.m b/Source/DebuggerConnection.m
index 520b8c2..c17cfec 100644
--- a/Source/DebuggerConnection.m
+++ b/Source/DebuggerConnection.m
@@ -46,7 +46,10 @@
 - (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
 
@@ -227,6 +230,7 @@ void SocketAcceptCallback(CFSocketRef socket,
 {
 	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.
@@ -274,6 +278,9 @@ void SocketAcceptCallback(CFSocketRef socket,
 
 	[runLoop_ run];
 
+  thread_ = nil;
+  runLoop_ = nil;
+
 	[pool release];
 }
 
@@ -349,7 +356,11 @@ void SocketAcceptCallback(CFSocketRef socket,
 	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;
 }
@@ -400,14 +411,38 @@ void SocketAcceptCallback(CFSocketRef socket,
 // 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.
  */
@@ -540,8 +575,7 @@ void SocketAcceptCallback(CFSocketRef socket,
 	}
 	
 	// 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_;
@@ -557,17 +591,22 @@ void SocketAcceptCallback(CFSocketRef socket,
 	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];
 }
@@ -623,8 +662,7 @@ void SocketAcceptCallback(CFSocketRef socket,
 	}
 	
 	// 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_;
 }
-- 
2.43.5