aboutsummaryrefslogtreecommitdiffstats
path: root/Frameworks/SPMySQLFramework
diff options
context:
space:
mode:
Diffstat (limited to 'Frameworks/SPMySQLFramework')
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h3
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m6
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m6
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m3
-rw-r--r--Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m61
5 files changed, 78 insertions, 1 deletions
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
index 5772eb72..ee7f6039 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQL Private APIs.h
@@ -39,7 +39,6 @@
#import "Locking.h"
#import "Conversion.h"
-
@interface SPMySQLConnection (PrivateAPI)
- (MYSQL *)_makeRawMySQLConnectionWithEncoding:(NSString *)encodingName isMasterConnection:(BOOL)isMaster;
@@ -47,6 +46,8 @@
- (void)_updateConnectionVariables;
- (void)_restoreConnectionVariables;
- (BOOL)_checkConnectionIfNecessary;
+- (void)_validateThreadSetup;
++ (void)_removeThreadVariables:(NSNotification *)aNotification;
@end
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m
index 3ce0c0cd..3201b55f 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Ping & KeepAlive.m
@@ -187,6 +187,9 @@ void _backgroundPingTask(void *ptr)
// Set up a cleanup routine
pthread_cleanup_push(_pingThreadCleanup, pingDetails);
+ // Initialise MySQL variables and handling on this thread
+ mysql_thread_init();
+
// Set up a signal handler for SIGUSR1, to handle forced timeouts.
signal(SIGUSR1, _forceThreadExit);
@@ -209,6 +212,9 @@ void _pingThreadCleanup(void *pingDetails)
{
SPMySQLConnectionPingDetails *pingDetailsStruct = pingDetails;
*(pingDetailsStruct->keepAlivePingActivePointer) = NO;
+
+ // Clean up MySQL variables and handlers
+ mysql_thread_end();
}
@end
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m
index 5df71e96..46615cae 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Querying & Preparation.m
@@ -76,6 +76,9 @@
}
if (![self _checkConnectionIfNecessary]) return nil;
+ // Ensure per-thread variables are set up
+ [self _validateThreadSetup];
+
// Perform a lossy conversion to bytes, using NSData to do the hard work. Preserves
// nul characters correctly.
NSData *cData = [theString dataUsingEncoding:stringEncoding allowLossyConversion:YES];
@@ -229,6 +232,9 @@
return nil;
}
+ // Ensure per-thread variables are set up
+ [self _validateThreadSetup];
+
// Check the connection if necessary, returning nil if the query couldn't be validated
if (![self _checkConnectionIfNecessary]) return nil;
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m
index f695d977..1022ccd1 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection Categories/Server Info.m
@@ -137,6 +137,9 @@
// Lock the connection before using it
[self _lockConnection];
+ // Ensure per-thread variables are set up
+ [self _validateThreadSetup];
+
// Get the process list
MYSQL_RES *mysqlResult = mysql_list_processes(mySQLConnection);
diff --git a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
index 800157ca..d42b82e6 100644
--- a/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
+++ b/Frameworks/SPMySQLFramework/Source/SPMySQLConnection.m
@@ -36,6 +36,9 @@
#include <pthread.h>
#include <SystemConfiguration/SCNetworkReachability.h>
+// Thread flag constant
+static pthread_key_t mySQLThreadInitFlagKey;
+static void *mySQLThreadFlag;
#pragma mark Class constants
@@ -76,6 +79,24 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS
#pragma mark Initialisation and teardown
/**
+ * In the one-off class initialisation, set up MySQL as necessary
+ */
++ (void)initialize
+{
+
+ // Set up a pthread thread-specific data key to be used across all classes and threads
+ pthread_key_create(&mySQLThreadInitFlagKey, NULL);
+ mySQLThreadFlag = malloc(1);
+
+ // MySQL requires mysql_library_init() to be called before any other MySQL
+ // functions are used; although mysql_init() will call it automatically, it
+ // won't do so in a thread-safe manner, so setting it up first is safer.
+ // No arguments are required.
+ // Note that this will install MySQL's SIGPIPE handler.
+ mysql_library_init(0, NULL, NULL);
+}
+
+/**
* Initialise the SPMySQLConnection object, setting up class defaults.
*
* Typically initialisation would be followed by setting the connection details
@@ -631,6 +652,13 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS
MYSQL *theConnection = mysql_init(NULL);
if (!theConnection) return NULL;
+ // Calling mysql_init will have automatically installed per-thread variables if necessary,
+ // so track their installation for removal and to avoid recreating again.
+ if (!pthread_getspecific(mySQLThreadInitFlagKey)) {
+ pthread_setspecific(mySQLThreadInitFlagKey, &mySQLThreadFlag);
+ [(NSNotificationCenter *)[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(_removeThreadVariables:) name:NSThreadWillExitNotification object:[NSThread currentThread]];
+ }
+
// Disable automatic reconnection, as it's handled in-framework to preserve
// options, encodings and connection state.
my_bool falseMyBool = FALSE;
@@ -835,4 +863,37 @@ const char *SPMySQLSSLPermissibleCiphers = "DHE-RSA-AES256-SHA:AES256-SHA:DHE-RS
// Otherwise check the connection
return [self checkConnection];
}
+
+/**
+ * Ensure that the thread this method is called on has been registered for
+ * use with MySQL. MySQL requires thread-specific variables for safe
+ * execution.
+ */
+- (void)_validateThreadSetup
+{
+
+ // Check to see whether the handler has already been installed
+ if (pthread_getspecific(mySQLThreadInitFlagKey)) return;
+
+ // If not, install it
+ mysql_thread_init();
+
+ // Mark the thread to avoid multiple installs
+ pthread_setspecific(mySQLThreadInitFlagKey, &mySQLThreadFlag);
+
+ // Set up the notification handler to deregister it
+ [(NSNotificationCenter *)[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(_removeThreadVariables:) name:NSThreadWillExitNotification object:[NSThread currentThread]];
+}
+
+/**
+ * Remove the MySQL variables and handlers from each closing thread which
+ * has had them installed to avoid memory leaks.
+ * This is a class method for easy global tracking; it will be called on the appropriate
+ * thread automatically.
+ */
++ (void)_removeThreadVariables:(NSNotification *)aNotification
+{
+ mysql_thread_end();
+}
+
@end