after unlock a NSRecursiveLock at one queue, other queue still waiting - ios

I have some code not thread safe, So I tried to use NSRecursiveLock to prevent my work queue and main thread access some properties at the same time. I create two work queue(work queue and wait queue), work queue to do some heavy work. In order to prevent block main thread, I create another wait queue to wait the lock, so I can async to main thread after I get the lock.
I face two problem:
after the work queue unlock, wait queue still waiting.
code will get two kind of output
I think the output should be
1. work queue get lock
2. wait queue wait lock
3. post notification from work queue
noti called
unlock
get lock
4. main thread do work
but I get Output:
[Output 1]
1. work queue get lock
2. wait queue wait lock
3. post notification from work queue
noti called
unlock
[Output 2]
1. work queue get lock
2. wait queue wait lock
get lock
4. main thread do work
3. post notification from work queue
noti called
unlock
#interface ViewController ()
#property (nonatomic) NSLock *lock;
#property (nonatomic) NSRecursiveLock *recursiveLock;
#property (nonatomic) dispatch_queue_t queue;
#property (nonatomic) dispatch_queue_t lockWaitQueue;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
_lockWaitQueue = dispatch_queue_create("wait_queue", DISPATCH_QUEUE_SERIAL);
[[NSNotificationCenter defaultCenter] addObserverForName:#"noti" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
NSLog(#"noti called");
}];
_lock = [NSLock new];
_recursiveLock = [NSRecursiveLock new];
[self tryFixDeadLockCace];
}
- (void)tryFixDeadLockCace {
NSRecursiveLock *lock = _recursiveLock;
dispatch_queue_t queue = _queue;
dispatch_queue_t lockWaitQueue = _lockWaitQueue;
NSLog(#"1. work queue get lock");
dispatch_async(queue, ^{
[lock lock];
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_async(lockWaitQueue, ^{
NSLog(#"2. wait queue wait lock");
[lock lock];
NSLog(#"get lock");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"4. main thread do work");
[lock unlock];
});
});
});
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), queue, ^{
NSLog(#"3. post notification from work queue");
[[NSNotificationCenter defaultCenter] postNotificationName:#"noti" object:nil];
NSLog(#"unlock");
[lock unlock];
});
}
- (void)noti {
}
#end

if user NSRecursiveLock, should not use GCD, because lock an unlock may not on the same thread

Related

How to wait for completion handler inside a #synchronized block?

I want to call a completion handler synchronously inside a critical section (using #synchronized block). I am trying to wait for completion handler using semaphore, but the semaphore signal is never called.
Here is what I am doing:
NSNumber *lock = 0;
#synchronized(lock) {
// critical section code begins
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[self someMethodWithCompletionHandler:^(BOOL result) {
// execute completion handler
dispatch_semaphore_signal(sema);
}];
// wait for completion block to finish
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
// critical section code ends
}
I believe, due to #synchronized block the completion handler is called on the same thread as the caller which results in a deadlock. Is that right? If yes, how else can this be achieved?
Thanks!
I would avoid #synchronized and use a serial dispatch queue to serialize the work in the critical section. You can still use a semaphore to block the serial dispatch queue until the asynchronous operation is complete.
The serial dispatch queue guarantees that only one block of code can enter the critical section at a time and there is no risk of blocking the main queue.
You will need to create the serial queue during your class initialisation
#property (strong,nonatomic) dispatch_queue_t serialQueue;
- (id)init {
if (self = [super init]) {
self.serialQueue = dispatch_queue_create("com.example.CriticalTaskQueue", NULL);
}
return self;
}
- submitSomeCriticalWork() {
dispatch_async(self.serialQueue, ^{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[self someMethodWithCompletionHandler:^(BOOL result) {
// execute completion handler
dispatch_semaphore_signal(sema);
}];
// wait for completion block to finish
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
// critical section code ends
}];
}

Dispatch Queue and NSOperation queue

I am creating a serial queue in which i add two task as shown below
dispatch_queue_t serial = dispatch_queue_create("com.apple.serial", DISPATCH_QUEUE_SERIAL);
**//Task 1**
dispatch_async(serial, ^{
[NMUserAPIManager getUserProfileData:^(NMUser *objUser) {
NSLog(#"Get User Profile .....");
_objUser = objUser;
}];
});
**//Task 2**
dispatch_async(serial, ^{
[NMUserAPIManager getUserRecentTransactionData:^(NSDictionary *responseDictionary) {
_accountTableView.hidden = NO;
[self recentTransactionSetup:responseDictionary];
NSLog(#"Get User Recent transaction");
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadTableData];
});
}];
});
Inside that two task i am calling web service with NSURLSession. Problem is that before my Task 1 completion handle Task2 completion handle get called. According to theory by using serial queue each task waits for the previous task to finish before being executed. It my understanding is correct.
NSURLSession's already run on a background thread, so the issue you are seeing here is that as far as your serial queue is concerned once you call 'getUserProfileData:' technically the work for that block in your queue is finished because the NSURLSession is running on a different thread. If your main goal here is to simply call your second task after your first one completes I don't think you need your own queue you would probably be better off simply doing something like:
[NMUserAPIManager getUserProfileData:^(NMUser *objUser) {
NSLog(#"Get User Profile .....");
_objUser = objUser;
[self getUserTransactions];
}];
-(void)getUserTransactions
{
[NMUserAPIManager getUserRecentTransactionData:^(NSDictionary *responseDictionary) {
_accountTableView.hidden = NO;
[self recentTransactionSetup:responseDictionary];
NSLog(#"Get User Recent transaction");
dispatch_async(dispatch_get_main_queue(), ^{
[self reloadTableData];
});
}];
}
EDIT:
If you are looking for something a little more robust I would check out this post for how you can subclass NSOperation to make your own Asynchronous Operation which you can then use with an NSOperationQueue.

How to run a process in background thread iOS

I want to run a task after 6sec in background in a separate thread. I used this code for that.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self getUnsyncNamesFromServer];
}
I am not sure this run in a background thread. Do i need to use dispatch_async for this purpose. What is the best approach for this kind of situation.
dispatch_async is what you want. In the code you've used, the method inside the block will be after 6 seconds on the main queue.
For the background queue, use the follow:
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^{
[weakSelf getUnsyncNamesFromServer];
});
For further reference, here's the GCD Apple Doc: https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_async
Your code would run in the main thread, i.e., not in background, because you are using dispatch_get_main_queue.
Instead of using the main queue, I would create a new one. The code would be something like:
dispatch_queue_t unsyncNamesQueue =
dispatch_queue_create("UnsyncNamesFromServer", DISPATCH_QUEUE_SERIAL);
//....
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC),
unsyncNamesQueue, ^{
[weakSelf getUnsyncNamesFromServer];
}
);
Be sure to read https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
This line will put your task on main thread not on separate thread
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
To put in secondary thread you have to put on global concurrent queue or make your own private queue.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC),dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //Put on global queue to run seprate thread
[self getUnsyncNamesFromServer];
}
Now to run in background(when application is in background state) you do not need to run on separate thread but if your application is taking too much time to do a task on main thread than you should put on separate thread as it is not advisable to block main thread for too much time.
Your code will not run in background state of application for that you need to register with iOS by calling beginBackgroundTaskWithExpirationHandler:
// Declare property in your class
#property (nonatomic) UIBackgroundTaskIdentifier backgroundTask;
-(void)yourfunction{
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
self.backgroundTask = UIBackgroundTaskInvalid;
}];
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC),dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //Put on global queue to run seprate thread
[weakSelf getUnsyncNamesFromServer];
if (weakSelf.backgroundTask != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:weakSelf.backgroundTask];
weakSelf.backgroundTask = UIBackgroundTaskInvalid;
}
});
}

