From 332f6201ce607a6622fadfd3e6426e4571dc035f Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Tue, 16 Mar 2010 02:04:50 +0000 Subject: - Make a number of changes to attempt to improve disconnection/quit crashes: prevent multiple disconnects, add more checks, cancel current queries, and add a tiny delay to allow mysql cleanup. - Alter MCPStreamingResult to no longer return a retained instance, setting up correct result disposal on autorelease but changing callers to retain as soon as they receive. - Review and change a number of local variables shadowing/shielding other local or global variables. --- Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h | 1 + Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m | 20 +++++++++++++------- .../MCPKit/MCPFoundationKit/MCPStreamingResult.h | 2 ++ .../MCPKit/MCPFoundationKit/MCPStreamingResult.m | 7 ++++++- 4 files changed, 22 insertions(+), 8 deletions(-) (limited to 'Frameworks/MCPKit/MCPFoundationKit') diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h index f04fb26e..8e5a60f9 100644 --- a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h @@ -82,6 +82,7 @@ static inline NSData* NSStringDataUsingLossyEncoding(NSString* self, NSInteger e NSLock *queryLock; /* Anything that performs a mysql_net_read is not thread-safe: mysql queries, pings */ BOOL useKeepAlive; + BOOL isDisconnecting; NSInteger connectionTimeout; CGFloat keepAliveInterval; diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m index f04fe226..607e1b8e 100644 --- a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m @@ -106,6 +106,7 @@ static BOOL sTruncateLongFieldInLogs = YES; queryCancelUsedReconnect = NO; serverVersionString = nil; mTimeZone = nil; + isDisconnecting = NO; // Initialize ivar defaults connectionTimeout = 10; @@ -298,7 +299,7 @@ static BOOL sTruncateLongFieldInLogs = YES; if (mConnected && newState == PROXY_STATE_IDLE && currentProxyState == PROXY_STATE_CONNECTED) { currentProxyState = newState; [connectionProxy setConnectionStateChangeSelector:nil delegate:nil]; - [self reconnect]; + if (!isDisconnecting) [self reconnect]; return; } @@ -404,14 +405,22 @@ static BOOL sTruncateLongFieldInLogs = YES; */ - (void)disconnect { + if (isDisconnecting) return; + isDisconnecting = YES; + [self stopKeepAliveTimer]; if (mConnected) { + [self cancelCurrentQuery]; + mConnected = NO; + + // Small pause for cleanup. + usleep(100000); mysql_close(mConnection); mConnection = NULL; } - mConnected = NO; + isDisconnecting = NO; if (connectionProxy) { [connectionProxy performSelectorOnMainThread:@selector(disconnect) withObject:nil waitUntilDone:YES]; @@ -457,6 +466,7 @@ static BOOL sTruncateLongFieldInLogs = YES; } mConnected = NO; + isDisconnecting = NO; // If there is a tunnel, ensure it's disconnected and attempt to reconnect it in blocking fashion if (connectionProxy) { @@ -1290,7 +1300,6 @@ void performThreadedKeepAlive(void *ptr) /** * Takes a query string and returns an MCPStreamingResult representing the result of the query. - * The returned MCPStreamingResult is retained and the client is responsible for releasing it. * If no fields are present in the result, nil will be returned. * Uses safe/fast mode, which may use more memory as results are downloaded. */ @@ -1301,7 +1310,6 @@ void performThreadedKeepAlive(void *ptr) /** * Takes a query string and returns an MCPStreamingResult representing the result of the query. - * The returned MCPStreamingResult is retained and the client is responsible for releasing it. * If no fields are present in the result, nil will be returned. * Can be used in either fast/safe mode, where data is downloaded as fast as possible to avoid * blocking the server, or in full streaming mode for lowest memory usage but potentially blocking @@ -1316,7 +1324,6 @@ void performThreadedKeepAlive(void *ptr) * Error checks connection extensively - if this method fails due to a connection error, it will ask how to * proceed and loop depending on the status, not returning control until either the query has been executed * and the result can be returned or the connection and document have been closed. - * If using streamingResult, the caller is responsible for releasing the result set. */ - (id)queryString:(NSString *) query usingEncoding:(NSStringEncoding) encoding streamingResult:(NSInteger) streamResultType { @@ -1516,7 +1523,6 @@ void performThreadedKeepAlive(void *ptr) (void)(*startKeepAliveTimerPtr)(self, startKeepAliveTimerSEL, YES); if (!theResult) return nil; - if (streamResultType != MCP_NO_STREAMING) return theResult; return [theResult autorelease]; } @@ -1618,7 +1624,7 @@ void performThreadedKeepAlive(void *ptr) // Reset the connection [self unlockConnection]; - [self reconnect]; + if (!isDisconnecting) [self reconnect]; // Set queryCancelled again to handle requery cleanups, and return. queryCancelled = YES; diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.h b/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.h index 2f5ec638..65ee6423 100644 --- a/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.h +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.h @@ -53,6 +53,8 @@ typedef struct SP_MYSQL_ROWS { unsigned long freedRowCount; pthread_mutex_t dataCreationLock; pthread_mutex_t dataFreeLock; + IMP isConnectedPtr; + SEL isConnectedSEL; } - (id)initWithMySQLPtr:(MYSQL *)mySQLPtr encoding:(NSStringEncoding)theEncoding timeZone:(NSTimeZone *)theTimeZone connection:(MCPConnection *)theConnection; diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.m b/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.m index 624f132c..fd7d181b 100644 --- a/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.m +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPStreamingResult.m @@ -95,6 +95,10 @@ mNumOfFields = 0; } + // Obtain SEL references and pointer + isConnectedSEL = @selector(isConnected); + isConnectedPtr = [parentConnection methodForSelector:isConnectedSEL]; + // If the result is opened in download-data-fast safe mode, set up the additional variables // and threads required. if (!fullyStreaming) { @@ -127,6 +131,7 @@ */ - (void) dealloc { + [self cancelResultLoad]; if (!connectionUnlocked) [parentConnection unlockConnection]; if (!fullyStreaming) { @@ -406,7 +411,7 @@ size_t sizeOfDataLengths = (size_t)(sizeof(unsigned long) * mNumOfFields); // Loop through the rows until the end of the data is reached - indicated via a NULL - while (theRow = mysql_fetch_row(mResult)) { + while ( (BOOL)(*isConnectedPtr)(parentConnection, isConnectedSEL) && (theRow = mysql_fetch_row(mResult))) { // Retrieve the lengths of the returned data fieldLengths = mysql_fetch_lengths(mResult); -- cgit v1.2.3