Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

 
Advanced search

1075929 Posts in 44152 Topics- by 36120 Members - Latest Member: Royalhandstudios

December 29, 2014, 03:58:51 PM
TIGSource ForumsDeveloperTechnical (Moderators: Glaiel-Gamer, ThemsAllTook)iOS Application Event Loop
Pages: [1]
Print
Author Topic: iOS Application Event Loop  (Read 3458 times)
technogothica
Level 1
*



View Profile WWW Email
« on: November 17, 2011, 06:27:37 PM »

Hi Guys,

I'm working on an iOS 5 app and I'm looking for a way to run some code as part of the main thread's event processing loop.

I come for a Windows background, and the usual procedure is to call PeekMessage() to determine if there are any event messages in the queue to be processed. If there are messages, they are dispatched to their appropriate windows. If there are no messages to process, I run my own "idle" function that does a bunch of non window system stuff such as triggering game updates and rendering.

Under the hood, I gather that iOS (and Mac OS X) works in the same way (I believe they are called "run loops"). But the main thread's run loop is wrapped up inside UIApplication (I think) and there doesn't appear to be a way to add any kind of delegate or callback to it.

The logical choices I have found so far are NSTimer or NSOperationQueue, but these are a bit of a kludge and bring performance issues of their own. Then again I may be over-thinking it.

Threads are not the answer to this particular problem. The idle processing I want to add is to poll the state of background threads synchronously with user interface updates. I want this to occur whenever the main thread has nothing else to do.

Thanks in advance.
Logged

ThemsAllTook
Moderator
Level 10
******


Alex Diener


View Profile WWW
« Reply #1 on: November 17, 2011, 06:44:51 PM »

NSTimer does what you want and isn't a kludge at all. CADisplayLink is probably what you want, though.
Logged
technogothica
Level 1
*



View Profile WWW Email
« Reply #2 on: November 17, 2011, 07:18:03 PM »

NSTimer does what you want and isn't a kludge at all. CADisplayLink is probably what you want, though.

Does an NSTimer with a small duration cause any problems like the event queue filling up with unprocessed messages? Or is it like the Windows WM_TIMER message in that it is only sent if the queue is empty?
Logged

eclectocrat
Level 5
*****


Most of your personality is unconscious.


View Profile
« Reply #3 on: November 17, 2011, 07:25:20 PM »

I think that CADisplayLink or whatever is the preferred method, but many devs prefer to implement there own loop. As of a few versions ago iOS had some problems with stuttering unless you took control of the timing.

From my own personal link collection:

http://www.iphonedevsdk.com/forum/iphone-sdk-game-development/42309-best-game-loop.html
http://www.iphonedevsdk.com/forum/iphone-sdk-game-development/31530-gaining-performance-coregraphics-uiviews.html
Logged

I make Mysterious Castle, a Tactics-Roguelike
ThemsAllTook
Moderator
Level 10
******


Alex Diener


View Profile WWW
« Reply #4 on: November 17, 2011, 08:08:33 PM »

Does an NSTimer with a small duration cause any problems like the event queue filling up with unprocessed messages?

Nope, NSTimer won't try to call your callback more often than it's able to. It's very good about keeping up, though; ask for a timer that fires at 60hz, and it will in fact fire at 60hz most of the time.
Logged
justinfic
Level 1
*


justinfic
View Profile WWW
« Reply #5 on: November 21, 2011, 12:35:25 PM »

Seconding the use of NSTimer-- I've used it in tons of commercial games and it's never given me a problem. You can actually jigger the frequency around to get the best result as well. 60hz worked great for me for most things, and for more demanding games I upped it to  100-120hz and saw a slight boost in performance. Couldn't tell you why though.

I've also tried the "proper" approach with threading and it was a nightmare. No noticeably performance increase over NSTimer and it blows open a whole new can of worms. Not worth it.

Logged

Twitter | Current Project: Heavy drinking
Average Software
Level 10
*****

Fleeing all W'rkncacnter


View Profile WWW Email
« Reply #6 on: November 21, 2011, 12:47:50 PM »

