Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ccgus/fmdb.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAugust Mueller <gus@mp.local>2014-03-01 01:05:44 +0400
committerAugust Mueller <gus@mp.local>2014-03-01 01:05:44 +0400
commit484b046a4a5ba7e8c8d3bb87dc838e71fa26db42 (patch)
treee9687c506326e0550d854b867b8e93117589f162
parent995dbb5540d3431decb10de6eff51a79c86527c8 (diff)
Discovered sqlite3_busy_handler, and got to kill some ivars and code. busyTimeout has been replaced by setMaxBusyRetryTimeInterval: - this is a _lot_ better than adding loops everywhere.
-rw-r--r--Tests/FMDatabasePoolTests.m2
-rw-r--r--Tests/FMDatabaseTests.m2
-rw-r--r--src/FMDatabase.h11
-rw-r--r--src/FMDatabase.m173
4 files changed, 89 insertions, 99 deletions
diff --git a/Tests/FMDatabasePoolTests.m b/Tests/FMDatabasePoolTests.m
index 2100b31..bb759cf 100644
--- a/Tests/FMDatabasePoolTests.m
+++ b/Tests/FMDatabasePoolTests.m
@@ -254,7 +254,7 @@
- (BOOL)databasePool:(FMDatabasePool*)pool shouldAddDatabaseToPool:(FMDatabase*)database {
- [database setRetryTimeout:.1];
+ [database setMaxBusyRetryTimeInterval:10];
// [database setCrashOnErrors:YES];
#pragma message "FIXME: Gus - we need to check for a SQLITE_BUSY when we call sqlite3_step. sqlite will sleep for the retry amount - BUT, it won't just try the step again. We'll have to put the old loops back in. testReadWriteStressTest shows this mistake. Maybe add a private method to FMDatabase that does the stepping and loop for us, and replace sqlite3_step with that?"
return YES;
diff --git a/Tests/FMDatabaseTests.m b/Tests/FMDatabaseTests.m
index 279f09f..66313d9 100644
--- a/Tests/FMDatabaseTests.m
+++ b/Tests/FMDatabaseTests.m
@@ -176,7 +176,7 @@
[self.db executeUpdate:@"create table t1 (a integer)"];
[self.db executeUpdate:@"insert into t1 values (?)", [NSNumber numberWithInt:5]];
- [self.db setRetryTimeout:2];
+ [self.db setMaxBusyRetryTimeInterval:2];
FMDatabase *newDB = [FMDatabase databaseWithPath:self.databasePath];
[newDB open];
diff --git a/src/FMDatabase.h b/src/FMDatabase.h
index 11e4cb5..8977c92 100644
--- a/src/FMDatabase.h
+++ b/src/FMDatabase.h
@@ -74,7 +74,8 @@
BOOL _shouldCacheStatements;
BOOL _isExecutingStatement;
BOOL _inTransaction;
- NSTimeInterval _busyTimeout;
+ NSTimeInterval _maxBusyRetryTimeInterval;
+ NSTimeInterval _startBusyRetryTime;
NSMutableDictionary *_cachedStatements;
NSMutableSet *_openResultSets;
@@ -95,10 +96,6 @@
@property (atomic, assign) BOOL checkedOut;
-/** Busy retry timeout */
-
-@property (atomic, assign) NSTimeInterval busyTimeout;
-
/** Crash on errors */
@property (atomic, assign) BOOL crashOnErrors;
@@ -692,8 +689,8 @@
// description forthcoming
-- (void)setRetryTimeout:(NSTimeInterval)timeout;
-- (NSTimeInterval)retryTimeout;
+- (void)setMaxBusyRetryTimeInterval:(NSTimeInterval)timeoutInSeconds;
+- (NSTimeInterval)maxBusyRetryTimeInterval;
#if SQLITE_VERSION_NUMBER >= 3007000
diff --git a/src/FMDatabase.m b/src/FMDatabase.m
index d3736a2..d6d2b84 100644
--- a/src/FMDatabase.m
+++ b/src/FMDatabase.m
@@ -12,7 +12,6 @@
@synthesize cachedStatements=_cachedStatements;
@synthesize logsErrors=_logsErrors;
@synthesize crashOnErrors=_crashOnErrors;
-@synthesize busyTimeout=_busyTimeout;
@synthesize checkedOut=_checkedOut;
@synthesize traceExecution=_traceExecution;
@@ -40,12 +39,12 @@
self = [super init];
if (self) {
- _databasePath = [aPath copy];
- _openResultSets = [[NSMutableSet alloc] init];
- _db = nil;
- _logsErrors = YES;
- _crashOnErrors = NO;
- _busyTimeout = 0;
+ _databasePath = [aPath copy];
+ _openResultSets = [[NSMutableSet alloc] init];
+ _db = nil;
+ _logsErrors = YES;
+ _crashOnErrors = NO;
+ _maxBusyRetryTimeInterval = 2;
}
return self;
@@ -102,8 +101,9 @@
return NO;
}
- if (_busyTimeout > 0.0) {
- sqlite3_busy_timeout(_db, (int)(_busyTimeout * 1000));
+ if (_maxBusyRetryTimeInterval > 0.0) {
+ // set the handler
+ [self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval];
}
@@ -122,8 +122,9 @@
return NO;
}
- if (_busyTimeout > 0.0) {
- sqlite3_busy_timeout(_db, (int)(_busyTimeout * 1000));
+ if (_maxBusyRetryTimeInterval > 0.0) {
+ // set the handler
+ [self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval];
}
return YES;
@@ -169,15 +170,44 @@
}
-- (void)setRetryTimeout:(NSTimeInterval)timeout {
- _busyTimeout = timeout;
- if (_db) {
- sqlite3_busy_timeout(_db, (int)(timeout * 1000));
+static int FMDatabaseBusyHandler(void *f, int count) {
+
+ FMDatabase *self = (__bridge FMDatabase*)f;
+
+ if (count == 0) {
+ self->_startBusyRetryTime = [NSDate timeIntervalSinceReferenceDate];
+ return 1;
+ }
+
+ NSTimeInterval delta = [NSDate timeIntervalSinceReferenceDate] - (self->_startBusyRetryTime);
+
+ if (delta < [self maxBusyRetryTimeInterval]) {
+ sqlite3_sleep(50); // milliseconds
+ return 1;
+ }
+
+ return 0;
+}
+
+- (void)setMaxBusyRetryTimeInterval:(NSTimeInterval)timeout {
+
+ _maxBusyRetryTimeInterval = timeout;
+
+ if (!_db) {
+ return;
+ }
+
+ if (timeout > 0) {
+ sqlite3_busy_handler(_db, &FMDatabaseBusyHandler, (__bridge void *)(self));
+ }
+ else {
+ // turn it off otherwise
+ sqlite3_busy_handler(_db, nil, nil);
}
}
-- (NSTimeInterval)retryTimeout {
- return _busyTimeout;
+- (NSTimeInterval)maxBusyRetryTimeInterval {
+ return _maxBusyRetryTimeInterval;
}
@@ -660,37 +690,26 @@
[statement reset];
}
-
-
if (!pStmt) {
+
+ rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
- BOOL retry;
-
- do {
- retry = NO;
- rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
-
- /*if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
- retry = YES;
+ if (SQLITE_OK != rc) {
+ if (_logsErrors) {
+ NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
+ NSLog(@"DB Query: %@", sql);
+ NSLog(@"DB Path: %@", _databasePath);
}
- else */if (SQLITE_OK != rc) {
- if (_logsErrors) {
- NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
- NSLog(@"DB Query: %@", sql);
- NSLog(@"DB Path: %@", _databasePath);
- }
-
- if (_crashOnErrors) {
- NSAssert(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
- abort();
- }
-
- sqlite3_finalize(pStmt);
- _isExecutingStatement = NO;
- return nil;
+
+ if (_crashOnErrors) {
+ NSAssert(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
+ abort();
}
- } while (retry);
+ sqlite3_finalize(pStmt);
+ _isExecutingStatement = NO;
+ return nil;
+ }
}
id obj;
@@ -846,8 +865,6 @@
if (!pStmt) {
rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0);
- #pragma message "FIXME: need the busy loop here."
-
if (SQLITE_OK != rc) {
if (_logsErrors) {
NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
@@ -942,55 +959,31 @@
** executed is not a SELECT statement, we assume no data will be returned.
*/
- BOOL retry;
- NSInteger numberOfRetries = 0;
+ rc = sqlite3_step(pStmt);
- do {
- retry = NO;
- rc = sqlite3_step(pStmt);
-
- if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) {
- retry = YES;
-
- if (SQLITE_LOCKED == rc) {
- rc = sqlite3_reset(pStmt);
- if (rc != SQLITE_LOCKED) {
- NSLog(@"Unexpected result from sqlite3_reset (%d) eu", rc);
- }
- }
-
-#pragma message "FIXME: HOW DO WE KNOW HOW TO BREAK OUT OF THIS? 10 is kind of a magic number?"
-
- if ((numberOfRetries++ > 10)) {
- NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
- NSLog(@"Database busy");
- retry = NO;
- }
- }
- else if (SQLITE_DONE == rc) {
- // all is well, let's return.
- }
- else if (SQLITE_ERROR == rc) {
- if (_logsErrors) {
- NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db));
- NSLog(@"DB Query: %@", sql);
- }
+ if (SQLITE_DONE == rc) {
+ // all is well, let's return.
+ }
+ else if (SQLITE_ERROR == rc) {
+ if (_logsErrors) {
+ NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db));
+ NSLog(@"DB Query: %@", sql);
}
- else if (SQLITE_MISUSE == rc) {
- // uh oh.
- if (_logsErrors) {
- NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db));
- NSLog(@"DB Query: %@", sql);
- }
+ }
+ else if (SQLITE_MISUSE == rc) {
+ // uh oh.
+ if (_logsErrors) {
+ NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db));
+ NSLog(@"DB Query: %@", sql);
}
- else {
- // wtf?
- if (_logsErrors) {
- NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db));
- NSLog(@"DB Query: %@", sql);
- }
+ }
+ else {
+ // wtf?
+ if (_logsErrors) {
+ NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db));
+ NSLog(@"DB Query: %@", sql);
}
- } while (retry);
+ }
if (rc == SQLITE_ROW) {
NSAssert(NO, @"A executeUpdate is being called with a query string '%@'", sql);