Behavior of GCD async method not understandable - ios

I have this below in my iOS app.
I am learning GCD. so, trying out the simple things.
Here, The output of this is confusing me.
Why always the 2. set of statements are coming first and then 1.?
Even though I am dispatching the two tasks to GCD, first I am dispatching 1. set first. It is not really a huge task so that 1. set and 2.set will overlap in time. Its just a simple task to print what threads it is running on.
I have run it several times expecting that it would give different results as how it happens in threading environment.
Please describe.
2. Crnt Thread = <NSThread: 0x10920fee0>{name = (null), num = 1}
2. Main thread = <NSThread: 0x10920fee0>{name = (null), num = 1}
1. Crnt Thread = <NSThread: 0x10920fee0>{name = (null), num = 1}
1. Main thread = <NSThread: 0x10920fee0>{name = (null), num = 1}
3. Crnt Thread = <NSThread: 0x10920fee0>{name = (null), num = 1}
3. Main thread = <NSThread: 0x10920fee0>{name = (null), num = 1}
Code here:
void displayAlertView(void *paramContext)
{
NSLog(#"3. Crnt Thread = %#",[NSThread currentThread]);
NSLog(#"3. Main thread = %#", [NSThread mainThread]);
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
dispatch_queue_t myQueue = dispatch_get_main_queue();
AlertViewData *contextData = (AlertViewData *)malloc(sizeof(AlertViewData));
dispatch_async(myQueue,^(void){
NSLog(#"1. Crnt Thread = %#",[NSThread currentThread]);
NSLog(#"1. Main thread = %#", [NSThread mainThread]);
});
if(contextData != NULL)
{
NSLog(#"2. Crnt Thread = %#",[NSThread currentThread]);
NSLog(#"2. Main thread = %#", [NSThread mainThread]);
dispatch_async_f(myQueue, contextData, displayAlertView);
}
return YES;
}

The "2" statements come first because that code is getting executed before the asynchronous block has had a chance to be setup and run. That's the whole point of dispatch_async. Such code gets run on another thread while the current thread continues on its merry way.
If you updated both blocks of code to use a loop that logs 100 logs statements, then you would probably see some mixing of "1" and "2" statements.
But with just the two logs, they happen so fast, the "2" logs complete before the block with the "1" logs has had a chance to kick in. Look at the timestamps in the log to see.
UPDATE
The above was written under the assumption that myQueue was a background queue. As Martin pointed out, it's the main queue. Since it is the main queue, the answer is quite a bit different.
Since you are doing asynchronous calls on the main queue, everything is done on the same main thread. Each call to dispatch_async is like adding it to the end of the line.
The currently running code is at the head of the line. When you call dispatch_async for the block with the "1" logs, that block is added to the end of the line and will be run when the current code is done. Then you call the dispatch_async_f for the "3" logs. Those get added to the end the line (after the "1" logs).
So once the current runloop completes (and the didFinishLaunchingWithOptions` method returns), then the next bit in line is run. This is your "1" logs. When that is done, the next block in the queue is run (your "3" logs).

Related

Making sure I'm explaining nested GCD correctly

So I'm putting 10 tasks on a concurrent queue using dispatch_async. They do not block the next task, and gets processed in order. My UI is responsive.
for (int i = 0; i < 10; i++) {
dispatch_async(concurrencyQueue, ^() {
NSLog(#"..calling insertion method to insert record %d", i);
dispatch_sync(serialQueue, ^() {
//this is to simulate writing to database
NSLog(#"----------START %d---------", i);
[NSThread sleepForTimeInterval:1.0f];
NSLog(#"--------FINISHED %d--------", i);
});
});
}
Within each task, we simulate a write to database with a "1 sec sleep" on a serial Queue via dispatch_sync.
I always thought dispatch_sync blocks everyone, and syncs its tasks because that's how it behaves when I use it individually. However, in this situation, it does not block the main thread. Instead, it runs beautifully in the background like I want it.
Is it because whatever thread is associated with the queue is being affected?
For example, the main thread is executing the concurrent queue via dispatch_async and that's why it is not blocked.
The dispatch_sync only syncs and blocks against the background thread that's working on the concurrent queue. Hence, the dispatch_sync is associated with the background thread, thus never affecting my UI main thread.
Is my thinking correct?
thank you!
You never block the main thread because your code is running on either the threads of the concurrencyQueue or the thread of the serialQueue. None of those are the main thread.
All of the calls to sleep happen one by one on the thread of the serialQueue. So it is the thread of the serialQueue that is blocked.
However, since you dispatch to the serialQueue using dispatch_sync, you are also blocking each thread of the concurrent queue. This would be better pictured if you add another NSLog after the call to dispatch_sync.
for (int i = 0; i < 10; i++) {
dispatch_async(concurrencyQueue, ^() {
NSLog(#"..calling insertion method to insert record %d", i);
dispatch_sync(serialQueue, ^() {
//this is to simulate writing to database
NSLog(#"----------START %d---------", i);
[NSThread sleepForTimeInterval:1.0f];
NSLog(#"--------FINISHED %d--------", i);
});
NSLog(#"..called insertion method to insert record %d", i);
});
}
That 2nd NSLog after the dispatch_sync will show you better how the dispatch_sync is affecting the calls to dispatch_async.
Yes, you are right. dispatch_sync() blocks only the thread the queue is running on.

Doing simple malloc/free within dispatch_async causes memory leak on iOS9

I just got a memory leak in my code after I updated my iPad to iOS9, which worked fine on iOS8 and iOS7.
I have an anonymous thread created by the following code:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self threadWork];
});
And the thread does a pair of malloc/free call like this:
- (void)threadWork {
// Create a serial queue.
dispatch_queue_t mySerialQueue = dispatch_queue_create("myQueue", NULL);
while (1) {
// Do a simple malloc.
int *foo = (int *)malloc(1024);
// Do free in serial queue.
dispatch_async(mySerialQueue, ^{
free(foo);
});
[NSThread sleepForTimeInterval:1.0 / 60.0];
}
}
This routing will keep the memory usage increasing and finally crashes device on iOS 9. The problem also happened on new/delete in Objective-C++.
I found some other way to do this without memory leak:
Use main queue or global queue to instead the serial queue.
Create concurrent queue instead the serial queue.
Use [NSThread detachNewThreadWithSelector:toTarget:withObject:] to create the thread instead GCD.
I don't understand why this simple routing causes this problem.
I've searched this on google but found nothing.
How can I do this with keeping serial queue and GCD anonymous thread?
Update:
I tried to put NSLog commands in my code to figure out when will the malloc/free be called. The result shows that both of them are called immediately and come in pair. I also tried to slow the thread down to once per second, but the problem still here.
The test code of thread:
- (void)threadWork {
uint64_t mallocCount = 0;
__block uint64_t freeCount = 0;
dispatch_queue_t mySerialQueue = dispatch_queue_create("MyQueue", NULL);
while (1) {
void *test = malloc(1024);
NSLog(#"malloc %llu", ++mallocCount);
dispatch_async(mySerialQueue, ^{
free(test);
NSLog(#"free %llu", ++freeCount);
});
[NSThread sleepForTimeInterval:1.0];
}
}
The console result:
...
2015-10-23 09:51:33.876 OS9MemoryTest[759:153135] malloc 220
2015-10-23 09:51:33.876 OS9MemoryTest[759:153133] free 220
2015-10-23 09:51:34.877 OS9MemoryTest[759:153135] malloc 221
2015-10-23 09:51:34.878 OS9MemoryTest[759:153133] free 221
2015-10-23 09:51:35.883 OS9MemoryTest[759:153135] malloc 222
2015-10-23 09:51:35.883 OS9MemoryTest[759:153133] free 222
I think I've found a better way to do this without leak problem rather than using dispatch_sync.
The point seems to be the setting of Quality of Service (QoS) class of serial queue.
Doing free in a queue which have QOS_CLASS_UNSPECIFIED QoS class causes this problem.
In my question, I free memory in a serial queue which was created by the following call :
dispatch_queue_t mySerialQueue = dispatch_queue_create("MyQueue", NULL);
Its QoS setting is QOS_CLASS_UNSPECIFIED which causes this problem.
If create a serial queue with dispatch_queue_attr_t object, which have QoS setting excepted QOS_CLASS_UNSPECIFIED, the code runs perfectly without leaking:
- (void)threadWork {
// Create a serial queue with QoS class.
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, 0);
dispatch_queue_t mySerialQueue = dispatch_queue_create("myQueue", attr);
while (1) {
// Do a simple malloc.
int *foo = (int *)malloc(1024);
// Do free in serial queue.
dispatch_async(mySerialQueue, ^{
free(foo);
});
[NSThread sleepForTimeInterval:1.0 / 60.0];
}
}
I still don't understand why this problem would happened on iOS9,
but setting the QoS seems to make things work.

Serializable dispatch queue create

I need to create a serializable Dispatch queue.
I'm using AudioUnit to register from the microphone, and then encoding the frame on the created queue.
I have one object encoding, so I need always the same thread accessing it.
However, when I create my queue :
queue = dispatch_queue_create("com.myapp.queue", DISPATCH_QUEUE_SERIAL);
If I dispatch to this queue with :
NSLog(#"recording callback Thread Info: %#", [NSThread currentThread]);
dispatch_async(queue, ^{
[self processAudio];
});
- (void) processAudio
{
NSLog(#"processAudio Thread Info: %#", [NSThread currentThread]);
...
}
It is mostly using the same thread until one point where I get this :
recording callback Thread Info: <NSThread: 0x7fb0ab331b90>{number = 12, name = (null)}
processAudio Thread Info: <NSThread: 0x7fb0ab210140>{number = 7, name = (null)}
recording callback Thread Info: <NSThread: 0x7fb0ab331b90>{number = 12, name = (null)}
processAudio Thread Info: <NSThread: 0x7fb0a8e33950>{number = 8, name = (null)}
recording callback Thread Info: <NSThread: 0x7fb0ab331b90>{number = 12, name = (null)}
processAudio Thread Info: <NSThread: 0x7fb0ab212bf0>{number = 13, name = (null)}
The dispatch_async will switch thread, and then stick a while with thread number 13, until it will switch again.
Is that a normal behavior although I specified that I wanted a SERIAL queue ?
Should I be worried about switching thread, when using only one instance of an object, or is it really serialized ?
This is expected behavior and you should not worry about it. From Apple's docs (https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html):
Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue.

Why calling dispatch_sync in current queue not cause deadlock

the apple document says:(concurrencyProgrammingGuide,page49)
Important: You should never call the dispatch_sync or dispatch_sync_f function from a task that is executing in the same queue that you are planning to pass to the function. This is particularly important for serial queues, which are guaranteed to deadlock, but should also be avoided for concurrent queues.
but the code here not cause a deadlock, since i have ran it many times:
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^(){
NSLog(#"in outer queue: %#", [NSThread currentThread]);
dispatch_sync(concurrentQueue, ^(){
NSLog(#"do someting thread: %#", [NSThread currentThread]);
});
});
Yet,we all know,in main thread context, if we execute the code below,it will cause deadlock in main thread. so i am confused why calling dispatch_sync in the same thread, one not deadlock(the code above), the other opposite(the code below)?
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"________update__UI");
});
dispatch_get_global_queue() returns a system-defined global concurrent queue.
Serial Dispatch Queue (the main queue and user created queues with default flag) uses only just one thread. Concurrent Dispatch Queue (global queue, created queues with concurrent flag) uses multiple threads (aka thread pool). The number of thread is vary with system, situation.
Take a look at the following code.
dispatch_async(queue, ^(){
/* Task 1 */
dispatch_sync(queue, ^(){
/* Task 2 */
});
});
Task 1 and Task 2 should be executed on the same order as it was queued. Thus, Task 1 is executed, and then Task 2.
On Serial Dispatch Queue, dispatch_sync have to wait in order to execute Task 2 on the thread that is executing Task 1 right now. DEADLOCK.
On Concurrent Dispatch Queue, dispatch_sync usually doesn't need to wait to execute Task 2 on a thread in the thread pool. But the number of thread in the thread pool is not unlimited actually, sometimes dispatch_sync have to wait until some other task finished. That's why "but should also be avoided for concurrent queues". dispatch_sync is also highly optimized, it uses the same thread of Task 1 for Task 2 in some situation.
EDITED
Thus, dispatch_sync a block means the exactly same as ordinary block(function) call. In this case, DEADLOCK never happened.
EDITED
Test code.
#import <Foundation/Foundation.h>
void task2()
{
NSLog(#"task2: %#", [NSThread currentThread]);
}
void task1(dispatch_queue_t q)
{
NSLog(#"task1: %#", [NSThread currentThread]);
dispatch_sync(q, ^{
task2();
});
}
int main()
{
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
dispatch_async(q, ^{
task1(q);
});
dispatch_main();
return 0;
}
lldb log
(lldb) breakpoint set -l 6
(lldb) run
task1: <NSThread: 0x1001155a0>{number = 2, name = (null)}
task2: <NSThread: 0x1001155a0>{number = 2, name = (null)}
Process stopped
(lldb) bt
* thread #2: tid = 0x4dbcc, 0x0000000100000d34 a.out`task2 + 4 at a.m:5, queue = 'com.apple.root.default-qos', stop reason = breakpoint 1.1
* frame #0: 0x0000000100000d34 a.out`task2 + 4 at a.m:5
frame #1: 0x0000000100000dc5 a.out`__task1_block_invoke(.block_descriptor=<unavailable>) + 21 at a.m:12
frame #2: 0x00007fff8d6d6c13 libdispatch.dylib`_dispatch_client_callout + 8
frame #3: 0x00007fff8d6e19a1 libdispatch.dylib`_dispatch_sync_f_invoke + 39
frame #4: 0x0000000100000da3 a.out`task1(q=0x00007fff79749b40) + 67 at a.m:11
task1 function calls task2 function via libdispatch APIs but it almost the same as ordinary function call.

NSCondition locked by invalid thread

I'm attempting to create a multithreaded transition animation system, but am confused by seemingly corrupted state in an NSCondition object I'm using for synchronization. I've added some lines from my debugger below to illustrate what's happening:
po [NSThread currentThread]
<NSThread: 0x146f77a0>{name = Transition Animation, num = 4}
po _condition
<NSCondition: 0x1464dff0>{locked = YES, thread = 0xb083b000, name = nil}
p *(NSCondition*)_condition
(NSCondition) $5 = {
NSObject = {
isa = NSCondition
}
_priv = 0x00000000
}
What's confusing me is the address and name given by the condvar's description since they don't match up with the name and address of the NSThread that's calling -lock on the condition. Additionally, inspecting the actual contents of the NSCondition itself isn't helpful since its _priv pointer is nil. Does anyone have any experience with this or know how to fix it?
P.S. Before people suggest that I just use GCD for concurrency: I'm using CADisplayLinks for my animation, so I need to use NSThreads since CADisplayLinks require an NSRunLoop to trigger updates.

Resources