Runloop not processing events from dispatch_async - objective-c

I'm having some issues using dispatch_async. On my applications main/UI thread, I call dispatch_async on the global queue, and tell it to go do some function call which has a completion handler. I'm expecting the completion handler to get called but it does not appear to sometimes.
dispatch_queue_t hiq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(hiq, ^(void){
[object doSomethingAndThenCompletionHandler:^(){
//Do some stuff because I'm done
}];
});
What is interesting is, I'm doing this in response to a controlTextDidChange whenever I type something into a textfield. When I click out of the textfield, all of the completion handlers fire at once.
My guess is that all of my completion handlers are getting queued up behind something on the run loop and then when my UI element loses focus, that frees up the queue. I'm not enough of an expert on run loops to know what exactly is happening as I'm typing in a textfield or combobox but perhaps it's tying up the runloop?
EDIT: I think it has to do with the run loop mode because when I print out the run loop mode it prints out as NULL except for in the completion handler when it prints out as the default mode. I'm using a combo box and the issue is only present when the combobox is open and expanded. UIScrollView blocks run loop? I think it might have to do with this issue. What do you guys think?

When you do [[NSRunLoop currentRunLoop] runUntilDate:fiveSecondsFromNow], it enters the run loop recursively, and the run loop processes events normally. So it will call your method again if the text field receives another event. The stack trace would look something like this (with lots more frames related to NSRunLoop):
main
NSApplicationMain
-[NSRunLoop runUntilDate:]
your method
-[NSRunLoop runUntilDate:]
your method
I have no idea why you're calling runUntilDate:, but it's probably not for a good reason.
If you want help understanding why your completion handler isn't getting called, then you need to show us the definition of your doSomethingAndThenCompletionHandler: method.

Figured out the issue I think. Some of the libraries I'm using are most likely sending NSURLConnections out without scheduling to run in common run loop modes. When the combo box is open the run loop mode changes to event tracking mode and then can't get call backs for them.

Related

Cannot update User Control Label Text from parent event until the event method reaches return statement

The main form of my application has an event that gets triggered upon completion of a certain task. The event completion launches a background thread and the called method in that thread runs a loop which takes a while to complete. This loop updates (or is supposed to update) the user on what's going on during its run by editing the label->Text property of the UserControl along with a percentage of completion in a different label in the same UserControl. There are some more labels in this UserControl that are updated, but are not of the concern for this question.
Here is how the methods and statements are called.
Event Completed -> Background Thread
Background Thread -> Process()
Process() -> for loop()
for loop() -> UserControl->SetMessage(System::String^ msg) and UserControl->SetPercentage(int Percentage)
(1) UserControl->SetMessage(System::String^ msg) -> stores msg in a private UserControl member and executes this->Invoke->SetMessage_sub()
(2) UserControl->SetPercentage(int prcnt) -> stores prcnt in a private UserControl member and executes this->Invoke->SetPercentage_sub()
(1) SetMessage_sub() -> label1->Text = _msg;
(2) SetPercentage() -> label1->Text = _prcnt;
However, I found that the labels were not getting updated for as long as the loop ran. The labels only updated once the loop finished its run inside Process().
I used the System::Console::WriteLine method to figure out where the problem occurs. I found out that all the methods were being called. But the execution would stop at where the program would reach label->Text property. Please help me out.
I have run into a similar problem recently and found that the GUI update calls must be made from either a thread running in the background or a background worker. Try calling the method that updates your GUI from a background thread or a background worker.
Edit 1 - it sounds like you've already tried a background thread. Try using a BackgroundWorker object to accomplish the task.
Edit 2 - upon re-reading your question, it occurred to me that the problem you are facing is the GUI not loading until the event method has returned. I encountered the same problem and asked my question here. To my surprise, all I got was downvotes and no answers. A comment tried to refer me to using BGWorker class to update GUI but none of them read my question carefully. I know background threads and background workers. I have tried both but the GUI does not update until return is called from the event method. In my case, after calling the BackgroundWorker to do the processing, I ran an empty while loop in the event method and called break when the BGWorker finished. But the GUI never updated until break was executed. So, my solution was to create a separate event for the processing and processing completion. The completion of the initial event calls the processing method and then processing method's completion calls the processionf completion event method.

