From 79eff5bf42154da8d7730e0e0159160f68ec4e16 Mon Sep 17 00:00:00 2001 From: rowanbeentje Date: Wed, 14 Mar 2012 01:16:18 +0000 Subject: =?UTF-8?q?Final=20feature=20work=20on=20the=20SPMySQL=20branch=20?= =?UTF-8?q?before=20merging:=20=20-=20Add=20a=20ping=20keepalive=20managin?= =?UTF-8?q?g=20object=20to=20prevent=20retain=20cycles=20from=20the=20NSTi?= =?UTF-8?q?mer=20=20-=20Add=20-[SPMySQLConnection=20copy]=20support=20=20-?= =?UTF-8?q?=20Refactor=20Hans-J=C3=B6rg=20Bibiko's=20database=20structure?= =?UTF-8?q?=20retrieval,=20moving=20it=20out=20of=20the=20MySQL=20framewor?= =?UTF-8?q?k=20and=20building=20it=20around=20a=20copy=20of=20the=20connec?= =?UTF-8?q?tion.=20=20This=20reduces=20the=20amount=20of=20connections-ove?= =?UTF-8?q?r-time=20used=20by=20Sequel=20Pro=20to=20two=20constant=20conne?= =?UTF-8?q?ctions=20(addressing=20Issue=20#1097)=20and=20improves=20robust?= =?UTF-8?q?ness.=20=20-=20Use=20the=20database=20structure=20retrieval=20c?= =?UTF-8?q?onnection=20for=20faster=20query=20cancellation=20without=20an?= =?UTF-8?q?=20extra=20connection=20required,=20if=20possible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SPMySQLFramework.xcodeproj/project.pbxproj | 16 +++ .../Source/SPMySQLConnection Categories/Copying.h | 36 ++++++ .../Source/SPMySQLConnection Categories/Copying.m | 71 ++++++++++++ .../Ping & KeepAlive.h | 5 +- .../Ping & KeepAlive.m | 15 +-- .../Querying & Preparation.h | 1 - .../Querying & Preparation.m | 10 +- .../SPMySQLFramework/Source/SPMySQLConnection.h | 6 +- .../SPMySQLFramework/Source/SPMySQLConnection.m | 16 +-- .../Source/SPMySQLKeepAliveTimer.h | 45 ++++++++ .../Source/SPMySQLKeepAliveTimer.m | 127 +++++++++++++++++++++ 11 files changed, 311 insertions(+), 37 deletions(-) create mode 100644 Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.h create mode 100644 Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.m create mode 100644 Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.h create mode 100644 Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.m (limited to 'Frameworks/SPMySQLFramework') diff --git a/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj b/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj index 933a68b7..2f8d300c 100644 --- a/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj +++ b/Frameworks/SPMySQLFramework/SPMySQLFramework.xcodeproj/project.pbxproj @@ -28,6 +28,10 @@ 584294FB14CB8002000F8438 /* Encoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 584294F914CB8002000F8438 /* Encoding.m */; }; 584294FE14CB8002000F8438 /* Server Info.h in Headers */ = {isa = PBXBuildFile; fileRef = 584294FC14CB8002000F8438 /* Server Info.h */; settings = {ATTRIBUTES = (Public, ); }; }; 584294FF14CB8002000F8438 /* Server Info.m in Sources */ = {isa = PBXBuildFile; fileRef = 584294FD14CB8002000F8438 /* Server Info.m */; }; + 584D812E15057ECD00F24774 /* SPMySQLKeepAliveTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 584D812C15057ECD00F24774 /* SPMySQLKeepAliveTimer.h */; }; + 584D812F15057ECD00F24774 /* SPMySQLKeepAliveTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 584D812D15057ECD00F24774 /* SPMySQLKeepAliveTimer.m */; }; + 584D82551509775000F24774 /* Copying.h in Headers */ = {isa = PBXBuildFile; fileRef = 584D82531509775000F24774 /* Copying.h */; }; + 584D82561509775000F24774 /* Copying.m in Sources */ = {isa = PBXBuildFile; fileRef = 584D82541509775000F24774 /* Copying.m */; }; 586A99FB14F02E21007F82BF /* SPMySQLStreamingResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 586A99F914F02E21007F82BF /* SPMySQLStreamingResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; 586A99FC14F02E21007F82BF /* SPMySQLStreamingResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 586A99FA14F02E21007F82BF /* SPMySQLStreamingResult.m */; }; 586AA16714F30C5F007F82BF /* Convenience Methods.h in Headers */ = {isa = PBXBuildFile; fileRef = 586AA16514F30C5F007F82BF /* Convenience Methods.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -92,6 +96,10 @@ 584294F914CB8002000F8438 /* Encoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Encoding.m; path = "Source/SPMySQLConnection Categories/Encoding.m"; sourceTree = ""; }; 584294FC14CB8002000F8438 /* Server Info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "Server Info.h"; path = "Source/SPMySQLConnection Categories/Server Info.h"; sourceTree = ""; }; 584294FD14CB8002000F8438 /* Server Info.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "Server Info.m"; path = "Source/SPMySQLConnection Categories/Server Info.m"; sourceTree = ""; }; + 584D812C15057ECD00F24774 /* SPMySQLKeepAliveTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SPMySQLKeepAliveTimer.h; path = Source/SPMySQLKeepAliveTimer.h; sourceTree = ""; }; + 584D812D15057ECD00F24774 /* SPMySQLKeepAliveTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SPMySQLKeepAliveTimer.m; path = Source/SPMySQLKeepAliveTimer.m; sourceTree = ""; }; + 584D82531509775000F24774 /* Copying.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Copying.h; path = "Source/SPMySQLConnection Categories/Copying.h"; sourceTree = ""; }; + 584D82541509775000F24774 /* Copying.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Copying.m; path = "Source/SPMySQLConnection Categories/Copying.m"; sourceTree = ""; }; 586A99F914F02E21007F82BF /* SPMySQLStreamingResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SPMySQLStreamingResult.h; path = Source/SPMySQLStreamingResult.h; sourceTree = ""; }; 586A99FA14F02E21007F82BF /* SPMySQLStreamingResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SPMySQLStreamingResult.m; path = Source/SPMySQLStreamingResult.m; sourceTree = ""; }; 586AA0E714F1CEC8007F82BF /* Readme.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Readme.txt; sourceTree = ""; }; @@ -202,6 +210,8 @@ 58C7C1E314DB6E4C00436315 /* SPMySQLFastStreamingResult.m */, 58C7C1E114DB6E3000436315 /* Result Categories */, 580A331B14D75CCF000D6933 /* Result types */, + 584D812C15057ECD00F24774 /* SPMySQLKeepAliveTimer.h */, + 584D812D15057ECD00F24774 /* SPMySQLKeepAliveTimer.m */, ); name = Classes; sourceTree = ""; @@ -288,6 +298,8 @@ 584294EB14CB8002000F8438 /* Connection Categories */ = { isa = PBXGroup; children = ( + 584D82531509775000F24774 /* Copying.h */, + 584D82541509775000F24774 /* Copying.m */, 58C00AB314E4892E00AC489A /* Delegate & Proxy.h */, 58C00AB414E4892E00AC489A /* Delegate & Proxy.m */, 58C00BCF14E7459600AC489A /* Databases & Tables.h */, @@ -387,6 +399,8 @@ 586A99FB14F02E21007F82BF /* SPMySQLStreamingResult.h in Headers */, 586AA16714F30C5F007F82BF /* Convenience Methods.h in Headers */, 586AA81414F6C648007F82BF /* SPMySQLArrayAdditions.h in Headers */, + 584D812E15057ECD00F24774 /* SPMySQLKeepAliveTimer.h in Headers */, + 584D82551509775000F24774 /* Copying.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -470,6 +484,8 @@ 58C00BD214E7459600AC489A /* Databases & Tables.m in Sources */, 586A99FC14F02E21007F82BF /* SPMySQLStreamingResult.m in Sources */, 586AA16814F30C5F007F82BF /* Convenience Methods.m in Sources */, + 584D812F15057ECD00F24774 /* SPMySQLKeepAliveTimer.m in Sources */, + 584D82561509775000F24774 /* Copying.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.h new file mode 100644 index 00000000..83d055d5 --- /dev/null +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.h @@ -0,0 +1,36 @@ +// +// $Id$ +// +// Copying.h +// SPMySQLFramework +// +// Created by Rowan Beentje (rowan.beent.je) on March 8, 2012 +// Copyright (c) 2012 Rowan Beentje. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// More info at + + +@interface SPMySQLConnection (Copying) + +@end diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.m new file mode 100644 index 00000000..c2df2a4b --- /dev/null +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Copying.m @@ -0,0 +1,71 @@ +// +// $Id$ +// +// Copying.m +// SPMySQLFramework +// +// Created by Rowan Beentje (rowan.beent.je) on March 8, 2012 +// Copyright (c) 2012 Rowan Beentje. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// More info at + +#import "Copying.h" + +@implementation SPMySQLConnection (Copying) + +/** + * Provide a copy of the SPMySQLConnection instance. + * The copy should inherit the full setup, but will not inherit + * the connection state - it will not be connected, and any connection + * details such as the selected database/encoding will not be inherited. + * Note that any proxy will not be referenced in the new connection, and + * should also be set if desired. + */ +- (id)copyWithZone:(NSZone *)zone +{ + SPMySQLConnection *copy = [[[self class] allocWithZone:zone] init]; + + // Synthesized details + [copy setDelegate:delegate]; + [copy setHost:host]; + [copy setUsername:username]; + [copy setPassword:password]; + [copy setPort:port]; + [copy setUseSocket:useSocket]; + [copy setSocketPath:socketPath]; + [copy setUseSSL:useSSL]; + [copy setSslKeyFilePath:sslKeyFilePath]; + [copy setSslCertificatePath:sslCertificatePath]; + [copy setSslCACertificatePath:sslCACertificatePath]; + [copy setTimeout:timeout]; + [copy setUseKeepAlive:useKeepAlive]; + [copy setRetryQueriesOnConnectionFailure:retryQueriesOnConnectionFailure]; + [copy setDelegateQueryLogging:delegateQueryLogging]; + + // Active connection state details, like selected database and encoding, are *not* copied. + + return copy; +} + +@end diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h index 3788c653..e84c4ca6 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.h @@ -40,11 +40,8 @@ typedef struct { @interface SPMySQLConnection (Ping_and_KeepAlive) -// Setup functions -- (void)_initKeepAlivePingTimer; - // Keepalive ping initialisation -- (void)_keepAlive:(NSTimer *)theTimer; +- (void)_keepAlive; - (void)_threadedKeepAlive; // Master ping method diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m index 9e25edcb..3ce0c0cd 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m @@ -37,19 +37,6 @@ @implementation SPMySQLConnection (Ping_and_KeepAlive) -#pragma mark - -#pragma mark Setup functions - -/** - * Set up the keepalive timer; this should be called on the main - * thread, to ensure the timer isn't descheduled when child threads - * terminate. - */ -- (void)_initKeepAlivePingTimer -{ - keepAliveTimer = [[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(_keepAlive:) userInfo:nil repeats:YES] retain]; -} - #pragma mark - #pragma mark Keepalive ping initialisation @@ -58,7 +45,7 @@ * This method is called every ten seconds and spawns a thread which determines * whether or not it should perform a ping. */ -- (void)_keepAlive:(NSTimer *)theTimer +- (void)_keepAlive { // Do nothing if not connected or if keepalive is disabled diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.h index 9ab8bc6a..0f086a89 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.h +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.h @@ -61,7 +61,6 @@ // Query cancellation - (void)cancelCurrentQuery; -- (BOOL)lastQueryWasCancelled; - (BOOL)lastQueryWasCancelledUsingReconnect; @end diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m index cb5ce70d..9b54029c 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m @@ -282,7 +282,7 @@ } else { // Prevent retries if the query was cancelled or not a connection error - if (lastQueryWasCancelled && ![SPMySQLConnection isErrorIDConnectionError:mysql_errno(mySQLConnection)]) { + if (lastQueryWasCancelled || ![SPMySQLConnection isErrorIDConnectionError:mysql_errno(mySQLConnection)]) { break; } } @@ -548,14 +548,6 @@ lastQueryWasCancelledUsingReconnect = YES; } -/** - * Returns whether the last query was cancelled using cancelCurrentQuery. - */ -- (BOOL)lastQueryWasCancelled -{ - return lastQueryWasCancelled; -} - /** * If the last query was cancelled, returns whether that query cancellation * required the connection to be reset or whether the query was successfully diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h index 5d1a5d11..8f3b7f9f 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.h @@ -30,6 +30,8 @@ // // More info at +@class SPMySQLKeepAliveTimer; + @interface SPMySQLConnection : NSObject { // Delegate @@ -80,7 +82,7 @@ // Timeout and keep-alive NSUInteger timeout; BOOL useKeepAlive; - NSTimer *keepAliveTimer; + SPMySQLKeepAliveTimer *keepAliveTimer; CGFloat keepAliveInterval; uint64_t lastKeepAliveTime; NSUInteger keepAlivePingFailures; @@ -153,6 +155,8 @@ @property (readwrite, assign) BOOL delegateQueryLogging; +@property (readwrite, assign) BOOL lastQueryWasCancelled; + #pragma mark - #pragma mark Connection and disconnection diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m index 4968266d..4f1e8a74 100644 --- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m @@ -31,6 +31,7 @@ // More info at #import "SPMySQL Private APIs.h" +#import "SPMySQLKeepAliveTimer.h" #include #include #include @@ -71,6 +72,7 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS @synthesize mysqlConnectionThreadId; @synthesize retryQueriesOnConnectionFailure; @synthesize delegateQueryLogging; +@synthesize lastQueryWasCancelled; #pragma mark - #pragma mark Initialisation and teardown @@ -161,11 +163,7 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS retryQueriesOnConnectionFailure = YES; // Start the ping keepalive timer - if ([NSThread isMainThread]) { - [self _initKeepAlivePingTimer]; - } else { - [self performSelectorOnMainThread:@selector(_initKeepAlivePingTimer) withObject:nil waitUntilDone:YES]; - } + keepAliveTimer = [[SPMySQLKeepAliveTimer alloc] initWithInterval:10 target:self selector:@selector(_keepAlive)]; } return self; @@ -181,6 +179,10 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS // Unset the delegate [self setDelegate:nil]; + // Clear the keepalive timer + [keepAliveTimer invalidate]; + [keepAliveTimer release]; + // Disconnect if appropriate (which should also disconnect any proxy) [self disconnect]; @@ -196,14 +198,12 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS } [connectionLock release], connectionLock = nil; - [encoding dealloc]; + [encoding release]; if (previousEncoding) [previousEncoding release], previousEncoding = nil; if (database) [database release], database = nil; if (serverVersionString) [serverVersionString release], serverVersionString = nil; if (queryErrorMessage) [queryErrorMessage release], queryErrorMessage = nil; - [keepAliveTimer invalidate]; - [keepAliveTimer release]; [delegateDecisionLock release]; [NSObject cancelPreviousPerformRequestsWithTarget:self]; diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.h b/Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.h new file mode 100644 index 00000000..ebe46bed --- /dev/null +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.h @@ -0,0 +1,45 @@ +// +// $Id$ +// +// SPMySQLKeepAliveTimer.h +// SPMySQLFramework +// +// Created by Rowan Beentje (rowan.beent.je) on March 5, 2012 +// Copyright (c) 2012 Rowan Beentje. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// More info at + + +@interface SPMySQLKeepAliveTimer : NSObject { + id timerTarget; + SEL timerSelector; + NSTimeInterval timerRepeatInterval; + + NSTimer *wrappedTimer; +} + +- (id)initWithInterval:(NSTimeInterval)anInterval target:(id)aTarget selector:(SEL)aSelector; +- (void)invalidate; + +@end diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.m b/Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.m new file mode 100644 index 00000000..f9164aff --- /dev/null +++ b/Frameworks/SPMySQLFramework/Source/SPMySQLKeepAliveTimer.m @@ -0,0 +1,127 @@ +// +// $Id$ +// +// SPMySQLKeepAliveTimer.m +// SPMySQLFramework +// +// Created by Rowan Beentje (rowan.beent.je) on March 5, 2012 +// Copyright (c) 2012 Rowan Beentje. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// More info at + + +#import "SPMySQLKeepAliveTimer.h" +#import "SPMySQL Private APIs.h" + +@interface SPMySQLKeepAliveTimer (Private_API) + +- (void)_initKeepAliveTimer; +- (void)_forwardPing; + +@end + +#pragma mark - + +@implementation SPMySQLKeepAliveTimer + +/** + * Prevent SPMySQLKeepAliveTimer from being init'd normally. + */ +- (id)init +{ + [NSException raise:NSInternalInconsistencyException format:@"SPMySQLKeepAliveTimers should not be init'd directly; use initWithInterval:target:selector: instead."]; + return nil; +} + +/** + * Initialise the SPMySQLKeepAliveTimer. This also sets up the contained timer, + * which has to be wrapped in this class to prevent retain cycles preventing the + * parent connection from being released. + * + * After initialisation, the delegate should be set to ensure that the timer events + * are received. + */ +- (id)initWithInterval:(NSTimeInterval)anInterval target:(id)aTarget selector:(SEL)aSelector +{ + if ((self = [super init])) { + wrappedTimer = nil; + + // Keep a weak reference to the target + timerTarget = aTarget; + timerSelector = aSelector; + timerRepeatInterval = anInterval; + + // Ensure the timer is set up on the main thread + if ([NSThread isMainThread]) { + [self _initKeepAliveTimer]; + } else { + [self performSelectorOnMainThread:@selector(_initKeepAliveTimer) withObject:nil waitUntilDone:YES]; + } + } + + return self; +} + +/** + * Invalidate the wrapped timer, which also releases the reference to the timer + * target (this object), breaking retain loops. + */ +- (void)invalidate +{ + if ([NSThread isMainThread]) { + [wrappedTimer invalidate]; + } else { + [wrappedTimer performSelectorOnMainThread:@selector(invalidate) withObject:nil waitUntilDone:YES]; + } +} + +- (void)dealloc +{ + [wrappedTimer dealloc]; + [super dealloc]; +} + +@end + +@implementation SPMySQLKeepAliveTimer (Private_API) + +/** + * Set up the timer to tickle the target. This must be set up on the main thread + * to ensure the timer events keep firing. + */ +- (void)_initKeepAliveTimer +{ + wrappedTimer = [[NSTimer scheduledTimerWithTimeInterval:timerRepeatInterval target:self selector:@selector(_forwardPing) userInfo:nil repeats:YES] retain]; +} + +/** + * Forward the NSTimer-fired ping to the target object. Performing this forwarding + * breaks the retain cycle. + */ +- (void)_forwardPing +{ + [timerTarget performSelector:timerSelector]; +} + +@end -- cgit v1.2.3