I use an NSTimer with a duration of 0.0, which effectively amounts to an idle loop.  Has worked perfectly for me for years.
Logged

Franchise - The restaurant wars begin!

What would John Carmack do?
mcc
Level 10
*****


glitch


View Profile WWW Email
« Reply #7 on: November 21, 2011, 01:23:58 PM »

Does an NSTimer with a small duration cause any problems like the event queue filling up with unprocessed messages?

Nope, NSTimer won't try to call your callback more often than it's able to. It's very good about keeping up, though; ask for a timer that fires at 60hz, and it will in fact fire at 60hz most of the time.
If you call an NSTimer with a RIDICULOUSLY high rate, like thousands of fires per second, you will eventually reach a point where the event queue has so many timer fire messages in it that desirable messages (usually "touch end") are dropped. However, this is a rare event and not a killer. (As long as you have not written your game to assume that all touches which begin eventually end. DON'T EVER ASSUME THIS.)

Average Software recommends an NSTimer with a duration of 0.0. According to the documentation, the interval of a timer is defined as: "The number of seconds between firings of the timer. If seconds is less than or equal to 0.0, this method chooses the nonnegative value of 0.1 milliseconds instead." However I would say that "fire this every 0.1 seconds" is a good proxy for "run whenever idle".

CADisplayLink is what you want if you want to just say "wake me up when it is time to display a frame". I do not think it is the correct solution to the specific problem gothica suggests?

I think there's some way to actually tell the cocoa event loop "call me whenever you're not doing anything else". I find this suggestion of how to do this using GCD: http://stackoverflow.com/questions/7356820/specify-to-call-someting-when-main-thread-is-idle but do not know if this truly works.

However: Technogothica, I do not think you are going about this in the right way to begin with. You usually do not need to write a "run when idle" handler yourself; rather what you really want is to engineer your program such that the Apple-provided run loop is doing the things you wanted your 'run when idle' loop to do, for you. This should always be possible. (Among other things, if you are truly running "whenever idle" then you are going to be 100%ing the CPU and people with laptops with loud fans will hate you.)

In particular, you say "The idle processing I want to add is to poll the state of background threads synchronously with user interface updates". What I would suggest is that instead of polling, when the background thread must communicate something to the main thread, it "push" by calling performSelector:onThread:withObject:waitUntilDone: (there is also a performSelectorOnMainThread).
http://cocoawithlove.com/2009/08/safe-threaded-design-and-inter-thread.html
This will cause the requested method to be invoked on the desired thread on the next visit to that thread's "run loop".

Aside from this, what I would suggest is that you research GCD. It's quick to learn and allows you to perform all kinds of elaborate and really neat tricks. GCD is like NSOperation/NSOperationQueue but more cleanly designed. It's also what the "run loop" in newer versions of OS X / iOS is based on, which means if you're using GCD you're effectively being given access to the primitives of the os x/ios run loop, and it is expressive enough I think it mops up the use cases where performSelector:onThread: isn't so hot. Using this you could do something like signal from a secondary thread like "schedule this function to run on the main thread's event loop, but only do so with the lowest possible priority". You can also use this to perform performSelector:onThread:-like messaging from non-objective-c code, and receive these sorts of inter-thread "messages" on threads which do not actually have a cocoa run loop.
« Last Edit: November 21, 2011, 01:55:19 PM by mcc » Logged

My projects:<br />Games: Jumpman Retro-futuristic platforming iJumpman iPhone version Drumcircle PC+smartphone music toy<br />More: RUN HELLO
technogothica
Level 1
*



View Profile WWW Email
« Reply #8 on: November 21, 2011, 03:16:41 PM »

Thanks mcc and others for your help.

I did a couple of tests and CADisplayLink proved to be the best solution for what I needed.

NSTimer turned out to be terrible for performance. CADisplayLink appears to be, more or less, implemented in the way I was describing. At the very least, I am using it for its intended purpose.

I am familiar with NSOperationQueue and GCD, and have been avoiding them specifically because of their design.

The background thread is processing a stream of video data coming from a network source. Video frames are being exchanged with the main thread through a double-buffer set up: background thread writes to a back buffer, main thread reads from front buffer. Whenever a new frame comes in, the background thread does the following:

1. Decode incoming frame into back buffer
2. Lock mutex
3. Swap front and back buffer pointers
4. Set flag signaling new frame has arrived
5. Unlock mutex

The main thread does the following in response to CADisplayLink:

1. Lock mutex
2. If flag is set:
    a) Grab frame from front buffer (glCopyTexSubImage2D)
    b) Clear flag
    c) Signal GUI to redraw itself when appropriate
