Understanding the Run loop idea and the updating of controls during long operations
Understanding the Run loop idea and the updating of controls during long operations
- Subject: Understanding the Run loop idea and the updating of controls during long operations
- From: eveningnick eveningnick <email@hidden>
- Date: Sun, 7 Nov 2010 04:19:11 +0200
Hello
I am trying to understand how does Run loop work.
Here is my story
(i am sorry, it is a little long, but to avoid questions "why do you need
that" and others i thought better to write it down
here). I also read the CFRunLoop documentation, and event processing guide
in Cocoa, but I couldn't put it all into one big picture.
Basically the problem i need to solve is like the following:
I have a Cocoa application, which is a little "adhoc" - for better
understanding of the "internals" of my app i do not use application
delegates, and i do not call NSApplicationMain:
NSApplication *app = [NSApplication sharedApplication];
...some manual init code that loads nib...
[NSApp run];
I have a button on my window, and a text label. My application installs an
event tap into another application (CGEventTap).
When a user clicks on the button, a "long action" begins executing - this is
something like a processing of a big file.
I want a user to see the percentage of the operation displayed on the label,
and i want when my application's runloop receives Keyboard tap event (ESC
key pressed) from another application to interrupt processing of that file.
I've been asking similar question before, and i was suggested to use an
NSOperation for that - I should delegate the processing code to NSOperation
instance, and add that instance to NSOperationQueue. Then, having called
something like "run", my process creates a new thread, while my current
thread continues to run its runloop - which allows my application to react
to these "Event Tap"'s Events and to display the results normally.
An another option (which is almost the same as previous) - is to instantiate
NSThread.
Here is what i did instead:
when a user clicks the button, i simply call the "long processing" routine,
and inside that routine i call methods to modify the label's status (to let
the user know that my application does something useful, instead of just
"hanging"), and i periodically (after processing next chunk of that Big
file) i am checking a "cancellation variable" (that is set in EventTap
callback-handler function when ESC keypress received). And it worked!! (Snow
Leopard). Worked till the moment i tried my application on OS X 10.5, where
no updates were displayed on the label and no "event tap"'s events were
received untill the contiguous processing of the file was finished.
One of the reasons of using this approach (at least on Snow Leopard, where
it worked great) was a wide use of Apple Script calls during that
processing. Trying to implement the idea with another thread greatly
decreased performance (it's like the second thread tried to self-synchronize
with the main one, to perform apple script calls). I dont really know what
happened and why was it happening. Unfortunately my understanding of this is
very superficial. I only heard that before NSAppleScript had some issues
with multithreading, and it was adviced to perform -executeAndReturnError
only in main (GUI) thread.
here are my questions:
o Why does label update and why is event tap callback function called on Mac
OS X 10.6, but nothing is updated or called on OS X 10.5 during the
executiong of a "big file processing"? I assume, that when i'm calling
[mylabel setTextTitleMnemonics:@"Hey, we are not hanging yet, the file is
still being processed (10% done!)"] the run loop is being run through all
the runloop sources (including EventTap RunLoopSource). But, again, why
doesnt it happen on Leopard?
I assume i should call a one run through the runloop manually, but how?
Here's what CoreFoundation reference suggest to do:
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false)
this is supposed to call only one run through the runloop. But, according to
the documentation "If multiple sources or timers are ready to fire
immediately, only one (possibly two if one is a version 0 source) will be
fired, regardless of the value of returnAfterSourceHandled.". I have more
than one source: it is a label update (well, GUI events, i am not sure how
to call it - something like redraw i guess), event tap source (that is
installed into another app), and i am also observing another application's
state changes (when it becomes active/showed/hidden/inactive). Therefore i
can't specify 0 is the second parameter? Then what number should i specify?
0.1? 0.5? 1? 10? how can i know how long it needs to be executed? And i dont
still want to slow down the processing waiting 1 second on every label
update. (i call these updates several times during processing the "big
file")
o Why isn't label updated immediately on OS X 10.5, when i call
-setTextWithMnemonics? Does it require passing the event loop to update?
o What is the relation between [NSApp run] and a core foundation runloop?
What is the relation between NSRunLoop and core foundation runloop? Is
NSRunLoop an another while loop (in terms of programming language) that
"includes" core foundation while-loop? Then manual calling
of CFRunLoopRunInMode is not wise - i should be calling some NSRunLoop's
method? Or, maybe, some NSApp's method? Or i can operate directly with Core
Foundation's runloop?
Currently i am imagining a runloop just like a message processing loop in
Windows application - it is something like this:
BOOL terminated = NO;
while(!terminated) {
//when there's an event on any of the sources, this function returns,
otherwise it blocks the application,
//without wasting CPU cycles (the OS is executing other scheduled thread
instead)
CGEventRef evt = GetAnEventFromTheSource(source1, source2, source3,
....);
if(evt.event == kEventRunLoopTerminated)
terminated = YES;
eventProcessingRoutine(evt);
}
But it is somehow counts the time given to execute a runloop, and if the
time is not enough, it just quits (maybe also some kind of asynchronous
events are used to interrupt the runloop? Or maybe some timer just sets the
terminated flag when the time is up?)?
Could you please clarify my understanding of what is happening under the
hood of event loop?
Thanks a lot for any response on any of my questions! If anything is unclear
in my description, i will try to explain it more conspicuous, just let me
know!
Thanks again,
George
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden