Bug fix for issue #307 - Memory corruption

This commit is contained in:
Robbie Hanson 2016-04-05 15:12:54 -07:00
parent 8688746cc6
commit 6f1a844d6c
3 changed files with 73 additions and 17 deletions

View File

@ -124,8 +124,8 @@ static NSString *const ext_key_class = @"class";
/**
* YapDatabaseConnection uses these methods to recycle sqlite3 instances using the connection pool.
**/
- (BOOL)connectionPoolEnqueue:(sqlite3 *)aDb;
- (sqlite3 *)connectionPoolDequeue;
- (BOOL)connectionPoolEnqueue:(sqlite3 *)aDb main_file:(yap_file *)main_file wal_file:(yap_file *)wal_file;
- (BOOL)connectionPoolDequeue:(sqlite3 **)aDb main_file:(yap_file **)main_file wal_file:(yap_file **)wal_file;
/**
* These methods are only accessible from within the snapshotQueue.

View File

@ -29,12 +29,20 @@
#endif
#pragma unused(ydbLogLevel)
/**
* YapDatabaseClosedNotification & corresponding keys.
**/
NSString *const YapDatabaseClosedNotification = @"YapDatabaseClosedNotification";
NSString *const YapDatabasePathKey = @"databasePath";
NSString *const YapDatabasePathWalKey = @"databasePath_wal";
NSString *const YapDatabasePathShmKey = @"databasePath_shm";
/**
* YapDatabaseModifiedNotification & corresponding keys.
**/
NSString *const YapDatabaseModifiedNotification = @"YapDatabaseModifiedNotification";
NSString *const YapDatabaseModifiedExternallyNotification = @"YapDatabaseModifiedExternallyNotification";
@ -57,6 +65,14 @@ NSString *const YapDatabaseExtensionsOrderKey = @"extensionsOrder";
NSString *const YapDatabaseExtensionDependenciesKey = @"extensionDependencies";
NSString *const YapDatabaseNotificationKey = @"notification";
/**
* ConnectionPool value dictionary keys.
**/
static NSString *const YDBConnectionPoolValueKey_db = @"db";
static NSString *const YDBConnectionPoolValueKey_main_file = @"main_file";
static NSString *const YDBConnectionPoolValueKey_wal_file = @"wal_file";
/**
* The database version is stored (via pragma user_version) to sqlite.
* It is used to represent the version of the userlying architecture of YapDatabase.
@ -522,6 +538,13 @@ static int connectionBusyHandler(void *ptr, int count) {
return nil;
}
// Configure VFS shim (for database connections).
yap_vfs_shim_name = [NSString stringWithFormat:@"yap_vfs_shim_%@", [[NSUUID UUID] UUIDString]];
yap_vfs_shim_register([yap_vfs_shim_name UTF8String], NULL, &yap_vfs_shim);
// Initialize variables
internalQueue = dispatch_queue_create("YapDatabase-Internal", NULL);
checkpointQueue = dispatch_queue_create("YapDatabase-Checkpoint", NULL);
snapshotQueue = dispatch_queue_create("YapDatabase-Snapshot", NULL);
@ -572,6 +595,7 @@ static int connectionBusyHandler(void *ptr, int count) {
dispatch_queue_set_specific(writeQueue, IsOnWriteQueueKey, IsOnWriteQueueKey, NULL);
// Complete database setup in the background
dispatch_async(snapshotQueue, ^{ @autoreleasepool {
[self upgradeTable];
@ -779,11 +803,6 @@ static int connectionBusyHandler(void *ptr, int count) {
sqlite3_wal_autocheckpoint(db, 0);
// Configure VFS shim (for database connections).
yap_vfs_shim_name = [NSString stringWithFormat:@"yap_vfs_shim_%@", [[NSUUID UUID] UUIDString]];
yap_vfs_shim_register([yap_vfs_shim_name UTF8String], NULL, &yap_vfs_shim);
return YES;
}
@ -2467,7 +2486,7 @@ static int connectionBusyHandler(void *ptr, int count) {
* Returns NO if the instance was not added to the pool.
* If so, the YapDatabaseConnection must close the instance.
**/
- (BOOL)connectionPoolEnqueue:(sqlite3 *)aDb
- (BOOL)connectionPoolEnqueue:(sqlite3 *)aDb main_file:(yap_file *)main_file wal_file:(yap_file *)wal_file
{
__block BOOL result = NO;
@ -2483,7 +2502,13 @@ static int connectionBusyHandler(void *ptr, int count) {
YDBLogVerbose(@"Enqueuing connection to pool: %p", aDb);
[connectionPoolValues addObject:[NSValue valueWithPointer:(const void *)aDb]];
NSDictionary *value = @{
YDBConnectionPoolValueKey_db : [NSValue valueWithPointer:(const void *)aDb],
YDBConnectionPoolValueKey_main_file : [NSValue valueWithPointer:(const void *)main_file],
YDBConnectionPoolValueKey_wal_file : [NSValue valueWithPointer:(const void *)wal_file],
};
[connectionPoolValues addObject:value];
[connectionPoolDates addObject:[NSDate date]];
result = YES;
@ -2502,15 +2527,25 @@ static int connectionBusyHandler(void *ptr, int count) {
* Retrieves a connection from the connection pool if available.
* Returns NULL if no connections are available.
**/
- (sqlite3 *)connectionPoolDequeue
- (BOOL)connectionPoolDequeue:(sqlite3 **)pDb main_file:(yap_file **)pMainFile wal_file:(yap_file **)pWalFile
{
NSParameterAssert(pDb != NULL);
NSParameterAssert(pMainFile != NULL);
NSParameterAssert(pWalFile != NULL);
__block sqlite3 *aDb = NULL;
__block yap_file *main_file = NULL;
__block yap_file *wal_file = NULL;
dispatch_sync(internalQueue, ^{
if ([connectionPoolValues count] > 0)
{
aDb = (sqlite3 *)[[connectionPoolValues objectAtIndex:0] pointerValue];
NSDictionary *value = [connectionPoolValues objectAtIndex:0];
aDb = (sqlite3 *)[[value objectForKey:YDBConnectionPoolValueKey_db] pointerValue];
main_file = (yap_file *)[[value objectForKey:YDBConnectionPoolValueKey_main_file] pointerValue];
wal_file = (yap_file *)[[value objectForKey:YDBConnectionPoolValueKey_wal_file] pointerValue];
YDBLogVerbose(@"Dequeuing connection from pool: %p", aDb);
@ -2521,7 +2556,11 @@ static int connectionBusyHandler(void *ptr, int count) {
}
});
return aDb;
*pDb = aDb;
*pMainFile = main_file;
*pWalFile = wal_file;
return (aDb != NULL);
}
/**
@ -2595,7 +2634,9 @@ static int connectionBusyHandler(void *ptr, int count) {
if ((interval >= connectionPoolLifetime) || (interval < 0))
{
sqlite3 *aDb = (sqlite3 *)[[connectionPoolValues objectAtIndex:0] pointerValue];
NSDictionary *value = [connectionPoolValues objectAtIndex:0];
sqlite3 *aDb = (sqlite3 *)[[value objectForKey:YDBConnectionPoolValueKey_db] pointerValue];;
YDBLogVerbose(@"Closing connection from pool: %p", aDb);

View File

@ -255,8 +255,21 @@ static int connectionBusyHandler(void *ptr, int count)
lock = OS_SPINLOCK_INIT;
db = [database connectionPoolDequeue];
if (db == NULL)
BOOL recycled = [database connectionPoolDequeue:&db main_file:&main_file wal_file:&wal_file];
if (recycled)
{
// Update pointer values
if (main_file) {
main_file->yap_database_connection = (__bridge void *)self;
}
if (wal_file) {
wal_file->yap_database_connection = (__bridge void *)self;
}
sqlite3_busy_handler(db, connectionBusyHandler, (__bridge void *)self);
}
else
{
// Open the database connection.
//
@ -343,7 +356,7 @@ static int connectionBusyHandler(void *ptr, int count)
//
// Note: In all my testing, I've only seen the busy_handler called once per db.
sqlite3_busy_handler(db, connectionBusyHandler, (__bridge void *)(self));
sqlite3_busy_handler(db, connectionBusyHandler, (__bridge void *)self);
#ifdef SQLITE_HAS_CODEC
// Configure SQLCipher encryption (if needed)
@ -417,7 +430,7 @@ static int connectionBusyHandler(void *ptr, int count)
wal_file->xNotifyDidRead = NULL;
}
if (![database connectionPoolEnqueue:db])
if (![database connectionPoolEnqueue:db main_file:main_file wal_file:wal_file])
{
int status = sqlite3_close(db);
if (status != SQLITE_OK)
@ -427,6 +440,8 @@ static int connectionBusyHandler(void *ptr, int count)
}
db = NULL;
main_file = NULL;
wal_file = NULL;
}
[database removeConnection:self];