How to properly call SQLite functions from background thread on iPhone?
I've tried these two solutions and they worked perfectly. You can either use critical sections or NSOperationQueue and I prefer the first one, here is the code for both of them:
define some class "DatabaseController" and add this code to its implementation:
static NSString * DatabaseLock = nil;
+ (void)initialize {
[super initialize];
DatabaseLock = [[NSString alloc] initWithString:@"Database-Lock"];
}
+ (NSString *)databaseLock {
return DatabaseLock;
}
- (void)writeToDatabase1 {
@synchronized ([DatabaseController databaseLock]) {
// Code that writes to an sqlite3 database goes here...
}
}
- (void)writeToDatabase2 {
@synchronized ([DatabaseController databaseLock]) {
// Code that writes to an sqlite3 database goes here...
}
}
OR to use the NSOperationQueue you can use:
static NSOperationQueue * DatabaseQueue = nil;
+ (void)initialize {
[super initialize];
DatabaseQueue = [[NSOperationQueue alloc] init];
[DatabaseQueue setMaxConcurrentOperationCount:1];
}
+ (NSOperationQueue *)databaseQueue {
return DatabaseQueue;
}
- (void)writeToDatabase {
NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(FUNCTION_THAT_WRITES_TO_DATABASE) object:nil];
[operation setQueuePriority:NSOperationQueuePriorityHigh];
[[DatabaseController databaseQueue] addOperations:[NSArray arrayWithObject:operation] waitUntilFinished:YES];
[operation release];
}
these two solutions block the current thread until the writing to database is finished which you may consider in most of the cases.
That error message maps to SQLITE_MISUSE (the source code is available at http://www.sqlite.org).
See http://www.sqlite.org/faq.html#q6 for limitations on using an sqlite3 * database handle from more than one thread. Effectively, you are allowed to reuse a database handle and statements across threads but one thread must be completely done accessing the database before the other thread starts (i.e. overlapping access is not safe). That sounds like what's happening for you and is consistent with the SQLITE_MISUSE error code.
If you need to access the same database from more than one thread, I recommend instead opening the database separately from each thread and setting a timeout using sqlite3_busy_timeout(). Sqlite will then handle contention for you, blocking for a short time in one thread if the other thread is writing data while still allowing simultaneous reads.