Reputation: 22966
Here's pseudo code of what I have in a method:
NSCondition condition = [[NSCondition alloc] init];
int predicate = 0;
dispatch_sync(dispatch_get_main_queue(), ^
{
[condition lock]; // Lock-0
});
bindBlock1ForDataReceived(^()
{
// Not main thread here.
// Get on main thread, because lock and unlock must be run on same thread.
dispatch_sync(dispatch_get_main_queue(), ^
{
predicate = 1;
[condition signal];
[condition unlock]; <<<<---- "unlocked when not locked"
});
});
bindBlock2ForNoDataAvailable(^()
{
// Not main thread here.
// Get on main thread, because lock and unlock must be run on same thread.
dispatch_sync(dispatch_get_main_queue(), ^
{
predicate = 2;
[condition signal];
[condition unlock];
});
});
[condition lock]; // Lock-1
while (predicate == 0)
{
[condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
}
[condition unlock];
if (predicate == 2)
{
[condition lock]; // Lock-2
[condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
[condition unlock];
}
The issue is that I get an "unlocked when not locked" warning from iOS (see above) when first event 2 occurs and then event 1.
Now let me explain what I'm trying to accomplish: This is part of a data fetcher. Normal cases data is received and block1 is executed: no issues. Sometimes the no-data block2 is spuriously executed first, shortly followed by block1; this is when I get the NSCondition
warning. To catch this rare case, I wait for 2.0
seconds. Here's what happens:
predicate
is no longer 0
so there's no wait.if
-statement whose condition (predicate == 2)
is true.2.0
seconds.The root cause (see above) is that the lock is acquired by the method (the worker/consumer), while it should have been acquired by the data producer. I've spend a lot of time trying to figure this out; one of the thoughts I had is using two NSCondition
s, but I could not figure this out because things are rather intertwined.
Note: I find it strange that the warning does not appear at the unlock
inside the if
-statement.
Thanks for your time!
Upvotes: 0
Views: 883
Reputation: 13642
The simplest approach with more than lock is to use
dispatch_group_t confirmGroup = dispatch_group_create(); // 1
if (requestContacts) {
dispatch_group_enter(confirmGroup); // 2
[Extractor requestAccessAddressBook:^(BOOL isComplete) {
if (isComplete) {
dispatch_group_leave(confirmGroup); //2
}
}];
}
if (requestEvent) {
dispatch_group_enter(confirmGroup); // 3
[Extractor requestAccessEvents:^(BOOL isComplete) {
if (isComplete) {
dispatch_group_leave(confirmGroup); // 3
}
}];
}
if (requestPhoto) {
dispatch_group_enter(confirmGroup); // 4
[Extractor requestAccessPhotos:^(BOOL isComplete) {
if (isComplete) {
dispatch_group_leave(confirmGroup); //4
}
}];
}
NSLog(@"dispatch_group_wait confirmations");
dispatch_group_wait(confirmGroup, DISPATCH_TIME_FOREVER); // 5
Upvotes: 2