How to view active .Net events while stepping through code?

How do I track what events are currently 'Active' or currently being handled while stepping through the code.
The Issue
While I was debugging code (stepping through it) within a rather large application, all of a sudden I found that the code that was being run was running through code in functions that had nothing to do with the code I was troubleshooting. It took me a considerable amount of time to figure out why I found myself stepping through code in functions that were way outside the code I was debugging.
Turns out, at the start of the application, handlers are added to certain controls as well as timer controls. The timer control triggers an event every 5 minutes or so. There were other events being triggered when certain actions were taken, however I had no idea what was causing the debugger to enter into certain functions due to the fact that there was no indication or 'prompt' telling me an Event was Triggered and that was why I was now stepping into other functions.
How do I become aware of what is happening when events are triggered in the manner I mentioned above?
Note
This is not a question about how to add handlers or remove handlers in code.
So basically timers are still running in the same thread as the main logic. every time a function/Event is done and the Window steps into the waiting part in which unser input is read and events are started, timers will trigger. they will not run while other events are busy. If you use Background Worker or start second threads. they will run in between you current steps and the visual studio will jump from one to another. there is a thread window to keep track of all active threads but this only gives a clue. sometimes its still annoying to jump from one to another function. My advice is to use a debugger-hidden attribute on those functions that keep on bothering you.
How to find the Thread window:
How can I view Threads window in Visual studio?
How to use Debugger Hidden:
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.debuggerhiddenattribute?redirectedfrom=MSDN&view=netframework-4.7.2
and
https://social.msdn.microsoft.com/Forums/vstudio/en-US/cf20736c-cbfb-4919-b495-ea9a9235f9e5/debuggerhiddenattribute-example?forum=csharpgeneral

how to kill the stop the background selector

hey I have a task in which I need to perform a selector in background thread in which I need to do some video encodeing. The task goes fine. But if I press the back back button then I want that the method should be stop/kill. But it does not stop.
I searched a lot but does not found a satisfactory answere. I tried "cancellAllPreviousRequests" and also looked for NSQueueOperation but could not found how to use it to stop the background selector. Can anybody help me with relevent code.
Thank you in advance.
You need a concurrent NSOperation which periodically checks if it has been cancelled, via its isCancelled property. You can then call the cancel method of the NSOperation or the cancelAllOperations of the NSOperationsQueue to stop the background task. Keep in mind, calling cancel does not actually stop the operation, but rather sets a boolean property that the operation should be periodically checking to see if it should kill itself (stop doing what it is doing). How often you check for the isCancelled flag inside your operation code is up to you.
For further reading see here and here.

How to update a NSTextField Value during an action?

I am running a lengthly task in an Action and I would like to have a display of where I am at. For that I created a Text Field and I tried it with setStringValue:
[textField setStingValue: [NSSting stringWithFormat:#"%ld",currentValue]]
The code works but unfortunately it is not updating the NSTextField after every iteration but rather when the whole Action is done.
What am I doing wrong?
This is because applications with the Cocoa framework use an event loop to perform operations, and events occur in a completely serial fashion.
An event is basically any kind of action that the framework designer could not predict or found convenient to have run in a delayed manner. Since you can't predict when clicks will be performed, they need to be considered events; and for efficiency reasons (since you don't want to repaint a component multiple times if you don't need to), the repaint actions are events too.
Your action runs in response to a user event (for instance, a click on a button is an event) and therefore blocks all other events waiting in the queue until it's complete. However, components are repainted in response to a different, framework-triggered event, and as such the text field must wait until your action completes to repaint itself. This is why you cannot visually change the value of a text field from inside an action.
In order to notify your user of the progress of your task, you'll need to run it on a different thread. There's a lot to say about threads, so you should probably read some about them. I'm also sure that there are plenty of examples of how to run a long action in a background thread and update the UI accordingly for Cocoa all over the Internet.
When you click on a UI component, and it enters the Action block, the code is running on the main thread, the same thread that is painting the UI. If you run a long running operation in that block, it isn't going to paint until you are done because it is busy doing whatever you have it doing - you have hijacked the paint thread.
As said elsewhere, you need to spawn another thread, and then have the new thread perform the long running operation, and occasionally send messages to have the UI be updated by the main thread.
As a next step, go read the Apple documentation on NSThread, specifically:
+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument
Be aware that threading is a non-trivial domain area, and be ready for some wierd behavior if you aren't careful.

Form not refreshing after ShowDialog (Compact Framework)

I'm having a weird issue with form painting in the Compact Framework. I have a login dialog that is basically a small form that is opened on top of another using ShowDialog. When a card is swiped, the login dialog is supposed to close, then some login tasks are performed and then the form behind it should be activated. The problem is that the form behind the login dialog is not being refreshed and so the login dialog will not be removed until after the form behind is refreshed by some user action. This is probably due to the heavy processing that goes on in the login tasks part, but I've not found a way to solve this.
Basically, I want a way to force the application to close the dialog and paint everything again, before performing the heavy login tasks. I've tried numerous refresh methods without any luck:
Form loginDialog = new Form();
DialogResult result = loginDialog.ShowDialog();
loginDialog.Dispose();
//I've tried everything at this point to get the form to refresh before performing
//login tasks
this.Refresh();
this.Invalidate();
Application.DoEvents();
PerformHeavyLoginTasks();
Does anyone know what could be going wrong? Thanks
Ok I figured this out. The problem was with a custom control on the background form that manually paints itself using rectangles and such. I think this is a compact framework bug since I called Refresh and Invalidate on that control as well and it should've repainted. I had to create a method that would call the control's OnPaint override directly since Invalidate and Refreshed were pretty much ignored.
The issue, I believe, is that you're not fully understanding what's going on system-wise here.
When your fore window (the Dialog) is dismissed, the background window (the Form) is given focu and tol to repaint the clipping region where the dialog was. This happens via a PostMessage call, which sends a Windows Message that has to be popped, translated and dispatched down in the bowels of the Application.Run call.
This is, by design, a fairly slow process as the UI should not be preempting things that are important.
If you are doing heavy processing immediately after that PostMessage happens, the processing of those windows messages can often be slowed, ending up with the UI appearing "locked" or drawing really slowly. This is exacerbated if the processing you're doing is on the same thread as the UI.
Why are your efforst not making things better?
Calling Refresh simply sends another message. That message now gets in line for processing, so it would actually make things worse.
Calling Invalidate does pretty much the same this as Refresh, just asynchronously. Again, it makes things worse.
DoEvents tells the message pump to pop, translate and dispatch a message. That dispatch still has to be processed on the UI thread, so noting is going to happen until the thread has time to do the work (i.e. after your processing)
So how do we "fix" this?
The first step is often to put the processing on a separate thread to allow the scheduler to round-robin tasks between the UI and processing threads, up to the default quantum. Thgis means that the processing can only starve the UI for a maximum of 100ms before some sort of drawing is allowed to occur (assuming equal thread priority).
new Thread(PerformHeavyLoginTasks)
{
IsBackground = true
}.Start();
You can go a step further and give the UI a "jump start" on the processing (of 10ms in this example):
new Thread(new ThreadStart(delegate
{
Thread.Sleep(10);
PerformHeavyLoginTasks();
}))
{
IsBackground = true
}.Start();
Of course this may mean you need to now handle the next "display" asynchrously if the UI you want to display is dependent on the processing result. There are plenty of online resources for async patterns, so I won't beat that dead horse here.