Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Discussion: Timers] Background execution and ordering semantics #167

Closed
ide opened this issue Mar 19, 2015 · 14 comments

Comments

@ide
Copy link
Contributor

commented Mar 19, 2015

I wanted to start a discussion about the timers and an API that exposes the capabilities of the system timers. A couple of things I noticed were that the timers use CADisplayLink on the main thread and also listen to the UIApplication state notifications. To my knowledge this works well for timers that are coupled to the UI (requestAnimationFrame comes to mind, there may be other uses). I'd like to discuss timers that do other work e.g. interact with the event loop or schedule background tasks, and how they co-interact.

Event loop timers seem to fall into two categories - those that run at the end of the current loop and those that run right after. This is like process.nextTick and setImmediate. These aren't too hard to implement though there are a couple of gotchas like warning if a process.nextTick handler endlessly schedules itself before blowing the stack. I would also expect both of these timers to run even if the app is inactive or backgrounded (e.g. say you get a network response while the app is backgrounded and schedule some work to run soon after - it should run in the background).

Background task timers (setTimeout, setInterval) raise a couple of questions like the semantics of a zero-ms timeout. I would also propose that these run in the background as well to address the use of running code that does not need the app to be foregrounded. And, if the app is backgrounded there is little harm in running UI code anyway -- in cases where it does matter, the timer handler can check the UIApplication state.

Background task timers that repeat are also good candidates for energy optimization. One thing that OS X systems do is coalesce timers where possible, partly aided by the programmer providing a tolerance value. If many running apps schedule timers that can overlap within their respective tolerance windows, the system may wake the CPU just once to run all of those tasks together.

There is also a question of the ordering of all of these timers. My strawman proposal is:

  • End of event loop: nextTick
  • ASAP after end of prior event loop: setImmediate, followed by setTimeout(0)
  • A short while later (using NSTimer): setTimeout(n + [0-tolerance]) up until the next frame begins
  • Start of next frame (CADisplayLink): requestAnimationFrame. However, this will wait till later if the app is foregrounded or inactive. Also I believe CADisplayLink may not fire if momentum scrolling is active (not verified).
  • Some time later (again using NSTimer): setTimeout(n + [0-tolerance]) where n is big enough that a frame has elapsed

Additionally, setTimeout(delay = 9, threshold = 2) should run before setTimeout(delay = 10, threshold = 0), and two setTimeouts with the same delay should run in the order that they were scheduled even if they have different thresholds. But to keep things simple at first I would recommend we go with whatever the system timer (NSTimer) does. There's an argument to be made that the timer handlers should use promises or semaphores, etc if they need to coordinate and not rely on the timer system.

@sahrens

This comment has been minimized.

Copy link
Contributor

commented Mar 19, 2015

Honestly we haven't given timers a very thorough considerations, and background operation is completely unconsidered at this point - thanks for such a thorough writeup!

I did add one simple feature recently - setTimeout(fn, 0) will now fire immediately (but in a different event loop, unlike setImmediate) rather than waiting for the next run loop frame as it currently does. This will be useful for time-slicing optimizations we have in mind.

-Spencer

On Mar 18, 2015, at 7:16 PM, James Ide notifications@github.com wrote:

I wanted to start a discussion about the timers and an API that exposes the capabilities of the system timers. A couple of things I noticed were that the timers use CADisplayLink on the main thread and also listen to the UIApplication state notifications. To my knowledge this works well for timers that are coupled to the UI (requestAnimationFrame comes to mind, there may be other uses). I'd like to discuss timers that do other work e.g. interact with the event loop or schedule background tasks, and how they co-interact.

Event loop timers seem to fall into two categories - those that run at the end of the current loop and those that run right after. This is like process.nextTick and setImmediate. These aren't too hard to implement though there are a couple of gotchas like warning if a process.nextTick handler endlessly schedules itself before blowing the stack. I would also expect both of these timers to run even if the app is inactive or backgrounded (e.g. say you get a network response while the app is backgrounded and schedule some work to run soon after - it should run in the background).

Background task timers (setTimeout, setInterval) raise a couple of questions like the semantics of a zero-ms timeout. I would also propose that these run in the background as well to address the use of running code that does not need the app to be foregrounded. And, if the app is backgrounded there is little harm in running UI code anyway -- in cases where it does matter, the timer handler can check the UIApplication state.

Background task timers that repeat are also good candidates for energy optimization. One thing that OS X systems do is coalesce timers where possible, partly aided by the programmer providing a tolerance value. If many running apps schedule timers that can overlap within their respective tolerance windows, the system may wake the CPU just once to run all of those tasks together.

There is also a question of the ordering of all of these timers. My strawman proposal is:

End of event loop: nextTick
ASAP after end of prior event loop: setImmediate, followed by setTimeout(0)
A short while later (using NSTimer): setTimeout(n + [0-tolerance]) up until the next frame begins
Start of next frame (CADisplayLink): requestAnimationFrame. However, this will wait till later if the app is foregrounded or inactive. Also I believe CADisplayLink may not fire if momentum scrolling is active (not verified).
Some time later (again using NSTimer): setTimeout(n + [0-tolerance]) where n is big enough that a frame has elapsed
Additionally, setTimeout(delay = 9, threshold = 2) should run before setTimeout(delay = 10, threshold = 0), and two setTimeouts with the same delay should run in the order that they were scheduled even if they have different thresholds. But to keep things simple at first I would recommend we go with whatever the system timer (NSTimer) does. There's an argument to be made that the timer handlers should use promises or semaphores, etc if they need to coordinate and not rely on the timer system.


Reply to this email directly or view it on GitHub.

@brentvatne brentvatne changed the title Timers - background execution and ordering semantics [Discussion: Timers] - background execution and ordering semantics May 31, 2015
@brentvatne brentvatne changed the title [Discussion: Timers] - background execution and ordering semantics [Discussion: Timers] Background execution and ordering semantics May 31, 2015
@christopherdro

This comment has been minimized.

Copy link
Contributor

commented Jul 20, 2015

@sahrens @ide

Any updates here?

@ide

This comment has been minimized.

Copy link
Contributor Author

commented Jul 20, 2015

I haven't had a need for this yet but sense that a more comprehensive timer API is going to be necessary once apps act as background services that need to do scheduling.

@ericvicenti

This comment has been minimized.

Copy link
Contributor

commented Jul 31, 2015

@jordwalke, I know you've been thinking about these problems, want to make sure you've seen this

harrykiselev pushed a commit to harrykiselev/react-native that referenced this issue Aug 5, 2015
Fix typo in dependency React => react
@jordwalke

This comment has been minimized.

Copy link
Member

commented Aug 5, 2015

@ide have you seen requestIdleFrame? That's a great way to use remaining frame time to do cleanup work that shouldn't block the UI - it should also tell you how much frame time remains.

BTW: Our JS timers should be fired from a display link on the JS thread. The only reason why they're not, is because I didn't know you could run display links from a non-Main thread! It would clean up so much complexity if every frame, on the JS thread, it just called into JS and asked "so, do you have any timers ready"? Feel free to send a PR, I know that @tadeuzagallo has been working towards this but hasn't gotten to actually moving the timers to the JS thread.

Having the timers be fired/managed from the JS thread will also make JS animations smoother because the pipeline is then more unidirectional and fewer things can delay/clog those timer messages being reported to the JS thread. Right now, if the main thread is blocked on updating UIViews, it can't send the timer message to the JS thread so that it can get work started.

@tadeuzagallo

This comment has been minimized.

Copy link
Contributor

commented Aug 6, 2015

@jordwalke requestIdleCallback is implemented, I just haven't landed because there were no use-cases so far, but I can do.

As for timers, our timers do run on the JS thread, for quite a while now, they are not implemented in JS though. TBC, everything runs based on a CADisplayLink on the JS thread, the only thing the displayLink on the main does right now is put VSync markers and count FPS.

@danielsandrewm

This comment has been minimized.

Copy link

commented Sep 22, 2015

Any update on background timers? I'm working on a fleet tracking app, and if I can't track GPS or fetch data in the background I'm going to have to abandon react-native.

@ide

This comment has been minimized.

Copy link
Contributor Author

commented Sep 22, 2015

@danielsandrewm no updates from my side. In the past when I've needed background tasks I wrote that code in Obj-C and then called into it from RN.

@jaygarcia

This comment has been minimized.

Copy link
Contributor

commented Sep 22, 2015

@danielsandrewm , yes the runloop currently is suspended by the OS when the application is backgrounded. I wrote my background tasks in ObjC, like @ide did, which will fire an update event with state information when the application was brought o the foreground.

The reality is that React Native doesn't entirely replace all classes of native applications.

@brentvatne

This comment has been minimized.

Copy link
Contributor

commented Jan 2, 2016

Moving this to ProductPains @ide - https://productpains.com/post/react-native/background-timer-execution/

I really want to see this but I think it'll have to be something that the community builds, in the short term at least.

@brentvatne brentvatne closed this Jan 2, 2016
rozele added a commit to rozele/react-native that referenced this issue Feb 15, 2016
@niftylettuce

This comment has been minimized.

Copy link
Contributor

commented Mar 10, 2016

@brentvatne @ide Could either of you give code example or perhaps implement the fix for Pomodoro app to make it work 100% proper?

@sahrens

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2016

@andrews is also thinking about timers and event ordering. We have some ideas floating around for hi-pri event precedence, and maybe even changing the JS runloop model to poll native rather than native always calling into it, which could give us more flexibility to do re-ordering and other queue processing in JS, and is probably more performant than calling in from native on every event like we are doing now.

On Mar 10, 2016, at 5:04 PM, niftylettuce notifications@github.com wrote:

@brentvatne @ide Could either of you give code example or perhaps implement the fix for Pomodoro app to make it work 100% proper?


Reply to this email directly or view it on GitHub.

@Amnesthesia

This comment has been minimized.

Copy link

commented Jun 14, 2016

Would it be possible to write a Swift module that runs whatever JavaScript it receives in a separate JavaScriptCore thread? Basically just a module that runs a separate thread in the background?

saghul added a commit to saghul/jitsi-meet that referenced this issue Feb 14, 2017
Turns out React Native's timers (setTimeout / setInterval) don't run while the
app is in the background: facebook/react-native#167

This patch replaces the global timer functions with those from the
react-native-background-timer package, which work in the background.

These timers won't magically make an application work in the background, but
they will run if an application already happens to run in the background. That's
our case while in a conference, so these timers will run, allowing XMPP pings to
be sent and the conference to stay up as long as the user desires.
lyubomir added a commit to saghul/jitsi-meet that referenced this issue Feb 15, 2017
Turns out React Native's timers (setTimeout / setInterval) don't run while the
app is in the background: facebook/react-native#167

This patch replaces the global timer functions with those from the
react-native-background-timer package, which work in the background.

These timers won't magically make an application work in the background, but
they will run if an application already happens to run in the background. That's
our case while in a conference, so these timers will run, allowing XMPP pings to
be sent and the conference to stay up as long as the user desires.
@ParthBarot-BoTreeConsulting

This comment has been minimized.

Copy link

commented Sep 9, 2017

@ide So what is the ultimate way for iOS?

I am really clueless, bcs iOS does not allow this timer to work when the app goes in background. I need to track GPS with some other important data every 5 minutes. As of now, I collect the data every 5 minutes in local async data and then push it to the server every 15 minutes. But on iOS, when the app goes background (Even screen is on or off), its no running this timer thread.

Now, what could be the options here?

  1. My App is not musical app, and also the usecase does not have changing GPS every momeny most of the time.
  2. Thinking of using heartbeat based tracking - https://github.com/transistorsoft/react-native-background-geolocation - This seems to work in iOS as per the issues and docs.
  3. Any other way, like Android's IntentService or BroadcastReceiver.
@danielsandrewm no updates from my side. In the past when I've needed background tasks I wrote that code in Obj-C and then called into it from RN.
@facebook facebook locked as resolved and limited conversation to collaborators Jul 23, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
You can’t perform that action at this time.