Re: Understanding the Run loop idea and the updating of controls during long operations
Re: Understanding the Run loop idea and the updating of controls during long operations
- Subject: Re: Understanding the Run loop idea and the updating of controls during long operations
- From: Ken Thomases <email@hidden>
- Date: Sat, 6 Nov 2010 22:18:09 -0500
On Nov 6, 2010, at 9:19 PM, eveningnick eveningnick wrote:
> I am trying to understand how does Run loop work.
> 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];
If you don't have a handle on the basics, it's foolhardy to get into advanced techniques.
> 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.
> 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).
You got unlucky. You may think it was lucky, but it was unlucky because it has lead you to expect or believe that what you're doing is acceptable or workable.
> 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.
Here is where you got lucky.
Suppose it had also worked on Leopard. You may have shipped your app like that. Then, it may have broken on Lion. Or even version 10.6.5 of Snow Leopard. Or Tuesdays. Or whenever.
> One of the reasons of using this approach (at least on Snow Leopard, where
> it worked great)
It didn't work great, it worked randomly.
> 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"?
Who knows. It's an implementation detail and Apple is free to change implementation details whenever and however they like.
If a behavior of the frameworks is not documented, then it's not part of the design contract, and you can't rely on it. Testing the behavior of the frameworks empirically is doomed to mislead you.
> 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).
Your assumption is almost certainly mistaken, as are all such assumptions.
> But, again, why
> doesnt it happen on Leopard?
Because it's not supposed to. Or because it doesn't happen to, for whatever reasons. Apple may have given it no thought whatsoever, and what you're observing is an unintended side effect of how they implemented that method on each version of the OS.
> I assume i should call a one run through the runloop manually, but how?
I recommend that you not attempt to "invert" the event loop or build one yourself. The "modal session" methods of NSApplication (-beginModalSessionForWindow:, -runModalSession:, and -endModalSession:) seem like a good fit for your requirements. It does entail implementing your long operation as a bunch of short work units.
> 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")
All of these questions indicate that 1) you're deep in the weeds and over your head, and 2) you're caring about irrelevant minutiae of implementation details, exactly what you should avoid paying any attention to.
> 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?
First, you're going to have to explain what -setTextWithMnemonics: is. I don't find any method by that name in Apple's docs.
Typically, when you set a control's value, it merely marks itself as needing display. It does not display immediately. Then, the framework takes care of displaying all views and windows needing display at an appropriate time (where the framework defines what's appropriate).
> 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?
More questions you shouldn't care about. -[NSApplication run] is built on NSRunLoop, which is built on CFRunLoop. Beyond that, you are not entitled to know. If you did know, you'd rely on it, which would constrain Apple's future flexibility in changing the frameworks.
> 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?
You can use either API, but I don't see much reason to use the Core Foundation API from within Cocoa-based code. In particular, the function you're talking about, CFRunLoopRunInMode(), doesn't have any capabilities that aren't also available from NSRunLoop methods.
> Currently i am imagining a runloop just like a message processing loop in
> Windows application - it is something like this:
[snip]
> 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?
I hesitate to do so, because it will only encourage bad habits on your part. Read the docs (as you say you have) and regard anything not described within them as subject to change at any time. If you build a fixed mental model of how you think it works, it will only mislead you and get you into trouble.
That said, a run loop is more akin to WaitForMultipleObjects() or, from the *nix world, select(), poll(), kqueue() or the like. However, those APIs are just means to be notified when something is "ready", more or less. A run loop source also incorporates a response to the source being ready, in terms of its callback or delegate message.
(Note that those other APIs all incorporate a timeout mechanism. It's inherent to the implementation. You can't easily reproduce this in user code like you wrote because it requires support from the kernel.)
Regards,
Ken
_______________________________________________
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