How to timeout an asynchronous method when ran synchronously

This is essentially what I'm doing to run an asynchronous method synchronously:
This essentially works when called once, but when called multiple times, it will eventually stay inside the while loop and never get signaled. Any ideas on how to set a timer to eventually time out after sometime?
__block SomeClass *result = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
if (!error) {
result = (SomeClass *)ResponseObject;
}
dispatch_semaphore_signal(semaphore);
}];
});
// wait with a time limit
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]];
}
dispatch_release(semaphore);
Thanks
That looks kind of like GCD abuse to me. ;) Are you running the run loop because this is executing on the main thread? Why not just use a dispatch_async() from your completion handler to invoke a handler on the main thread? eg:
- (void)handleDataReady: (id) results error: (NSError *) error {
// update your app
}
- (void)performAsyncUpdate {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[self handleDataReady:responseObject error:error];
}];
});
}
If you really want to make it synchronous, i.e. blocking the calling thread until the operation completes then use the following pattern (of course you want to avoid blocking threads if possible)
NSCondition *waitCondtion = [NSCondition new];
__block BOOL completed = NO;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0UL);
dispatch_async(queue, ^{
[[SomeManager sharedInstance] someMethodWithCallback:^(id responseObject, NSError *error) {
if (!error) {
result = (SomeClass *)ResponseObject;
}
[waitCondtion lock];
completed = YES;
[waitCondition signal];
[waitCondition unlock];
}];
});
[waitCondtion lock];
if (!completed)
[waitCondtion wait];
[waitCondition unlock];
You can also use "waitUntilDate:" to timeout the wait after a period.
However, this pattern only works as long as the "someMethodWithCallback does not call its callback block on the same thread that is being blocked. I have copied your code because it is not obvious how "someMethodWithCallback" is implemented. Since this method is using an asynchronous pattern, then it must be doing something asynchronously therefore why are you calling it inside a dispatch_async? What thread will it call its callback block on?
You should "fill" the completion handler with whatever code you require to process the result when the completion handler finished (and also completely removing that run loop).
In order to "abort" an asynchronous operation, you should provide a cancel message which you send the asynchronous result provider.
In your case, since you have a singleton, the cancel message would have to be send like this:
[[SomeManager sharedInstance] cancel];
When the operation receives the cancel message, it should as soon as possible abort its task and call the completion handler with an appropriate NSError object indicating that it has been cancelled.
Note, that cancel messages may be asynchronous - that means, when it returns, the receiver may still execute the task.
You may achieve a "timeout" with setting up a timer, which sends the cancel message the operation, unless it has been invalidated when the operation finished.