3. Unlock mutex

It is perfectly acceptable for the main thread to skip frames.

If the background thread were to dispatch an NSOperation every time a new thread arrived, the operation queue could potentially overflow with redundant events. The continual allocation, processing and deallocation of NSOperation objects, many of which may be discarded anyway, would be an unnecessary hit on performance, memory and battery. I doubt GCD would be any better, though it's worth a test if I have time.
Logged

mcc
Level 10
*****


glitch


View Profile WWW Email
« Reply #9 on: November 21, 2011, 06:19:52 PM »

Technogothica: So, based on your description, I agree you have chosen the correct architecture.

Simply for the record:

If the background thread were to dispatch an NSOperation every time a new thread arrived, the operation queue could potentially overflow with redundant events. The continual allocation, processing and deallocation of NSOperation objects, many of which may be discarded anyway, would be an unnecessary hit on performance, memory and battery. I doubt GCD would be any better, though it's worth a test if I have time.

I am very certain that GCD will be more efficient than NSOperation, and what bookkeeping it does will be far more lightweight than Objective C objects (although GCD can have hidden costs, at least in objc mode, for example if you create a block in objc mode there will be retain calls). I believe Apple has made claims GCD is more efficient than a mutex. I will note that if you have something that at maximum will be happening 120 times a second (unless something very surprising is happening I doubt you will be streaming video at more than 120fps... and if you are streaming >60fps video to a phone that can't refresh faster than that maybe that is something to rethink also) I doubt you are looking for performance in the right place if you are worried about the performance cost of creating 120 objective c objects in a second. But my understanding is GCD should be appropriate even in inner loops so tight that objc_msgsend starts to be considered a burden...

As for the specific issue of "but I don't want to flood the queue": I think GCD might have some primitives for handling this situation already. For example I know there's some kind of concept of placing messages in a "group"; I'd be curious if this or a mechanism like it could be used potentially to coalesce messages such that only one "new frame!" type message is in the queue at a time. However, I have no interest in looking things up far enough to determine if this is true or not since I agree you've already got the right approach and GCD is not particularly relevant to what you're doing right now... Tongue

But, if this were a situation I personally were looking at (notification of event needs to be passed to main thread; event may occur many times before main thread gets around to servicing its messages; do not want to flood queue with redundant messages) the way I would likely handle it is to simply have one message, meaning something like "wake up and poll all the worker threads". The worker threads could then very well themselves efficiently negotiate the question of "is there more than one 'please wake up and poll us' notice dispatched to the main thread currently?". You could probably just have a single "worker-threads-need-attention message inflight" flag, set on when the first message is sent to the main thread and turned off when the main thread receives it, and I bet if one were just a little clever setting that flag on and off could probably be done purely with atomic TAS.
« Last Edit: November 21, 2011, 06:29:17 PM by mcc » Logged

My projects:<br />Games: Jumpman Retro-futuristic platforming iJumpman iPhone version Drumcircle PC+smartphone music toy<br />More: RUN HELLO
technogothica
Level 1
*



View Profile WWW Email
« Reply #10 on: November 21, 2011, 07:34:39 PM »

Thanks mcc Smiley

I doubt you are looking for performance in the right place if you are worried about the performance cost of creating 120 objective c objects in a second.

I'm the sort of programmer that prefers to allocate once and reuse where possible.

I started programming back when 640K was enough for anybody and CPU speed was measured in megahertz. I'm still obsessed with performance to this day Grin
Logged

Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic