Sequence operations - ios

I have a little card playing app. When the computer is playing, some functions are being fired after some time to mimic a real player, like so:
self.operation.addOperation {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+2) {
self.passTurn()
}
}
Everything works fine, but when I want to cancel the game and start a new one, the app crashes if I do it within two seconds before the self.passTurn() function fires.
Ideally I would like to have some sort of pause between the different operations. The above mentioned operation gets released from the queue immediately after it fires the delayed function, so cancelling or suspends the operation does not work.
Is it possible to somehow retain the operation in the queue for two seconds and release it afterwards when the actual function is done?
Thanks!

Related

iOS: Handling OpenGL code running on background threads during App Transition

I am working on an iOS application that, say on a button click, launches several threads, each executing a piece of Open GL code. These threads either have a different EAGLContext set on them, or if they use same EAGLContext, then they are synchronised (i.e. 2 threads don't set same EAGLContext in parallel).
Now suppose the app goes into background. As per Apple's documentation, we should stop all the OpenGL calls in applicationWillResignActive: callback so that by the time applicationDidEnterBackground: is called, no further GL calls are made.
I am using dispatch_queues to create background threads. For e.g.:
__block Byte* renderedData; // some memory already allocated
dispatch_sync(glProcessingQueue, ^{
[EAGLContext setCurrentContext:_eaglContext];
glViewPort(...)
glBindFramebuffer(...)
glClear(...)
glDrawArrays(...)
glReadPixels(...) // read in renderedData
}
use renderedData for something else
My question is - how to handle applicationWillResignActive: so that any such background GL calls can be not just stopped, but also be able to resume on applicationDidBecomeActive:? Should I wait for currently running blocks to finish before returning from applicationWillResignActive:? Or should I just suspend glProcessingQueue and return?
I have also read that similar is the case when app is interrupted in other ways, like displaying an alert, a phone call, etc.
I can have multiple such threads at any point of time, invoked by possibly multiple ViewControllers, so I am looking for some scalable solution or design pattern.
The way I see it you need to either pause a thread or kill it.
If you kill it you need to ensure all resources are released which means again calling openGL most likely. In this case it might actually be better to simply wait for the block to finish execution. This means the block must not take too long to finish which is impossible to guarantee and since you have multiple contexts and threads this may realistically present an issue.
So pausing seems better. I am not sure if there is a direct API to pause a thread but you can make it wait. Maybe a s system similar to this one can help.
The linked example seems to handle exactly what you would want; it already checks the current thread and locks that one. I guess you could pack that into some tool as a static method or a C function and wherever you are confident you can pause the thread you would simply do something like:
dispatch_sync(glProcessingQueue, ^{
[EAGLContext setCurrentContext:_eaglContext];
[ThreadManager pauseCurrentThreadIfNeeded];
glViewPort(...)
glBindFramebuffer(...)
[ThreadManager pauseCurrentThreadIfNeeded];
glClear(...)
glDrawArrays(...)
glReadPixels(...) // read in renderedData
[ThreadManager pauseCurrentThreadIfNeeded];
}
You might still have an issue with main thread if it is used. You might want to skip pause on that one otherwise your system may simply never wake up again (not sure though, try it).
So now you are look at interface of your ThreadManager to be something like:
+ (void)pause {
__threadsPaused = YES;
}
+ (void)resume {
__threadsPaused = NO;
}
+ (void)pauseCurrentThreadIfNeeded {
if(__threadsPaused) {
// TODO: insert code for locking until __threadsPaused becomes false
}
}
Let us know what you find out.

Flood main thread with consuming tasks

I would like to flood the main thread with random tasks for a certain amount of time in order to figure out how another part of my application would in this circumstances. How can I achieve this?
You could just create an while-loop that never ends. Something like:
BOOL contunue = YES;
while (contunue) {
// Code
}
That would run until you stop the program yourself.
Edit:
To add a timer to the above code, you could use NSTimer to change the value of continue to NO after a determined amount of time

UIApplicationDidEnterBackgroundNotification remaining time for execution

In my app using iOS 9.2, Swift 2.1 I need to save some data into core data when the app goes to background. For this I registered each of the view controllers in the call path for UIApplicationDidEnterBackgroundNotification notification, with an instance method each for saving respective data.
I read on multiple places that by default the app gets about 5 seconds to finish off the execution and hence we need to use beginBackgroundTaskWithExpirationHandler to extend it to about 5 minutes. Following is an example of the selector method that responds to the above notification.
func applicationEntersBackground()
{
print("Before Extension: \(UIApplication.sharedApplication().backgroundTimeRemaining)")
let taskID = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler(nil)
print("During Extension: \(UIApplication.sharedApplication().backgroundTimeRemaining)")
saveCoreData()
if(taskID != UIBackgroundTaskInvalid)
{
UIApplication.sharedApplication().endBackgroundTask(taskID)
}
print("After Extension: \(UIApplication.sharedApplication().backgroundTimeRemaining)")
}
Following is the results of print() statements
Before Extension: 179.933103708318
During Extension: 179.930266333336
After Extension: 179.922843541659
My doubts are
Why is the remaining time about 180 seconds even before I requested for time extension? I tried multiple times. It is always close to 180 seconds and not the 5 seconds as suggested.
Why doesn't the call to beginBackgroundTaskWithExpirationHandler have any impact on the remaining time?
Once the applicationEntersBackground method of a VC returns, similar notification is sent to another VC's corresponding method. Suppose 180 seconds is the total extended duration and VC1 spends about 10 seconds on notification handling, does VC2 notification handler get around 170 seconds between its beginBackgroundTaskWithExpirationHandler - endBackgroundTask calls?
Between successive invocations of the notification handlers of different VCs, there is obviously a very short period where the extension request is not active. How does the timing play out in this case? Does the 5 second counter (provided it is true) come back to life as soon as an endBackgroundTask call is made, and possibly terminate the application before the next VC can get its notification?
Appreciate any help.
By looking at the documentation for backgroundTimeRemaining:
While the app is running in the foreground, the value in this property remains suitably large. If the app starts one or more long-running tasks using the beginBackgroundTaskWithExpirationHandler: method and then transitions to the background, the value of this property is adjusted to reflect the amount of time the app has left to run.
To answer your questions:
backgroundTimeRemaining stays around 180 while the application is in foreground so you can tell what time you'd have once you start a background task. This value is not an indicator of how long are you allowed to run without a background task.
beginBackgroundTaskWithExpirationHandler has an impact, as you can see, the remaining time decreased (by a small value as the method doesn't take much time)
What matters here is the time passed between the call to beginBackgroundTaskWithExpirationHandler and the one to endBackgroundTask. You can split whatever you need the time interval between your calls, providing you don't exceed the 180s limit
Once you call endBackgroundTask the application will be suspended, regardless it took 2 seconds or 179 seconds.
You can find out more details about application entering background here. I'd recommend going through the documentation, it might clarify other questions you might have on this matter.

Playing sound at interval with GCD timer not behaving as expected

Hi !
I'm building a timer using GCD for the purpose of playing a sound at a specific interval, to be more precise, it's a metronome sound. I've been trying for days to solve my issue but nothing. Everything is good but when I set my tempo to a bigger value , let's say 150 bpm or 200 bpm, when the sound starts for the first time, it fires very quickly(almost like two sounds in the same time meaning it does not have the expected interval) and after this , it calibrates. I start the sound the second time , all is good... so this happens only the first time I resume my dispatch source so I'm guessing it has something to do with loading the sound from the disk , like in this post : Slow start for AVAudioPlayer the first time a sound is played . For my sound I used at first an instance of AVAudioPlayer with prepareToPlay and play and also created it in the AppDelegate class, it hasn't work...I have even tried the SoundManager class developed by #NickLockwood,same issue. At present, I'm using a SystemSoundID. As for the timers, this is my first GCD timer , I've already tried the classical NSTimer, CADisplayLink and other timers found on git... all in vain.
Another interesting issue is that with the other timers , everything is perfect on the simulator but on the device the same glitch.
Here's the code, I hope someone will bring me to the light.
-(void)playButtonAction //
{
if (_metronomeIsAnimatingAndPLaying == NO)
{
[self startAnimatingArm]; // I start my animation and create my timer
metronomeTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
dispatch_source_set_timer(metronomeTimer,dispatch_time(DISPATCH_TIME_NOW, duration * NSEC_PER_SEC),duration * NSEC_PER_SEC,duration *NSEC_PER_SEC);
dispatch_source_set_event_handler(metronomeTimer, ^{[self playTick];});
dispatch_resume(metronomeTimer);
_metronomeIsAnimatingAndPLaying = YES;
}
}
-(void)playTick
{
AudioServicesPlaySystemSound(appDeleg.soundID); // soundID is created in appDelegate
}
In my application didFinishLaunching
NSString *path = [[NSBundle mainBundle] pathForResource:#"tick"
ofType:#"caf"];
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:path]
, &_soundID);
And BPM setter and getter :
- (NSUInteger)bpm
{
return round(60.0 / duration);
}
- (void)setBpm:(NSUInteger)bpm
{
if (bpm >= MaxBPM) {
bpm = MaxBPM;
} else if (bpm <= MinBPM) {
bpm = MinBPM;
}
duration = (60.0 / bpm);
}
This arrangement will fundamentally never work.
GCD is a thread-pool designed to facilitate task-level parallelism. It is usually asynchronous and non real-time. These are almost precisely the opposite characteristics to those required in an audio application.
Each thread servicing a GCD queue is contending with other threads in the system for an opportunity to execute. Furthermore, the queue may be busy at requested time processing something else. If that something else is long-running - and long-running tasks are precisely the kind of thing that GCD is made for - the scheduler may pre-empt the thread before the operation has completed and penalise the queue; it may wait a long time for service.
The Manpage for GCD states the following about timers on GCD queues:
A best effort attempt is made to submit the event handler block to the target queue at the specified time; however, actual invocation may occur at a later time.
NSTimer will not be any better. Its documentation states A timer is not a real-time mechanism. Since you'll probably run this on the application's main run-loop, it will also be very unpredictable.
The solution to this problem is to use lower-level audio APIs - specifically Audio Units. The advantage of doing so is that soft-syth units have an event queue which is serviced by the unit's render handler. This runs on a real-time thread, and offers extremely robust and predictable service. Since you can queue a considerable number of events with timestamps in the future, your timing requirements are now quite loose. You could safely use either GCD or a NSTimer for this.

Cocos2d (iOS): scheduleOnce called in callback won't fire

I am scheduling a callback via scheduleOnce (Cocos 1.1b), and when the callback is executed and once all tasks were performed there, I try to reschedule the same callback again (just with a different delay). The reasoning is to achieve a varying delay between the callbacks.
However, while it is called properly the first time, the second scheduling will never fire it again. Stepping through the Cocos libs, it eventually adds a timer to the list, but it won't fire.
Any clue what I am doing wrong and need to do differently?
Edit: just saw this entry in the log on the second scheduling:
CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: 0.00 to 0.00
I tried now to unschedule all timers explicitly first, however it doesn't make a difference. I would anyway expect scheduleOnce to reset that timer on callback.
It may be a bug in Cocos2D, after all you're using the very latest beta version. So I won't divulge into that, you may want to report this through the official channels however (cocos2d forum, google code issues for cocos2d-iphone).
In the meantime you can simply do this:
-(id) init
{
…
[self scheduleSelector:#selector(repeat) interval:0];
}
-(void) repeat
{
// simply schedule the selector again with a new interval
[self scheduleSelector:#selector(repeat) interval:CCRANDOM_0_1()];
}
Alternatively, if you want to re-schedule the selector at a later time, you can unschedule it as follows within the repeat method (the _cmd is shorthand for the selector of the current method):
-(void) repeat
{
[self unschedule:_cmd];
// re-schedule repeat at a later time
}

Resources