I have a long running function inside an asynchronous (serial) worker queue.
I know that sometimes this function hangs inside a particular openCV call.
For some reason this hang is also causing the main thread to hang.
When pausing and entering debug mode I see that there is a call to
semaphore_wait_trap()
on the main thread (Queue)
I can suspend the hanging thread (My worker queue) in debug mode and then this trap goes away and the GUI becomes responsive once again on the phone.
After unpausing the worker thread the GUI is responsive for 1-2 seconds (I suspect until this thread is activated again) and then the UI becomes unresponsive once again.
This thread makes no dispatch_sync() calls to the main thread/Queue
Is it possible that IOS pauses the main thread ("traps" it) because the worker is long running?
Can I force it to remove the block??
I am adding some print screens of the debug mode stack.
Before suspending the hanging Queue:
And the hanging thread:
And After Pausing and suspending the bad queue:
Is it possible that IOS pauses the main thread ("traps" it) because the worker is long running? - NO.
I think, your problem is related to drawing or changing some UI elements. Not all functions can be called from background thread (e.g. changes to UI elements has to be done in main thread.). In your serial queue, if any method needs to change UI elements, you have to call it on main thread e.g
dispatch_async(dispatch_get_main_queue(), ^{
//do some main thread job here
});
)
Maybe you just forgot to retain a variable into dispatch function call (As for me I was omitted a static keyword before dispatch_once_t declaration and dispatch can't process with inline function). The stack trace was just like yours. That was my fault.
+ (instancetype)sharedInstance
{
(static was omitted) dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Related
I have a really strange situation where I have multiple nested dispatch queues, and at some point I need to update the UI. DispatchQueue.main.async doesn't execute if I am already on the main thread, if I skip it I can see in the debugger (as well as using Thread.isMainThread) that I am on the main thread but inside one of the custom queues and the UI doesn't update.
How do I get to the “real” main thread (com.apple.main-thread)?
Or how can I ensure that a queue created via
private let progressQueue = DispatchQueue(label: "custom_thread", attributes: .concurrent)
is definitely a background thread?
You said:
DispatchQueue.main.async doesn't execute ...
If this dispatched code is not running, then the main queue must be blocked.
I see that Thread.isMainThread is true, but when updating the ui nothin happens
It does not matter that you currently are on the main thread or not, the code dispatched to main via async simply cannot run until the main queue is free again.
How do I get to the “real” main thread (com.apple.main-thread)? Or how can I ensure that a queue created via ... is definitely a background thread?
The dispatch queue that you are creating is a background queue. And GCD decides, at its discretion, on which thread dispatched code will run. Generally, code dispatched to a background queue will run on one of GCD’s worker threads of the appropriate QoS, but there are some interesting edge cases.
Specifically, if you dispatch synchronously (i.e., with sync) from the main thread to a GCD queue, as an optimization, GCD may actually run the dispatched code on the main thread. When we sync to a background queue, the main thread is blocked until the dispatched code finishes, anyway, so GCD may just use the idle main thread and avoid a costly context switch. That is why code dispatched synchronously to a background queue may report that isMainThread is still true. But, just to be clear, it is not the case that this is not the “real” main thread. It just is just code dispatched synchronously to some background queue that is actually running on the main thread (and, in your case, likely blocking the main thread in the process).
Bottom line, we do not care what thread this queue happens to be using. The fact that GCD may be using the main thread as an optimization is immaterial. Do not get distracted by isMainThread. We only care whether we have blocked the main thread anywhere.
So, you are going to have to identify where the main thread is getting blocked. Needless to say, we should never block the main thread. By avoiding the blocking of the main thread, we ensure that our UI updates can happen freely. It also provides a better user experience and ensures that the watchdog process will never kill our app.
Given your comments, the problem is likely that sync was called from the main thread. Consistent with the above observation, one simply should not call sync from the main thread. (The one exception is the very fast data synchronizations of memory accessed from multiple threads, but even that is a pattern to be avoided, if you can.)
That having been said, there are other potential blocking patterns that could cause this problem, including (a) semaphores or groups where wait called from the main thread; or (b) some spinning loop.
But in this case, sync is the likely culprit. If you find where you are initially calling sync from the main thread, and change that to adopt an asynchronous pattern, then calls to DispatchQueue.main.async will be free to update the UI.
Your assertion "DispatchQueue.main.async doesn't execute if I am already on the main thread" is not correct. When you dispatch a block of code that block is placed on a queue and executed in order, e.g. consider this code:
print ("hello #1 \(Thread.current.description) isMain=\(Thread.isMainThread)")
DispatchQueue.global().async {
print ("hello #2 \(Thread.current.description) isMain=\(Thread.isMainThread)")
DispatchQueue.main.async {
print ("hello #3 \(Thread.current.description) isMain=\(Thread.isMainThread)")
}
}
DispatchQueue.main.async {
print ("hello #4 \(Thread.current.description) isMain=\(Thread.isMainThread)")
}
let progressQueue = DispatchQueue(label:"custom_thread", attributes: .concurrent)
progressQueue.async {
print ("hello #5 \(Thread.current.description) isMain=\(Thread.isMainThread)")
}
results in console output:
hello #1 <NSThread: 0x2824c8380>{number = 1, name = main} isMain=true
hello #2 <NSThread: 0x2824f6740>{number = 3, name = (null)} isMain=false
hello #5 <NSThread: 0x2824f6740>{number = 3, name = (null)} isMain=false
hello #4 <NSThread: 0x2824c8380>{number = 1, name = main} isMain=true
hello #3 <NSThread: 0x2824c8380>{number = 1, name = main} isMain=true
Keep in mind that the background queue could execute at any time. The only certainty is that hello #1 will appear first, and that hello #2 will appear before hello #3.
You can also see that progressQueue is definitely a background thread, and appears to be the same as the global dispatch queue.
Hello iOS experts just to clear my concept I have a bit confusion about UI updation from Main Thread. Apple requirements are that all UI related stuff should be carried out in main Thread.So to test:
Case1: I dispatch a task to a global dispatch concurrent queue asynchronously . After some processing I update my UI stuff directly from the concurrent queue (background thread), working fine using the below code.
dispatch_queue_t myGlobalQueue;
myGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(myGlobalQueue, ^{
// Some processing
// Update UI;
});
Case2: Than i tried the Apple required way, dispatch a block to global dispatch concurrent queue asynchronously. After some processing I update the UI stuff in Main thread using below code:
dispatch_queue_t myGlobalQueue;
myGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(myGlobalQueue, ^{
// Some processing
dispatch_async(dispatch_get_main_queue(), ^{
// UI Updation
});
});
Now in both cases I am getting the same result. Now listen my questions
Questions: 1: In case1 are we still in Main Thread not in background thread ? If so than why Apple doc say:
Concurrent queues (also known as a type of global dispatch queue) execute one or more tasks concurrently, but tasks are still started in
the order in which they were added to the queue. The currently
executing tasks run on distinct threads that are managed by the
dispatch queue. The exact number of tasks executing at any given
point is variable and depends on system conditions.
Now if we are on Main Thread than this is a contradiction with the bold part of Apple Doc.
2: In Case1 if we are in background thread, than why Apple require to get Main Thread for UI Updation, Even though we can update UI from background Thread too?.
Kindly read my question fully and suggest me if I am doing something wrong. Your help and time would be greatly appreciated.
To 1)
This simply says, that tasks from the same queue can run on distinct threads. It does not say, that a task cannot run on a specific thread. (But I really do not expect to run a task on the main thread.)
To 2)
Apple does not say, that updating the UI from a different thread will fail in every case, but can fail. You shouldn't do it: One time it will fail.
You should read this:
https://en.wikipedia.org/wiki/Necessity_and_sufficiency
I am having UI frozen/blocked due to semaphore_wait_trap in main thread.
When the UI is frozen, I pause using XCODE and the last two lines in stacktrace:
0x103f0ea30 <+809>: callq 0x103f1554d ; _dispatch_thread_semaphore_wait
dispatch_sync(someQueue, block); // this is my code.
How can I find what is causing the block?
Any other suggestion to find out what is causing the block?
It always blocks on the same line/code.
In the Debug navigator (cmd-6), you should have a list of threads. One OTHER thread in there should be waiting for someQueue as well. I can't think off hand of a case where that wasn't the case. Usually the two threads are waiting for each other, (e.g. via dispatch_sync).
For example, you might have this:
dispatch_sync(mySerialQueue, ^{
[self foo];
});
and
- (void)foo
{
dispatch_sync(mySerialQueue, ^{
...
});
}
The latter will be waiting for the former to finish forever, because the former is holding onto myQueue until it finishes the call to -foo.
(Note that mySerialQueue has to be created with the DISPATCH_QUEUE_SERIAL for this to happen.)
I have an app that uses a connection queue that handles the connections on a background thread. Each connection sends a JSON post, then when it receives a success, saves some objects into coredata.
Once all connections are complete, i call a dispatch_async on the main thread to call a finished method.
However, under very specific conditions of data im sending/saving, I've noticed the dispatch_async block to the main thread never gets called, and the app screen freezes, all execution stops, and the app sits idle with a frozen screen. processing power according to xcode is 0%.
Here is method with the block that fails.
- (void)connectionDidComplete
{
_completeConnections++;
_syncProgress = (float)_completeConnections / (float)_totalConnections;
dispatch_async(mainQueue, ^(void) {
[[NSNotificationCenter defaultCenter] postNotificationName:SyncQueueDidUpdateNotification object:nil];
}); <-- this dispatch works
if (_completeConnections == _totalConnections)
{
// clear unsynced data
NSArray *syncedObjects = [SyncObject completedSyncObjects];
if (syncedObjects.count > 0)
{
for (SyncObject *syncObject in syncedObjects)
{
[syncObject delete];
}
}
//this method saves the current context, then merges this context with the main context right after
[[VS_CoreDataManager sharedManager] saveManagedObjectContextAndWait:managedObjectContext];
// cleanup the thread's context
[[VS_CoreDataManager sharedManager] unRegisterManagedObjectContextForThread:currentThread];
managedObjectContext = nil;
// complete sync
dispatch_async(mainQueue, ^(void) {
[self performSelector:#selector(finishSync) withObject:nil afterDelay:2];
}); <-- this dispatch never gets called
}
}
My suspicion is this problem has something to do with saving the context then merging it. And possibly while that is happening its released in the middle of the merge, causing some weird hang up and the dispatch isn't getting executed. This is just a guess though, and I don't know how to fix it.
Any ideas?
Thanks.
If the block on the main thread is not executed, then it is because of 1 of 2 reasons.
The main thread is blocked; is not processing any events at all. Got a while() loop on the main thread? That'd do it. A lock? There you go.
The main thread is running a modal run loop inside the outer run loop. Asynchronous dispatches to the main event loop -- main thread -- won't be processed in this case.
Set a breakpoint on that dispatch_async() and see what the main thread is doing (at the point of dispatch the main thread is most likely already in the bad state).
DarkDust's suggestion of using dispatch_after() is a good one, but is unlikely to work in that it is almost assuredly the case that your main thread is not processing events when the problem occurs. I.e. fix the problem, then move to dispatch_after() as DarkDust suggests.
If your main thread is busy with modal runloop, then you could try
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, block
});
I believe this is a great discussion. I came across this when I had the following code:
dispatch_synch(dispatch_get_main_queue()){
print("I am here")
}
the print code did not execute as I was dispatching a 'synch' block on the serial main thread which caused a dead lock. print was waiting for the dispatch to finish and dispatch was waiting for print to finish. When you dispatch in the main serial queue then you should use dispatch_async. and i guess if you use a concurrent queue then dispatch synch suits better
I am currently learning IOS Threading programming... I encountered an issue:
Here comes my code, please kindly have a look:
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSThread *t1 = [[NSThread alloc]initWithTarget:[MyThread class] selector:#selector(myMethod:) object:Nil];
[t1 start];
}
return 0;
}
#import "MyThread.h"
#implementation MyThread
+ (void)myMethod:(id)param
{
#autoreleasepool {
NSLog(#"called...");
}
}
#end
However, when I ran my program, though there was no error, no message was printed on the console. It seems like myMethod was not executed. I wonder if anyone could give me some suggestions. It has already driven me crazy.
Many thanks in advance.
The main thread of your application is exiting before your other thread has a chance to process anything.
It will work if you add in a simple sleep(1000) statement anywhere before the return 0 statement in your main method.
Your application is terminating before the thread has executed the NSLog.
NSThread creates a detached thread, see Apple's Thread Programming Guide, from which comes:
Important: At application exit time, detached threads can be terminated immediately but joinable threads cannot. Each joinable thread must be joined before the process is allowed to exit. Joinable threads may therefore be preferable in cases where the thread is doing critical work that should not be interrupted, such as saving data to disk.
To create a joinable thread, and hence be able to block your main thread until all joinable threads have finished, you use pthread - covered in the above Guide.
The Java thread model is similar, but uses slightly different terminology. By default a Java thread is joinable and the Java application will continue to execute until all such threads have terminated. A Java thread can be converted to a daemon thread, which is automatically terminated on application exit as with NSThread threads.