症狀表現:
總是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的調用,可見上述代碼的問題