Skip to content

Commit

Permalink
Added a new class that works as a queue for queries and updates, inst…
Browse files Browse the repository at this point in the history
…ead of a pool- which you can still get deadlocked on if you aren't careful.
  • Loading branch information
ccgus committed Nov 29, 2011
1 parent 1c8a0ef commit c0160b3
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 2 deletions.
34 changes: 34 additions & 0 deletions src/FMDatabaseQueue.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// FMDatabasePool.h
// fmdb
//
// Created by August Mueller on 6/22/11.
// Copyright 2011 Flying Meat Inc. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "sqlite3.h"

@class FMDatabase;

@interface FMDatabaseQueue : NSObject {
dispatch_queue_t _queue;
FMDatabase *_db;
}

+ (id)databaseQueueWithPath:(NSString*)aPath;
- (id)initWithPath:(NSString*)aPath;

- (void)inDatabase:(void (^)(FMDatabase *db))block;

- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block;
- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block;

#if SQLITE_VERSION_NUMBER >= 3007000
// NOTE: you can not nest these, since calling it will pull another database out of the pool and you'll get a deadlock.
// If you need to nest, use FMDatabase's startSavePointWithName:error: instead.
- (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block;
#endif

@end

118 changes: 118 additions & 0 deletions src/FMDatabaseQueue.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//
// FMDatabasePool.m
// fmdb
//
// Created by August Mueller on 6/22/11.
// Copyright 2011 Flying Meat Inc. All rights reserved.
//

#import "FMDatabaseQueue.h"
#import "FMDatabase.h"

@implementation FMDatabaseQueue

+ (id)databaseQueueWithPath:(NSString*)aPath {
return [[[self alloc] initWithPath:aPath] autorelease];
}

- (id)initWithPath:(NSString*)aPath {

self = [super init];

if (self != nil) {

_db = [[FMDatabase databaseWithPath:aPath] retain];

if (![_db open]) {
NSLog(@"Could not create database queue for path %@", aPath);
[self release];
return 0x00;
}

_queue = dispatch_queue_create([[NSString stringWithFormat:@"fmdb.%@", self] UTF8String], NULL);
}

return self;
}

- (void)dealloc {

[_db release];

if (_queue) {
dispatch_release(_queue);
_queue = 0x00;
}

[super dealloc];
}

- (void)inDatabase:(void (^)(FMDatabase *db))block {

dispatch_sync(_queue, ^() { block(_db); });
}

- (void)beginTransaction:(BOOL)useDeferred withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block {

dispatch_sync(_queue, ^() {

BOOL shouldRollback = NO;

if (useDeferred) {
[_db beginDeferredTransaction];
}
else {
[_db beginTransaction];
}

block(_db, &shouldRollback);

if (shouldRollback) {
[_db rollback];
}
else {
[_db commit];
}

});
}

- (void)inDeferredTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
[self beginTransaction:YES withBlock:block];
}

- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
[self beginTransaction:NO withBlock:block];
}

#if SQLITE_VERSION_NUMBER >= 3007000
- (NSError*)inSavePoint:(void (^)(FMDatabase *db, BOOL *rollback))block {

static unsigned long savePointIdx = 0;
__block NSError *err = 0x00;

dispatch_sync(_queue, ^() {

NSString *name = [NSString stringWithFormat:@"savePoint%ld", savePointIdx++];

BOOL shouldRollback = NO;

if ([_db startSavePointWithName:name error:&err]) {

block(_db, &shouldRollback);

if (shouldRollback) {
[_db rollbackToSavePointWithName:name error:&err];
}
else {
[_db releaseSavePointWithName:name error:&err];
}

}
});

return err;
}
#endif

@end
62 changes: 60 additions & 2 deletions src/fmdb.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#import "FMDatabase.h"
#import "FMDatabaseAdditions.h"
#import "FMDatabasePool.h"
#import "FMDatabaseQueue.h"

#define FMDBQuickCheck(SomeBool) { if (!(SomeBool)) { NSLog(@"Failure on line %d", __LINE__); abort(); } }

Expand Down Expand Up @@ -901,13 +902,70 @@ int main (int argc, const char * argv[]) {

}

NSLog(@"That was version %@ of sqlite", [FMDatabase sqliteLibVersion]);

[pool release];


FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];

FMDBQuickCheck(queue);

{

[queue inDatabase:^(FMDatabase *db) {


int count = 0;
FMResultSet *rsl = [db executeQuery:@"select * from likefoo where foo like 'h%'"];
while ([rsl next]) {
count++;
}

FMDBQuickCheck(count == 2);

count = 0;
rsl = [db executeQuery:@"select * from likefoo where foo like ?", @"h%"];
while ([rsl next]) {
count++;
}

FMDBQuickCheck(count == 2);
}];

}


{

int ops = 16;

dispatch_queue_t dqueue = dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_HIGH);

dispatch_apply(ops, dqueue, ^(size_t nby) {

// just mix things up a bit for demonstration purposes.
if (nby % 2 == 1) {
[NSThread sleepForTimeInterval:.1];
}

if (nby % 3 == 1) {
[NSThread sleepForTimeInterval:.1];
}

[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
NSLog(@"Starting %ld", nby);
[db executeUpdate:@"insert into likefoo values ('1')"];
[db executeUpdate:@"insert into likefoo values ('2')"];
[db executeUpdate:@"insert into likefoo values ('3')"];
NSLog(@"Ending %ld", nby);
}];
});

}


NSLog(@"That was version %@ of sqlite", [FMDatabase sqliteLibVersion]);

[pool release];

return 0;
}

0 comments on commit c0160b3

Please sign in to comment.