I am try to make one function wait for another, and I would like to use NSCondionLock in order to accomplish this. I am not asking for help, but really hoping someone could
EDIT: as @Bonshington commented, this answer refers to NSCondition
(as opposed to NSConditionLock
):
- (void) method1 {
[myCondition lock];
while (!someCheckIsTrue)
[myCondition wait];
// Do something.
[myCondition unlock];
}
- (void) method2 {
[myCondition lock];
// Do something.
someCheckIsTrue = YES;
[myCondition signal];
[myCondition unlock];
}
The someCheckIsTrue
can be anything, it could be a simple BOOL variable or even something like [myArray count] == 0 && color == kColorRed
, it doesn't matter. It only matters that in one method you check for a condition while you have the lock and in another method you make changes that can make the condition become true also while having the lock. The magic is the wait
and signal
part: the wait
actually unlocks the lock and reacquires it after some other thread called signal
.
NSConditionLock
example program.
#import <Foundation/Foundation.h>
#define IDLE 0
#define START 1
#define TASK_1_FINISHED 2
#define TASK_2_FINISHED 3
#define CLEANUP_FINISHED 4
#define SHARED_DATA_LENGTH 1024 * 1024 * 1024
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:IDLE];
char *shared_data = calloc(SHARED_DATA_LENGTH, sizeof(char));
[NSThread detachNewThreadWithBlock:^{
[lock lockWhenCondition:START];
NSLog(@"[Thread-1]: Task 1 started...");
for (size_t i = 0; i < SHARED_DATA_LENGTH; i++) {
shared_data[i] = 0x00;
}
[lock unlockWithCondition:TASK_1_FINISHED];
}];
[NSThread detachNewThreadWithBlock:^{
[lock lockWhenCondition:TASK_1_FINISHED];
NSLog(@"[Thread-2]: Task 2 started...");
for (size_t i = 0; i < SHARED_DATA_LENGTH; i++) {
char c = shared_data[i];
shared_data[i] = ~c;
}
[lock unlockWithCondition:TASK_2_FINISHED];
}];
[NSThread detachNewThreadWithBlock:^{
[lock lockWhenCondition:TASK_2_FINISHED];
NSLog(@"[Thread-3]: Cleaning up...");
free(shared_data);
[lock unlockWithCondition:CLEANUP_FINISHED];
}];
NSLog(@"[Thread-main]: Threads set up. Waiting for 2 task to finish");
[lock unlockWithCondition:START];
[lock lockWhenCondition:CLEANUP_FINISHED];
NSLog(@"[Thread-main]: Completed");
}
return 0;
}
For those that want a sample test class here I post what I did to play around and understand how NSCondition works.
// --- MyTestClass.h File --- //
@interface MyTestClass
- (void)startTest;
@end
// --- MyTestClass.m File --- //
@implementation MyTestClass
{
NSCondition *_myCondition;
BOOL _someCheckIsTrue;
}
- (id)init
{
self = [super init];
if (self)
{
_someCheckIsTrue = NO;
_myCondition = [[NSCondition alloc] init];
}
return self;
}
#pragma mark Public Methods
- (void)startTest
{
[self performSelectorInBackground:@selector(_method1) withObject:nil];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(5);
[self performSelectorInBackground:@selector(_method2) withObject:nil];
});
}
#pragma mark Private Methods
- (void)_method1
{
NSLog(@"STARTING METHOD 1");
NSLog(@"WILL LOCK METHOD 1");
[_myCondition lock];
NSLog(@"DID LOCK METHOD 1");
while (!_someCheckIsTrue)
{
NSLog(@"WILL WAIT METHOD 1");
[_myCondition wait];
NSLog(@"DID WAIT METHOD 1");
}
NSLog(@"WILL UNLOCK METHOD 1");
[_myCondition unlock];
NSLog(@"DID UNLOCK METHOD 1");
NSLog(@"ENDING METHOD 1");
}
- (void)_method2
{
NSLog(@"STARTING METHOD 2");
NSLog(@"WILL LOCK METHOD 2");
[_myCondition lock];
NSLog(@"DID LOCK METHOD 2");
_someCheckIsTrue = YES;
NSLog(@"WILL SIGNAL METHOD 2");
[_myCondition signal];
NSLog(@"DID SIGNAL METHOD 2");
NSLog(@"WILL UNLOCK METHOD 2");
[_myCondition unlock];
NSLog(@"DID UNLOCK METHOD 2");
}
@end
// --- Output --- //
/*
2012-11-14 11:01:21.416 MyApp[8375:3907] STARTING METHOD 1
2012-11-14 11:01:21.418 MyApp[8375:3907] WILL LOCK METHOD 1
2012-11-14 11:01:21.419 MyApp[8375:3907] DID LOCK METHOD 1
2012-11-14 11:01:21.421 MyApp[8375:3907] WILL WAIT METHOD 1
2012-11-14 11:01:26.418 MyApp[8375:4807] STARTING METHOD 2
2012-11-14 11:01:26.419 MyApp[8375:4807] WILL LOCK METHOD 2
2012-11-14 11:01:26.419 MyApp[8375:4807] DID LOCK METHOD 2
2012-11-14 11:01:26.420 MyApp[8375:4807] WILL SIGNAL METHOD 2
2012-11-14 11:01:26.420 MyApp[8375:4807] DID SIGNAL METHOD 2
2012-11-14 11:01:26.421 MyApp[8375:4807] WILL UNLOCK METHOD 2
2012-11-14 11:01:26.421 MyApp[8375:3907] DID WAIT METHOD 1
2012-11-14 11:01:26.421 MyApp[8375:4807] DID UNLOCK METHOD 2
2012-11-14 11:01:26.422 MyApp[8375:3907] WILL UNLOCK METHOD 1
2012-11-14 11:01:26.423 MyApp[8375:3907] DID UNLOCK METHOD 1
2012-11-14 11:01:26.423 MyApp[8375:3907] ENDING METHOD 1
*/
Swift 5 version of @vilanovi answer from Playground:
let myCondition = NSCondition()
var someCheckIsTrue = false
func method1() {
print("STARTING METHOD 1")
print("WILL LOCK METHOD 1")
myCondition.lock()
print("DID LOCK METHOD 1")
while (!someCheckIsTrue) {
print("WILL WAIT METHOD 1")
myCondition.wait()
print("DID WAIT METHOD 1")
}
print("WILL UNLOCK METHOD 1")
myCondition.unlock()
print("DID UNLOCK METHOD 1")
print("ENDING METHOD 1")
}
func method2() {
print("STARTING METHOD 2")
print("WILL LOCK METHOD 2")
myCondition.lock()
print("DID LOCK METHOD 2")
someCheckIsTrue = true
print("WILL SIGNAL METHOD 2")
myCondition.signal()
print("DID SIGNAL METHOD 2")
print("WILL UNLOCK METHOD 2")
myCondition.unlock()
print("DID UNLOCK METHOD 2")
print("ENDING METHOD 2")
}
DispatchQueue.global().async {
method1()
}
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 0.5) {
method2()
}