I have a snippet of code I want to execute repeatedly, but with the ability to pause and resume it. To do this, I have utilised an NSTimer, which I can stop and start as required.
Within the snippet, I use a sleep command to wait for something to update (0.3 seconds). The timer is firing every 0.5 seconds.
What would be ideal is to keep the stop and start functionality, and be firing every 0.3 seconds, but to not have to explicitly say I want it to fire every x seconds. The 0.5 is completely arbitrary, and is just set to be > 0.3.
If I set the timer to fire every 0.01 seconds, but keep the sleep command within the code fired to 0.3 seconds, will I get the desired behaviour? Or will the timer back up all the events, with unexpected results (for example multiple firings after I have stopped it)? This way I could make the 0.3 sec sleep a variable, and not have to change the timer whenever I increase it over 0.5.
Or is there a better way to get this functionality?
One of the biggest problems with this I see is that NSTimer will be consuming time on whatever thread it's registered with.
In most cases for me this is my main thread, sleeping inside of your main thread would be a bad thing as far as I can see.
A few alternative designs that may be better for you.
Don't sleep. Have the selector called by the timer do a 'am I paused' check and exit early if it is.
Invalidate the NSTimer when pausing and recreate it when unpausing (Note: you can't just reschedule the old timer).
Spawn a background thread with it's own RunLoop or even your own while loop that just handles the rescheduling with sleeping.
Those are also roughly in the order I'd do them, as No. 1 seems the cleanest to me, followed by No.2. No.3 while viable could introduce a lot of potential nastiness (threading issues, clean shutdowns, etc)
Related
I am creating an application for Mac, in Objective C, which will run in the Menu-bar and do periodic Desktop operations (such as changing the wallpaper). I am creating the application so that it stays in the Menu bar at all times, allowing easy access to configuration options and other information. My main concern is how to schedule my app to run every X minutes to do the desktop operations.
The most common solution I have seen is using NSTimer, however, I am concerned that it will not be memory efficient (after reading the following page on Apple Developer docs. Using an NSTimer will prevent the laptop from going to sleep, and will need an always-running thread to check for when the NSTimer has elapsed. Is there a more memory-efficient way of using NSTimer to schedule these operations?
Alternately, is there a way to use LaunchD to initiate a call to my application (which is in the Menu bar) so that it can handle the event and do the desktop operations. I think that the second way is better, but am not sure if it is possible.
First, excellent instincts on keeping this low-impact. But you're probably over-worried in this particular case.
When they say "waking the system from an idle state" they don't mean system-level "sleep" where the screen goes black. They mean idle state. The CPU can take little mini-naps for fractions of a second when there isn't work that immediately needs to be done. This can dramatically reduce power requirements, even while the system is technically "awake."
The problem with having lots of timers flying around isn't so much their frequencies as their tolerances. Say one you have 10 timers with a 1 second frequency, but they're offset from each other by 100ms (just by chance of what time it was when they happened to start). That means the longest possible "gap" is 100ms. But if they were configured at 1 second with a 0.9 second tolerance (i.e. between 1s and 1.9s), then the system could schedule them all together, do a bunch of work, and spend most of the second idle. That's much better for power.
To be a good timer citizen, you should first set your timer at the interval you really want to do work. If it is common for your timer to fire, but all you do is check some condition and reschedule the timer, then you're wasting power. (Sounds like you already have this in hand.) And the second thing you should do is set a reasonable tolerance. The default is 0 which is a very small tolerance (it's not actually "0 tolerance," but it's very small compared to a minutes). For your kind of problem, I'd probably use a tolerance of at least 1s.
I highly recommend the Energy Best Practices talk from WWDC 2013. You may also be interested in the later Writing Energy Efficient Code sessions from 2014 and Achieving All-day Battery Life from 2015.
It is possible of course to do with with launchd, but it adds a lot of complexity, especially on installation. I don't recommend it for the problem you're describing.
Is there any sort of "sleep" method that is more accurate than the stopwatch? Or, is there a way to make the stopwatch class more accurate? It doesn't have to be in .NET, it can be in c++, but whatever language it is in has to have exactly 1ms accuracy; I don't need more then that. Say if I want my program to "sleep" for 300ms, I would like it to sleep for 300ms at least most of the time.
Currently I use:
Dim StopWatch As New StopWatch
StopWatch.Start
Do
Loop Until StopWatch.ELapsed.Milliseconds >= 300
StopWatch.Stop
My results running it 5 times were: 306, 305, 315, 327, 304.
It stayed like that if I ran it more.
I put my thread and process priority on "Realtime" / "High".
The Stopwatch class has a property IsHighResolution. If it returns ´true´ you are using the High Performance Event Timer (HPET) - availability depends on hardware and OS. Using this, you can measure times very accurate. BUT! Windows (as usual Linuxes) is NOT a realtime OS, but uses preemptive multitasking. Whenever the OS thinks, that it needs to, it will put your current thread on hold to do other work and after some time, it will return to your thread and let it continue. If this switch happens somewhere inside your loop, you still measure the correct time, but it contains an amount of inactivty time.
Since a time slice under Windows is something between 15 and 30 ms, you(r thread) might be suspended after 299 ms and 15-30 ms later you will get back. And that's the effect you see. The Stopwatch IS accurate. It just measures stuff you didn't expect.
How to overcome: You can't. As said: Windoes IS NOT a realtime OS! Even if you assign priority "realtime" to your process.
What you are seeing is completely normal. Delay will never be exactly 300ms, it will always be more than that. Sleep itself is accurate, but the actual delay depends on your operating system, and other processes running in parallel to yours.
If you want a more accurate timer, you need to use the current date and time as a reference. Here is a simple equation that you can run every millisecond:
currentTime - startTime = elapsedTime
...where currentTime is System.DateTime.Now, startTime is the time that the timer was started, and elapsedTime is a System.DateTime.TimeSpan.
For more details on how to do this, check out the source of a program I made in VB.Net, E-Tech Timer: http://etechtimer.codeplex.com
I am currently working on an app that requires a sound file to be played at exact intervals, which are variable in duration.
I seem to remember being told that NSTimer simply places an operation onto the stack after the specified duration, rather than the operation being run after the specified duration. This would mean that if there were lots of other operations on the stack before it, it would not be called on time.
I would like to know if this is indeed correct, and if so, is there any way of getting an operation to be guaranteed to run after a specified duration.
Before any makes the comment that the sound files may be delayed the first time, they are already pre-loaded to avoid this.
In the first paragraph of Apple's NSTimer documentation you'll find this:
A timer is not a real-time mechanism; it fires only when one of the
run loop modes to which the timer has been added is running and able
to check if the timer’s firing time has passed. Because of the various
input sources a typical run loop manages, the effective resolution of
the time interval for a timer is limited to on the order of 50-100
milliseconds. If a timer’s firing time occurs during a long callout or
while the run loop is in a mode that is not monitoring the timer, the
timer does not fire until the next time the run loop checks the timer.
Therefore, the actual time at which the timer fires potentially can be
a significant period of time after the scheduled firing time.
So you will definitely run (or play your sound, in your case) after your scheduled firing time, but not necessarily exactly at your time.
I'd like to animate some loading points while the app is doing some computation in the background. I achieve this via an NSTimer:
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.3f
target:self
selector:#selector(updateLoadingPoints:)
userInfo:nil
repeats:YES];
Unfortunately, sometimes, when the computation becomes pretty heavy, the method is not fired and the updating therefore doesn't happen. It seems like all the firing is in a queue which is fired after the heavy computation.
Is there a way to give the NSTimer a higher priority to ensure that it's regularly calling my method? Or is there another way to achieve this?
NSTimer works by adding events to the queue on the main run loop; it's the same event queue used for touch events and I/O data received events and so on. The time interval you set isn't a precise schedule; basically on each pass through the run loop, the timers are checked to see if any are due to be fired.
Because of the way they are implemented, there is no way to increase the priority of a timer.
It sounds like your secondary thread is taking a lot of CPU time away from the main thread, and so the timers don't fire as often as you would like. In other words, the main thread is starved for CPU time.
Calling performSelectorOnMainThread: won't necessarily help, because these methods essentially add a single-fire timer to the main thread's event queue. So you'll just be setting up timers in a different way.
To fix your problem, I would suggest that you increase the relative priority of the main thread by decreasing the priority of your computation thread. (See [NSThread setThreadPriority:].)
It may seem counter-intuitive to have your important worker thread running at a lower priority than the main thread, which is just drawing stuff to the screen, but in a human-friendly application, keeping the screen up to date and responding to user input usually is the most important thing that the app should be doing.
In practice, the main thread needs very little CPU, so it won't really be slowing your worker thread down; rather, you are just ensuring that for the small amount of time that the main thread needs to do something, it gets done quickly.
The timer is added to the run loop it's been scheduled with. If you create the timer on a secondary thread (e.g. your worker thread), there's a good chance you also scheduled it on the secondary thread.
You want the UI updates on the main thread. Thus, you want the timer scheduled on the main thread. If your updates are still slow, perhaps your main thread can do less work, and ensure that you have very low number of threads, and that you are locking appropriately.
I suspect you created it on a secondary thread which did not run the run loop as often as the timer wanted to fire. If it is doing a lot of (prolonged) work in the background, and not running the run loop, then the timer would not have a chance to fire because the messages would not have the chance to be fired while its thread is still out processing.
Make your timer call from a separate thread rather than from main thread. this will certainly keep it separate from your other main thread's processing which will give you desired results.
Perform your computation on a separate thread, using performSelectorInBackground:withObject. Always do as little as possible in your UI loop, as any work done here will prevent mouseClicks, cause SPoDs/beachballs, and delay timer handlers.
I suspect that it's not just your TIMER being unresponsive, but the whole UI in general.
Sorry for having called out the wrong API in my earlier revision - copy/paste failure on my part.
Is the best practice of setting an alarm on OS X to create a NSTimer scheduled for the number of seconds between the current time and the desired time for the alarm, or is there an alternative to that method?
That is the best practice, yes. Timers don't poll and don't otherwise consume resources until fired.
Timers aren't particularly accurate in the fine grain, but should be "good enough" at anything in the 100s of milliseconds or greater range.
One thing to consider, though, is that the system may go to sleep and this can interfere with your timer. If you need to prevent sleep, consider carefully the impact on battery life and read up on Power Management.