Async dispatch queue triggering error

I am trying to code a simple iPhone app that does some modification to file contents in the app sandbox.
I need to make the process lazy. It is also important to ensure serialisation of consecutive file add/modify operation.
I've got this method:
#import "ProcessData.h"
#import <dispatch/dispatch.h>
#interface ProcessData ()
#end
dispatch_queue_t backgroundQueue;
NSLock *lock;
#implementation ProcessData
-(int)newEffects: (int) idNo : (NSString*) someData
{
[lock lock];
dispatch_async(backgroundQueue, ^(void)
{
#try
{
DiskAccess *dAccess = [[DiskAccess alloc] init];
//some operations here
[dAccess release];
}
#catch (NSException *exception)
{
}
#finally
{
printf("done with this stuff!");
}
})
[lock release];
}
#end
When I run this code, the application crashes unceremoniously and I get to see this:
0x1b1f2b5: cmpl $1, 36(%esi) Thread 1: EXC_BAD_ACCESS(code=2,address=0x24)
However, if I change the line:
dispatch_async(backgroundQueue, ^(void)
TO
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
...the system runs without a hiccup.
I'd like to know:
How to get my background queue to work. What am I doing wrong here?
If I use dispatch_get_global_queue and if there are multiple, back
to back file access requests, how can I ensure that the operations
are carried out sequentially?
Am I right that you initialize backgroundQueue nowhere? You must initialize backgroundQueue before using it. For example add code
backgroundQueue = dispatch_queue_create(#"Background queue name", NULL);
and only after that call
dispatch_async(backgroundQueue, ^{
//some code here
});
Perhaps a similar error with lock.

Resources