症状表现:
总是crash ,后台打印:
inDatabase: was called reentrantly on the same queue, which would lead to a deadlock
错误代码如下:
//use fmdb larger bugs ,不能在执行一个fmdbqueue还没有执行完毕的时候,去执行另外一个fmdbqueue
[[SQLUtils getInstance]saveRulesAction:rulesArray rulesDescription:ruleDescription result:^(BOOL sucess) {
[[SQLUtils getInstance]updateRules];
if(done){
done(sucess);
//the rule has changed we should check it now
if(sucess){
dispatch_async(dispatch_get_main_queue(), ^{
[self checkRules];
});
}
}
}];
-(void)saveRulesAction:(NSArray*)rules rulesInfo:(NSArray*)ruleInfo rulesDescription:(NSArray*)rulesDescription result:(void(^)(BOOL sucess))done
{
[self checkDBQueue];
__block BOOL is_exculate_success = NO;
[queue inDatabase:^(FMDatabase *db) {
[db beginTransaction];
@try{
for(int i=0;i<[ruleInfo count];i++){
//delete the rules in db
BOOL rc = [db executeUpdate:@"delete from rulesAction where ruleType=?",ruleInfo[i]];
if(!rc){
debugLog(@"table rulesAction delete info error! info:%@",[db lastErrorMessage]);
}
}
//ruleType actionType TEXT NOT NULL)"];
//delete rules description info
BOOL rc = [db executeUpdate:@"delete from rulesDescription"];
if(!rc){
debugLog(@"table rulesDescription delete info error! info:%@",[db lastErrorMessage]);
}
if(rulesDescription!=nil){
//save rules description info
rc= [db executeUpdate:@"insert into rulesDescription(uuid,displayName,description) values (?,?,?)",rulesDescription[0],rulesDescription[1],rulesDescription[2]];
}else{
// rules description is nill
//debugLog(@"rules description is nil");
}
//save all rules and action
for (int i =0; i<[rules count]; i++) {
NSArray * rule = rules[i];
BOOL rc = [db executeUpdate:@"insert into rulesAction(ruleType,actionType,time,actionIndex,ruleLevel,hasDone,rulesKey,ruleTime) values (?,?,?,?,?,?,?,datetime('now'))",rule[0],rule[1],rule[2],rule[3],rule[4],rule[5],rule[6]];
if(!rc){
debugLog(@"saveRulesAction into db failed! db error info is %@",[currentDB lastErrorMessage]);
}
}
if (done)
{
done(YES);
}
}
@catch (NSException *exception) {
[db rollback];
debugLog( @"Failed to open database file with message %@ in saveRulesAction", [db lastErrorMessage]);
if (done)
{
done(NO);
}
}
@finally {
[db commit];
}
}];
}
正确代码:
-(void)saveRulesAction:(NSArray*)rules rulesInfo:(NSArray*)ruleInfo rulesDescription:(NSArray*)rulesDescription result:(void(^)(BOOL sucess))done
{
[self checkDBQueue];
__block BOOL is_exculate_success = NO;
[queue inDatabase:^(FMDatabase *db) {
[db beginTransaction];
@try{
for(int i=0;i<[ruleInfo count];i++){
//delete the rules in db
BOOL rc = [db executeUpdate:@"delete from rulesAction where ruleType=?",ruleInfo[i]];
if(!rc){
debugLog(@"table rulesAction delete info error! info:%@",[db lastErrorMessage]);
}
}
//ruleType actionType TEXT NOT NULL)"];
//delete rules description info
BOOL rc = [db executeUpdate:@"delete from rulesDescription"];
if(!rc){
debugLog(@"table rulesDescription delete info error! info:%@",[db lastErrorMessage]);
}
if(rulesDescription!=nil){
//save rules description info
rc= [db executeUpdate:@"insert into rulesDescription(uuid,displayName,description) values (?,?,?)",rulesDescription[0],rulesDescription[1],rulesDescription[2]];
}else{
// rules description is nill
//debugLog(@"rules description is nil");
}
//save all rules and action
for (int i =0; i<[rules count]; i++) {
NSArray * rule = rules[i];
BOOL rc = [db executeUpdate:@"insert into rulesAction(ruleType,actionType,time,actionIndex,ruleLevel,hasDone,rulesKey,ruleTime) values (?,?,?,?,?,?,?,datetime('now'))",rule[0],rule[1],rule[2],rule[3],rule[4],rule[5],rule[6]];
if(!rc){
debugLog(@"saveRulesAction into db failed! db error info is %@",[currentDB lastErrorMessage]);
}
}
is_exculate_success = YES;
}
@catch (NSException *exception) {
[db rollback];
debugLog( @"Failed to open database file with message %@ in saveRulesAction", [db lastErrorMessage]);
is_exculate_success = NO;
}
@finally {
[db commit];
}
}];
if(done){
done(is_exculate_success);
}
}
问题说明:一个queue的block还没有执行完,就在done block块中执行了,另外的queue查询;导致程序中出现了两个queue;
解决过程:
根据报错情况调查:
- (void)inDatabase:(void (^)(FMDatabase *db))block {
/* Get the currently executing queue (which should probably be nil, but in theory could be another DB queue
* and then check it against self to make sure we're not about to deadlock. */
FMDatabaseQueue *currentSyncQueue = (__bridge id)dispatch_get_specific(kDispatchQueueSpecificKey);
assert(currentSyncQueue != self && "inDatabase: was called reentrantly on the same queue, which would lead to a deadlock");
FMDBRetain(self);
dispatch_sync(_queue, ^() {
NSLog(@"dispatch_sync queue start");
FMDatabase *db = [self database];
block(db);
if ([db hasOpenResultSets]) {
NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]");
#if defined(DEBUG) && DEBUG
NSSet *openSetCopy = FMDBReturnAutoreleased([[db valueForKey:@"_openResultSets"] copy]);
for (NSValue *rsInWrappedInATastyValueMeal in openSetCopy) {
FMResultSet *rs = (FMResultSet *)[rsInWrappedInATastyValueMeal pointerValue];
NSLog(@"query: '%@'", [rs query]);
}
#endif
}
NSLog(@"dispatch_sync queue end");
});
FMDBRelease(self);
}
上述代码中居然没有执行这个NSLog(@"dispatch_sync queue end");,就出现了死锁,说明上一个indatabase:还没有执行完毕,另外一个就又开始了,根据程序的执行顺序一一排查,block块外的queue还没有执行完毕,就开是了另外一个indatabase的调用,可见上述代码的问题