From f9738f2650db92bcfd4414f41a666066717c5645 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Tue, 18 Jun 2013 01:21:05 -0400 Subject: [PATCH] Gut the old NetworkConnection and NetworkCallbackController classes. The old API will bridge to the new ProtocolClient until the callers can be updated. --- MacGDBp.xcodeproj/project.pbxproj | 8 - Source/NetworkCallbackController.h | 95 -------- Source/NetworkCallbackController.mm | 302 ----------------------- Source/NetworkConnection.h | 61 +---- Source/NetworkConnection.mm | 364 +++------------------------- Source/NetworkConnectionPrivate.h | 49 ---- Source/ProtocolClient.h | 2 +- 7 files changed, 36 insertions(+), 845 deletions(-) delete mode 100644 Source/NetworkCallbackController.h delete mode 100644 Source/NetworkCallbackController.mm delete mode 100644 Source/NetworkConnectionPrivate.h diff --git a/MacGDBp.xcodeproj/project.pbxproj b/MacGDBp.xcodeproj/project.pbxproj index b1fc552..c930fda 100644 --- a/MacGDBp.xcodeproj/project.pbxproj +++ b/MacGDBp.xcodeproj/project.pbxproj @@ -48,7 +48,6 @@ 1EEBFD120D359A9F008F835B /* dimple.png in Resources */ = {isa = PBXBuildFile; fileRef = 1EEBFD110D359A9F008F835B /* dimple.png */; }; 1EEE875D0D9DE4B4009CBA7C /* MacGDBp.icns in Resources */ = {isa = PBXBuildFile; fileRef = 1EEE875C0D9DE4B4009CBA7C /* MacGDBp.icns */; }; 1EFBE63012C515C200F96D6E /* NetworkConnection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1EFBE62F12C515C200F96D6E /* NetworkConnection.mm */; }; - 1EFBE66B12C51E3900F96D6E /* NetworkCallbackController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1EFBE66A12C51E3900F96D6E /* NetworkCallbackController.mm */; }; 1EFF70C30DFDC018006B9D33 /* BreakpointController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EFF70C20DFDC018006B9D33 /* BreakpointController.m */; }; 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; @@ -135,9 +134,6 @@ 1EEBFD110D359A9F008F835B /* dimple.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = dimple.png; sourceTree = ""; }; 1EEE875C0D9DE4B4009CBA7C /* MacGDBp.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = MacGDBp.icns; path = Icons/MacGDBp.icns; sourceTree = ""; }; 1EFBE62F12C515C200F96D6E /* NetworkConnection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = NetworkConnection.mm; path = Source/NetworkConnection.mm; sourceTree = ""; }; - 1EFBE66912C51E3900F96D6E /* NetworkCallbackController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NetworkCallbackController.h; path = Source/NetworkCallbackController.h; sourceTree = ""; }; - 1EFBE66A12C51E3900F96D6E /* NetworkCallbackController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = NetworkCallbackController.mm; path = Source/NetworkCallbackController.mm; sourceTree = ""; }; - 1EFBE66C12C51EFA00F96D6E /* NetworkConnectionPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NetworkConnectionPrivate.h; path = Source/NetworkConnectionPrivate.h; sourceTree = ""; }; 1EFF70C10DFDC018006B9D33 /* BreakpointController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BreakpointController.h; path = Source/BreakpointController.h; sourceTree = ""; }; 1EFF70C20DFDC018006B9D33 /* BreakpointController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BreakpointController.m; path = Source/BreakpointController.m; sourceTree = ""; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -234,10 +230,7 @@ isa = PBXGroup; children = ( 1E0724E111B47BCC0017AD3C /* NetworkConnection.h */, - 1EFBE66C12C51EFA00F96D6E /* NetworkConnectionPrivate.h */, 1EFBE62F12C515C200F96D6E /* NetworkConnection.mm */, - 1EFBE66912C51E3900F96D6E /* NetworkCallbackController.h */, - 1EFBE66A12C51E3900F96D6E /* NetworkCallbackController.mm */, 1E02C56F0C610158006F1752 /* DebuggerBackEnd.h */, 1E02C5700C610158006F1752 /* DebuggerBackEnd.m */, 1E35FFB00C65A74C0030F527 /* NSXMLElementAdditions.h */, @@ -487,7 +480,6 @@ 1EC1337E127DBB00007946FC /* VariableNode.m in Sources */, 1EC6965812BBC6A700A8D984 /* modp_b64.cc in Sources */, 1EFBE63012C515C200F96D6E /* NetworkConnection.mm in Sources */, - 1EFBE66B12C51E3900F96D6E /* NetworkCallbackController.mm in Sources */, 1EDA9CF812DD13B300596211 /* BSLineNumberRulerView.mm in Sources */, 1E11814A1319805E003BFEF1 /* BSSourceViewTextView.m in Sources */, 1E108E40136CC8B9002E34E0 /* EvalController.m in Sources */, diff --git a/Source/NetworkCallbackController.h b/Source/NetworkCallbackController.h deleted file mode 100644 index 7ac5810..0000000 --- a/Source/NetworkCallbackController.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * MacGDBp - * Copyright (c) 2007 - 2011, Blue Static - * - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU - * General Public License as published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program; if not, - * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#import -#import - -@class NetworkConnection; - -// This class is used for the CFNetwork callbacks. It is a private class and -// the instance is owned by the NewtorkConnection instance. This class can be -// considered an extension of NetworkConnection. -class NetworkCallbackController -{ - public: - // This object should be constructed on the thread which the streams are - // to be scheduled on. It will hold a weak reference to the run loop on that - // thread. - explicit NetworkCallbackController(NetworkConnection* connection); - - // Creates a socket and schedules it on the current run loop. - void OpenConnection(NSUInteger port); - - // Closes down the read/write streams. - void CloseConnection(); - - // Checks whether the write stream is ready for writing. - BOOL WriteStreamCanAcceptBytes(); - - // Writes the string to the write stream. This will block, so be sure to check - // if it can write before calling this. Returns YES if the string was - // successfully written. - BOOL WriteString(NSString* string); - - private: - // These static methods forward an invocation to the instance methods. The - // last void pointer, named |self|, is the instance of this class. - static void SocketAcceptCallback(CFSocketRef socket, - CFSocketCallBackType callbackType, - CFDataRef address, - const void* data, - void* self); - static void ReadStreamCallback(CFReadStreamRef stream, - CFStreamEventType eventType, - void* self); - static void WriteStreamCallback(CFWriteStreamRef stream, - CFStreamEventType eventType, - void* self); - - void OnSocketAccept(CFSocketRef socket, - CFDataRef address, - const void* data); - void OnReadStreamEvent(CFReadStreamRef stream, CFStreamEventType eventType); - void OnWriteStreamEvent(CFWriteStreamRef stream, CFStreamEventType eventType); - - // Closes down the listening socket but keeps the streams alive. This can be - // called multiple times, even if the socket is NULL. - void CloseSocket(); - - // Removes the read or write stream from the run loop, closes the stream, - // releases the reference. - void UnscheduleReadStream(); - void UnscheduleWriteStream(); - - // Messages the NetworkConnection's delegate and takes ownership of |error|. - void ReportError(CFErrorRef error); - - // The socket that listens for incoming connections from the engine. - CFSocketRef listeningSocket_; // Strong. - - // The child socket from the |listeningSocket_| that is connected to the - // engine. The streams are attached to this socket. - CFSocketNativeHandle socketHandle_; - - // The read and write streams that are scheduled on the |runLoop_|. Both are - // weak and are owned by the run loop source. - CFReadStreamRef readStream_; - CFWriteStreamRef writeStream_; - - NetworkConnection* connection_; // Weak, owns this. - CFRunLoopRef runLoop_; // Weak. -}; - diff --git a/Source/NetworkCallbackController.mm b/Source/NetworkCallbackController.mm deleted file mode 100644 index bbe5cbd..0000000 --- a/Source/NetworkCallbackController.mm +++ /dev/null @@ -1,302 +0,0 @@ -/* - * MacGDBp - * Copyright (c) 2007 - 2011, Blue Static - * - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU - * General Public License as published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program; if not, - * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#import "NetworkCallbackController.h" - -#import -#import - -#import "NetworkConnection.h" -#import "NetworkConnectionPrivate.h" - -NetworkCallbackController::NetworkCallbackController(NetworkConnection* connection) - : listeningSocket_(NULL), - socketHandle_(NULL), - readStream_(NULL), - writeStream_(NULL), - connection_(connection), - runLoop_(CFRunLoopGetCurrent()) -{ -} - -void NetworkCallbackController::OpenConnection(NSUInteger port) -{ - // Pass ourselves to the callback so we don't have to use ugly globals. - CFSocketContext context = { 0 }; - context.info = this; - - // Create the address structure. - struct sockaddr_in address; - memset(&address, 0, sizeof(address)); - address.sin_len = sizeof(address); - address.sin_family = AF_INET; - address.sin_port = htons(port); - address.sin_addr.s_addr = htonl(INADDR_ANY); - - // Create the socket signature. - CFSocketSignature signature; - signature.protocolFamily = PF_INET; - signature.socketType = SOCK_STREAM; - signature.protocol = IPPROTO_TCP; - signature.address = (CFDataRef)[NSData dataWithBytes:&address length:sizeof(address)]; - - do { - listeningSocket_ = - CFSocketCreateWithSocketSignature(kCFAllocatorDefault, - &signature, // Socket signature. - kCFSocketAcceptCallBack, // Callback types. - &NetworkCallbackController::SocketAcceptCallback, // Callout function pointer. - &context); // Context to pass to callout. - if (!listeningSocket_) { - [connection_ errorEncountered:@"Could not open socket."]; - sleep(1); - } - } while (!listeningSocket_); - - // Allow old, yet-to-be recycled sockets to be reused. - BOOL yes = YES; - setsockopt(CFSocketGetNative(listeningSocket_), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(BOOL)); - setsockopt(CFSocketGetNative(listeningSocket_), SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(BOOL)); - - // Schedule the socket on the run loop. - CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, listeningSocket_, 0); - CFRunLoopAddSource(runLoop_, source, kCFRunLoopCommonModes); - CFRelease(source); -} - -void NetworkCallbackController::CloseConnection() -{ - UnscheduleReadStream(); - UnscheduleWriteStream(); - - if (socketHandle_) { - close(socketHandle_); - socketHandle_ = NULL; - [connection_ socketDisconnected]; - } -} - -BOOL NetworkCallbackController::WriteStreamCanAcceptBytes() -{ - return writeStream_ && CFWriteStreamCanAcceptBytes(writeStream_); -} - -BOOL NetworkCallbackController::WriteString(NSString* string) -{ - // TODO: May need to negotiate with the server as to the string encoding. - const NSStringEncoding kEncoding = NSUTF8StringEncoding; - // Add space for the NUL byte. - NSUInteger maxBufferSize = [string maximumLengthOfBytesUsingEncoding:kEncoding] + 1; - - UInt8* buffer = new UInt8[maxBufferSize]; - bzero(buffer, maxBufferSize); - - NSUInteger bufferSize = 0; - if (![string getBytes:buffer - maxLength:maxBufferSize - usedLength:&bufferSize - encoding:kEncoding - options:0 - range:NSMakeRange(0, [string length]) - remainingRange:NULL]) { - delete [] buffer; - return NO; - } - - // Include a NUL byte. - ++bufferSize; - - // Write the packet out, and spin in a busy wait loop if the stream is not ready. This - // method is only ever called in response to a stream ready event. - NSUInteger totalWritten = 0; - while (totalWritten < bufferSize) { - if (WriteStreamCanAcceptBytes()) { - CFIndex bytesWritten = CFWriteStreamWrite(writeStream_, buffer + totalWritten, bufferSize - totalWritten); - if (bytesWritten < 0) { - CFErrorRef error = CFWriteStreamCopyError(writeStream_); - ReportError(error); - break; - } - totalWritten += bytesWritten; - } - } - - delete [] buffer; - return YES; -} - -// Static Methods ////////////////////////////////////////////////////////////// - -void NetworkCallbackController::SocketAcceptCallback(CFSocketRef socket, - CFSocketCallBackType callbackType, - CFDataRef address, - const void* data, - void* self) -{ - assert(callbackType == kCFSocketAcceptCallBack); - static_cast(self)->OnSocketAccept(socket, address, data); -} - -void NetworkCallbackController::ReadStreamCallback(CFReadStreamRef stream, - CFStreamEventType eventType, - void* self) -{ - static_cast(self)->OnReadStreamEvent(stream, eventType); -} - -void NetworkCallbackController::WriteStreamCallback(CFWriteStreamRef stream, - CFStreamEventType eventType, - void* self) -{ - static_cast(self)->OnWriteStreamEvent(stream, eventType); -} - - -// Private Instance Methods //////////////////////////////////////////////////// - -void NetworkCallbackController::OnSocketAccept(CFSocketRef socket, - CFDataRef address, - const void* data) -{ - // Keep a reference to the socket handle of the child socket. Do not create - // a CFSocket with this because doing so prohibits the use of streams. The - // kCFSocketDataCallBack would have to be used instead. - socketHandle_ = *(CFSocketNativeHandle*)data; - - // Create the streams on the socket. - CFStreamCreatePairWithSocket(kCFAllocatorDefault, - socketHandle_, // Socket handle. - &readStream_, // Read stream in-pointer. - &writeStream_); // Write stream in-pointer. - - // Create struct to register callbacks for the stream. - CFStreamClientContext context = { 0 }; - context.info = this; - - // Set the client of the read stream. - CFOptionFlags readFlags = kCFStreamEventOpenCompleted | - kCFStreamEventHasBytesAvailable | - kCFStreamEventErrorOccurred | - kCFStreamEventEndEncountered; - if (CFReadStreamSetClient(readStream_, readFlags, &NetworkCallbackController::ReadStreamCallback, &context)) - // Schedule in run loop to do asynchronous communication with the engine. - CFReadStreamScheduleWithRunLoop(readStream_, runLoop_, kCFRunLoopCommonModes); - else - return; - - // Open the stream now that it's scheduled on the run loop. - if (!CFReadStreamOpen(readStream_)) { - ReportError(CFReadStreamCopyError(readStream_)); - return; - } - - // Set the client of the write stream. - CFOptionFlags writeFlags = kCFStreamEventOpenCompleted | - kCFStreamEventCanAcceptBytes | - kCFStreamEventErrorOccurred | - kCFStreamEventEndEncountered; - if (CFWriteStreamSetClient(writeStream_, writeFlags, &NetworkCallbackController::WriteStreamCallback, &context)) - // Schedule it in the run loop to receive error information. - CFWriteStreamScheduleWithRunLoop(writeStream_, runLoop_, kCFRunLoopCommonModes); - else - return; - - // Open the write stream. - if (!CFWriteStreamOpen(writeStream_)) { - ReportError(CFWriteStreamCopyError(writeStream_)); - return; - } - - [connection_ socketDidAccept]; - - CloseSocket(); -} - -void NetworkCallbackController::OnReadStreamEvent(CFReadStreamRef stream, - CFStreamEventType eventType) -{ - switch (eventType) - { - case kCFStreamEventHasBytesAvailable: - if (readStream_) - [connection_ readStreamHasData:stream]; - break; - - case kCFStreamEventErrorOccurred: - ReportError(CFReadStreamCopyError(stream)); - CloseConnection(); - break; - - case kCFStreamEventEndEncountered: - CloseConnection(); - break; - }; -} - -void NetworkCallbackController::OnWriteStreamEvent(CFWriteStreamRef stream, - CFStreamEventType eventType) -{ - switch (eventType) - { - case kCFStreamEventCanAcceptBytes: - [connection_ sendQueuedWrites]; - break; - - case kCFStreamEventErrorOccurred: - ReportError(CFWriteStreamCopyError(stream)); - CloseConnection(); - break; - - case kCFStreamEventEndEncountered: - CloseConnection(); - break; - } -} - -void NetworkCallbackController::CloseSocket() -{ - if (listeningSocket_) { - CFSocketInvalidate(listeningSocket_); - CFRelease(listeningSocket_); - listeningSocket_ = NULL; - } -} - -void NetworkCallbackController::UnscheduleReadStream() -{ - if (!readStream_) - return; - CFReadStreamUnscheduleFromRunLoop(readStream_, runLoop_, kCFRunLoopCommonModes); - CFReadStreamClose(readStream_); - CFRelease(readStream_); - readStream_ = NULL; -} - -void NetworkCallbackController::UnscheduleWriteStream() -{ - if (!writeStream_) - return; - CFWriteStreamUnscheduleFromRunLoop(writeStream_, runLoop_, kCFRunLoopCommonModes); - CFWriteStreamClose(writeStream_); - CFRelease(writeStream_); - writeStream_ = NULL; -} - -void NetworkCallbackController::ReportError(CFErrorRef error) -{ - [connection_ errorEncountered:[(NSError*)error description]]; - CFRelease(error); -} diff --git a/Source/NetworkConnection.h b/Source/NetworkConnection.h index dd8b621..74c5ed6 100644 --- a/Source/NetworkConnection.h +++ b/Source/NetworkConnection.h @@ -16,11 +16,7 @@ #import -#ifdef __cplusplus -class NetworkCallbackController; -#else -@class NetworkCallbackController; -#endif +#import "ProtocolClient.h" @protocol NetworkConnectionDelegate; @class LoggingController; @@ -29,52 +25,16 @@ class NetworkCallbackController; // the intricacies of network and stream programming. Almost all the work this // class does is on a background thread, which is created when the connection is // asked to connect and shutdown when asked to close. -@interface NetworkConnection : NSObject +@interface NetworkConnection : ProtocolClient { // The port to connect on. NSUInteger port_; - + + ProtocolClient* _ideClient; + // 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_; - - // Internal class that manages CFNetwork callbacks. Strong. - NetworkCallbackController* callbackController_; - - // Run loop source used to quit the thread. Strong. - CFRunLoopSourceRef quitSource_; - - // An ever-increasing integer that gives each transaction a unique ID for the - // debugging engine. - NSInteger transactionID; - - // The most recently received transaction ID. - NSInteger lastReadTransaction_; - - // The last transactionID written to the stream. - NSInteger lastWrittenTransaction_; - - // To prevent blocked writing, we enqueue all writes and then wait for the - // write stream to tell us it's ready. We store the pending commands in this - // array. We use this as a queue (FIFO), with index 0 being first. - NSMutableArray* queuedWrites_; - - // We send queued writes in multiple places, sometimes off a run loop event. - // Because of this, we need to ensure that only one client is dequeing and - // sending at a time. - NSRecursiveLock* writeQueueLock_; - - // Information about the current read loop. We append to |currentPacket_| - // until |currentPacketSize_| has reached |packetSize_|. - NSMutableString* currentPacket_; - NSInteger packetSize_; - NSInteger currentPacketIndex_; - // The delegate. All methods are executed on the main thread. NSObject* delegate_; } @@ -88,18 +48,7 @@ class NetworkCallbackController; - (void)connect; - (void)close; -// 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, ...; - -// Sends a command to the debugger. The command must have a substring |{txn}| -// within it, which will be replaced with the transaction ID. Use this if -// |-sendCommandWithFormat:|'s insertion of the transaction ID is incorrect. -- (NSNumber*)sendCustomCommandWithFormat:(NSString*)format, ...; - - (NSString*)escapedURIPath:(NSString*)path; -- (NSInteger)transactionIDFromResponse:(NSXMLDocument*)response; -- (NSInteger)transactionIDFromCommand:(NSString*)command; @end diff --git a/Source/NetworkConnection.mm b/Source/NetworkConnection.mm index 2fae29f..0bd3f99 100644 --- a/Source/NetworkConnection.mm +++ b/Source/NetworkConnection.mm @@ -15,19 +15,23 @@ */ #import "NetworkConnection.h" -#import "NetworkConnectionPrivate.h" #import "AppDelegate.h" #import "LoggingController.h" -#include "NetworkCallbackController.h" -// Other Run Loop Callbacks //////////////////////////////////////////////////// +// This is the private interface for the NetworkConnection class. This is shared +// by the C++ NetworkCallbackController to communicate. +@interface NetworkConnection (Private) + +- (void)handleResponse:(NSXMLDocument*)response; + +// Threadsafe wrappers for the delegate's methods. +- (void)errorEncountered:(NSString*)error; +- (LogEntry*)recordSend:(NSString*)command; +- (LogEntry*)recordReceive:(NSString*)command; + +@end -void PerformQuitSignal(void* info) -{ - NetworkConnection* obj = (NetworkConnection*)info; - [obj performQuitSignal]; -} //////////////////////////////////////////////////////////////////////////////// @@ -36,197 +40,50 @@ void PerformQuitSignal(void* info) @synthesize port = port_; @synthesize connected = connected_; @synthesize delegate = delegate_; -@synthesize lastReadTransaction = lastReadTransaction_; -@synthesize currentPacket = currentPacket_; -@synthesize lastWrittenTransaction = lastWrittenTransaction_; -@synthesize queuedWrites = queuedWrites_; - (id)initWithPort:(NSUInteger)aPort { if (self = [super init]) { port_ = aPort; + _ideClient = [[ProtocolClient alloc] initWithDelegate:self]; } return self; } -- (void)dealloc -{ - self.currentPacket = nil; - [super dealloc]; -} - /** * Kicks off the socket on another thread. */ - (void)connect { - if (thread_ && !connected_) { - // A thread has been detached but the socket has yet to connect. Do not - // spawn a new thread otherwise multiple threads will be blocked on the same - // socket. - return; - } - [NSThread detachNewThreadSelector:@selector(runNetworkThread) toTarget:self withObject:nil]; + [_ideClient connectOnPort:port_]; } -/** - * Creates, connects to, and schedules a CFSocket. - */ -- (void)runNetworkThread -{ - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - - thread_ = [NSThread currentThread]; - runLoop_ = [NSRunLoop currentRunLoop]; - callbackController_ = new NetworkCallbackController(self); - - // Create a source that is used to quit. - CFRunLoopSourceContext quitContext = { 0 }; - quitContext.info = self; - quitContext.perform = PerformQuitSignal; - quitSource_ = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &quitContext); - CFRunLoopAddSource([runLoop_ getCFRunLoop], quitSource_, kCFRunLoopCommonModes); - - callbackController_->OpenConnection(port_); - - CFRunLoopRun(); - - thread_ = nil; - runLoop_ = nil; - delete callbackController_; - callbackController_ = NULL; - - CFRunLoopSourceInvalidate(quitSource_); - CFRelease(quitSource_); - quitSource_ = NULL; - - if ([delegate_ respondsToSelector:@selector(connectionDidClose:)]) - [delegate_ connectionDidClose:self]; - - [pool release]; -} - -/** - * Called by SocketWrapper after the connection is successful. This immediately calls - * -[SocketWrapper receive] to clear the way for communication, though the information - * could be useful server information that we don't use right now. - */ -- (void)socketDidAccept -{ - connected_ = YES; - transactionID = 1; - lastReadTransaction_ = 0; - lastWrittenTransaction_ = 0; - self.queuedWrites = [NSMutableArray array]; - writeQueueLock_ = [NSRecursiveLock new]; - if ([delegate_ respondsToSelector:@selector(connectionDidAccept:)]) - [delegate_ performSelectorOnMainThread:@selector(connectionDidAccept:) - withObject:self - waitUntilDone:NO]; -} - -/** - * Closes a socket and releases the ref. - */ - (void)close { - if (thread_) { - [thread_ cancel]; - } - if (runLoop_ && quitSource_) { - CFRunLoopSourceSignal(quitSource_); - CFRunLoopWakeUp([runLoop_ getCFRunLoop]); - } -} - -/** - * Quits the run loop and stops the thread. - */ -- (void)performQuitSignal -{ - self.queuedWrites = nil; - connected_ = NO; - [writeQueueLock_ release]; - - if (runLoop_) { - CFRunLoopStop([runLoop_ getCFRunLoop]); - } - - callbackController_->CloseConnection(); + [_ideClient disconnect]; } -/** - * Notification that the socket disconnected. - */ -- (void)socketDisconnected +- (void)debuggerEngineConnected:(ProtocolClient*)client { - [self close]; + if ([delegate_ respondsToSelector:@selector(connectionDidAccept:)]) + [delegate_ connectionDidAccept:self]; } -/** - * Writes a command into the write stream. If the stream is ready for writing, - * we do so immediately. If not, the command is queued and will be written - * when the stream is ready. - */ -- (void)send:(NSString*)command +- (void)debuggerEngineDisconnected:(ProtocolClient*)client { - if (lastReadTransaction_ >= lastWrittenTransaction_ && callbackController_->WriteStreamCanAcceptBytes()) { - [self performSend:command]; - } else { - [writeQueueLock_ lock]; - [queuedWrites_ addObject:command]; - [writeQueueLock_ unlock]; - } - [self sendQueuedWrites]; + if ([delegate_ respondsToSelector:@selector(connectionDidClose:)]) + [delegate_ connectionDidClose:self]; } -/** - * This will send a command to the debugger engine. It will append the - * transaction ID automatically. It accepts a NSString command along with a - * a variable number of arguments to substitute into the command, a la - * +[NSString stringWithFormat:]. Returns the transaction ID as a NSNumber. - */ -- (NSNumber*)sendCommandWithFormat:(NSString*)format, ... +- (void)debuggerEngine:(ProtocolClient*)client receivedMessage:(NSXMLDocument*)message { - // Collect varargs and format command. - va_list args; - va_start(args, format); - NSString* command = [[NSString alloc] initWithFormat:format arguments:args]; - va_end(args); - - NSNumber* callbackKey = [NSNumber numberWithInt:transactionID++]; - NSString* taggedCommand = [NSString stringWithFormat:@"%@ -i %@", [command autorelease], callbackKey]; - [self performSelector:@selector(send:) - onThread:thread_ - withObject:taggedCommand - waitUntilDone:connected_]; - - return callbackKey; + [self handleResponse:message]; } -/** - * Certain commands expect encoded data to be the the last, unnamed parameter - * of the command. In these cases, inserting the transaction ID at the end is - * incorrect, so clients use this method to have |{txn}| replaced with the - * transaction ID. - */ -- (NSNumber*)sendCustomCommandWithFormat:(NSString*)format, ... +- (void)dealloc { - // Collect varargs and format command. - va_list args; - va_start(args, format); - NSString* command = [[[NSString alloc] initWithFormat:format arguments:args] autorelease]; - va_end(args); - - NSNumber* callbackKey = [NSNumber numberWithInt:transactionID++]; - NSString* taggedCommand = [command stringByReplacingOccurrencesOfString:@"{txn}" - withString:[callbackKey stringValue]]; - [self performSelector:@selector(send:) - onThread:thread_ - withObject:taggedCommand - waitUntilDone:connected_]; - - return callbackKey; + [_ideClient release]; + [super dealloc]; } /** @@ -251,27 +108,6 @@ void PerformQuitSignal(void* info) return urlString; } -/** - * Returns the transaction_id from an NSXMLDocument. - */ -- (NSInteger)transactionIDFromResponse:(NSXMLDocument*)response -{ - return [[[[response rootElement] attributeForName:@"transaction_id"] stringValue] intValue]; -} - -/** - * Scans a command string for the transaction ID component. If it is not found, - * returns NSNotFound. - */ -- (NSInteger)transactionIDFromCommand:(NSString*)command -{ - NSRange occurrence = [command rangeOfString:@"-i "]; - if (occurrence.location == NSNotFound) - return NSNotFound; - NSString* transaction = [command substringFromIndex:occurrence.location + occurrence.length]; - return [transaction intValue]; -} - // Private ///////////////////////////////////////////////////////////////////// #pragma mark Private @@ -293,8 +129,8 @@ void PerformQuitSignal(void* info) { LoggingController* logger = [[AppDelegate instance] loggingController]; LogEntry* entry = [LogEntry newSendEntry:command]; - entry.lastReadTransactionID = lastReadTransaction_; - entry.lastWrittenTransactionID = lastWrittenTransaction_; + entry.lastReadTransactionID = _lastReadID; + entry.lastWrittenTransactionID = _lastWrittenID; [logger performSelectorOnMainThread:@selector(recordEntry:) withObject:entry waitUntilDone:NO]; @@ -305,104 +141,14 @@ void PerformQuitSignal(void* info) { LoggingController* logger = [[AppDelegate instance] loggingController]; LogEntry* entry = [LogEntry newReceiveEntry:command]; - entry.lastReadTransactionID = lastReadTransaction_; - entry.lastWrittenTransactionID = lastWrittenTransaction_; + entry.lastReadTransactionID = _lastReadID; + entry.lastWrittenTransactionID = _lastWrittenID; [logger performSelectorOnMainThread:@selector(recordEntry:) withObject:entry waitUntilDone:NO]; return [entry autorelease]; } -// Stream Managers ///////////////////////////////////////////////////////////// - -/** - * Callback from the CFReadStream that there is data waiting to be read. - */ -- (void)readStreamHasData:(CFReadStreamRef)stream -{ - const NSUInteger kBufferSize = 1024; - UInt8 buffer[kBufferSize]; - CFIndex bufferOffset = 0; // Starting point in |buffer| to work with. - CFIndex bytesRead = CFReadStreamRead(stream, buffer, kBufferSize); - const char* charBuffer = (const char*)buffer; - - // The read loop works by going through the buffer until all the bytes have - // been processed. - while (bufferOffset < bytesRead) { - // Find the NULL separator, or the end of the string. - NSUInteger partLength = 0; - for (CFIndex i = bufferOffset; i < bytesRead && charBuffer[i] != '\0'; ++i, ++partLength) ; - - // If there is not a current packet, set some state. - if (!self.currentPacket) { - // Read the message header: the size. This will be |partLength| bytes. - packetSize_ = atoi(charBuffer + bufferOffset); - currentPacketIndex_ = 0; - self.currentPacket = [NSMutableString stringWithCapacity:packetSize_]; - bufferOffset += partLength + 1; // Pass over the NULL byte. - continue; // Spin the loop to begin reading actual data. - } - - // Substring the byte stream and append it to the packet string. - CFStringRef bufferString = CFStringCreateWithBytes(kCFAllocatorDefault, - buffer + bufferOffset, // Byte pointer, offset by start index. - partLength, // Length. - kCFStringEncodingUTF8, - true); - [self.currentPacket appendString:(NSString*)bufferString]; - CFRelease(bufferString); - - // Advance counters. - currentPacketIndex_ += partLength; - bufferOffset += partLength + 1; - - // If this read finished the packet, handle it and reset. - if (currentPacketIndex_ >= packetSize_) { - [self handlePacket:[[currentPacket_ retain] autorelease]]; - self.currentPacket = nil; - packetSize_ = 0; - currentPacketIndex_ = 0; - } - } -} - -/** - * Performs the packet handling of a raw string XML packet. From this point on, - * the packets are associated with a transaction and are then dispatched. - */ -- (void)handlePacket:(NSString*)packet -{ - // Test if we can convert it into an NSXMLDocument. - NSError* error = nil; - NSXMLDocument* xml = [[NSXMLDocument alloc] initWithXMLString:currentPacket_ - options:NSXMLDocumentTidyXML - error:&error]; - // TODO: Remove this assert before stable release. Flush out any possible - // issues during testing. - assert(xml); - - // Validate the transaction. - NSInteger transaction = [self transactionIDFromResponse:xml]; - if (transaction < lastReadTransaction_) { - NSLog(@"Transaction #%d is out of date (lastRead = %d). Dropping packet: %@", - transaction, lastReadTransaction_, packet); - return; - } - if (transaction != lastWrittenTransaction_) { - NSLog(@"Transaction #%d received out of order. lastRead = %d, lastWritten = %d. Continuing.", - transaction, lastReadTransaction_, lastWrittenTransaction_); - } - - lastReadTransaction_ = transaction; - - // Log this receive event. - LogEntry* log = [self recordReceive:currentPacket_]; - log.error = error; - - // Finally, dispatch the handler for this response. - [self handleResponse:[xml autorelease]]; -} - - (void)handleResponse:(NSXMLDocument*)response { // Check and see if there's an error. @@ -425,56 +171,6 @@ void PerformQuitSignal(void* info) [delegate_ performSelectorOnMainThread:@selector(handleResponse:) withObject:response waitUntilDone:NO]; - - [self sendQueuedWrites]; -} - -/** - * This performs a blocking send. This should ONLY be called when we know we - * have write access to the stream. We will busy wait in case we don't do a full - * send. - */ -- (void)performSend:(NSString*)command -{ - // If this is an out-of-date transaction, do not bother sending it. - NSInteger transaction = [self transactionIDFromCommand:command]; - if (transaction != NSNotFound && transaction < lastWrittenTransaction_) - return; - - if (callbackController_->WriteString(command)) { - // We need to scan the string to find the transactionID. - if (transaction == NSNotFound) { - NSLog(@"sent %@ without a transaction ID", command); - } - lastWrittenTransaction_ = transaction; - } - - // Log this trancation. - [self recordSend:command]; -} - -/** - * Checks if there are unsent commands in the |queuedWrites_| queue and sends - * them if it's OK to do so. This will not block. - */ -- (void)sendQueuedWrites -{ - if (!connected_) - return; - - [writeQueueLock_ lock]; - if (lastReadTransaction_ >= lastWrittenTransaction_ && [queuedWrites_ count] > 0) { - NSString* command = [queuedWrites_ objectAtIndex:0]; - - // We don't want to block because this is called from the main thread. - // |-performSend:| busy waits when the stream is not ready. Bail out - // before we do that becuase busy waiting is BAD. - if (callbackController_->WriteStreamCanAcceptBytes()) { - [self performSend:command]; - [queuedWrites_ removeObjectAtIndex:0]; - } - } - [writeQueueLock_ unlock]; } @end diff --git a/Source/NetworkConnectionPrivate.h b/Source/NetworkConnectionPrivate.h deleted file mode 100644 index 02090ad..0000000 --- a/Source/NetworkConnectionPrivate.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * MacGDBp - * Copyright (c) 2007 - 2011, Blue Static - * - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU - * General Public License as published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program; if not, - * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -@class LogEntry; - -// This is the private interface for the NetworkConnection class. This is shared -// by the C++ NetworkCallbackController to communicate. -@interface NetworkConnection () - -@property NSInteger lastReadTransaction; -@property (retain) NSMutableString* currentPacket; -@property NSInteger lastWrittenTransaction; -@property (retain) NSMutableArray* queuedWrites; - -- (void)runNetworkThread; - -- (void)socketDidAccept; -- (void)socketDisconnected; -- (void)readStreamHasData:(CFReadStreamRef)stream; - -// These methods MUST be called on the network thread as they are not threadsafe. -- (void)send:(NSString*)command; -- (void)performSend:(NSString*)command; -- (void)sendQueuedWrites; - -- (void)performQuitSignal; - -- (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 diff --git a/Source/ProtocolClient.h b/Source/ProtocolClient.h index 982edad..b0158fa 100644 --- a/Source/ProtocolClient.h +++ b/Source/ProtocolClient.h @@ -24,7 +24,7 @@ // debugger engine and receives XML packets in response. This class ensures // proper sequencing of the messages. @interface ProtocolClient : NSObject { - @private + //@private MessageQueue* _messageQueue; NSRecursiveLock* _lock; -- 2.22.5