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

How should timers account for system sleep/suspend? #6759

Open
shaseley opened this issue Jun 10, 2021 · 10 comments
Open

How should timers account for system sleep/suspend? #6759

shaseley opened this issue Jun 10, 2021 · 10 comments

Comments

@shaseley
Copy link

I'm wondering how system sleep/suspend should affect setTimeout()'s waiting steps: should time pause during sleep, or should time continue to tick? Step 15 of the timer initialization steps says time should only tick for fully active documents and non-suspended workers. IIUC the former applies to BFCache, but I'm not sure if either are meant to take into account system sleep/suspend.

As far as use cases and developer expectations go, I think there are some use cases where pausing time during sleep is fine and some where it isn't. Things like calendar notifications are an obvious use case where you'd want time to continue to tick during sleep, so the notification can be shown immediately upon resume if the timer expired while suspended (although maybe something like Notification Triggers is the solution here?). But there are also use cases where pausing time would be fine, ​e.g. deferring low priority work during loading.

In Chromium's implementation, the behavior unfortunately varies depending on the platform, which is something we want to fix. I'm not sure what other browsers are doing here. From an implementation standpoint, there can be a flood of work including expired timers that happens during resume (thundering herd problem), so having time pause during sleep can be helpful to mitigate this to some degree. But then developers would need workarounds to get realtime behavior [1].

While I do think aligning on a reasonable default here is the right first step, it might be beneficial to consider adding an option for developers to specify this behavior. There is precedent in other systems, e.g. POSIX timers.

For additional context, this came up recently in a couple places:

  1. Writing the spec for the new scheduler.postTask() API. This provides another way to post delayed tasks, and we want to align with setTimeout() behavior.

  2. The behavior of clocks ticking during sleep came up in the context of performance.now() as there is some inconsistency on some platforms/browsers.

Do folks have opinions on what this behavior should be?

[1] We've seen workarounds for this in already in JavaScript code, e.g. implementing "realtime timers" by periodically checking how much time has elapsed. This seems unfortunate for a few reasons, including from an energy perspective (more wake-ups in the background).

@domenic domenic added the agenda+ To be discussed at a triage meeting label Jun 10, 2021
@annevk
Copy link
Member

annevk commented Jun 17, 2021

@smaug---- @rniwa care to provide context on Gecko and WebKit?

Pausing seems very much preferable to me and I don't think you cannot really rely on these being accurate anyway with background tab throttling, other tasks that might run long, etc.

@smaug----
Copy link

@shaseley do you happen to have some simple test case for this?

@shaseley
Copy link
Author

shaseley commented Jul 8, 2021

I cobbled one together here, but unfortunately don't have anything that automates putting the computer to sleep/waking it back up. That page just lets you set a timer for some number of minutes and computes how long it took. But it was helpful for testing this locally. My testing methodology was to:

  1. Set a timer for 5 minutes
  2. Put my laptop to sleep for ~3 minutes (via Apple button, Sleep)
  3. Wake it back up (before the timer was going to expire)
  4. Record when the timer expired.

Those steps can probably be compressed, but I wanted to make sure the result was obvious.

On my macbook (Big Sur), the timer expired after ~7.5-8 min of wall time on Chrome, FF, and Safari, so it was clear that timers were paused during sleep. I can test Android and probably Linux, but I'll need to work on getting more devices or crowdsourcing to test other platforms.

@domenic
Copy link
Member

domenic commented Jul 8, 2021

Let's start a table:

Platform Browser Result Source
macOS Big Sur Chrome Timers paused during sleep #6759 (comment)
macOS Big Sur Firefox Timers paused during sleep #6759 (comment)
macOS Big Sur Safari Timers paused during sleep #6759 (comment)
Windows 10 Chrome Timers did not pause during sleep #6759 (comment)
Windows 10 Firefox Timers did not pause during sleep #6759 (comment)
Linux Chrome Timers paused during sleep #6759 (comment)
Linux Firefox Timers paused during sleep #6759 (comment)
Android Chrome Timers paused during sleep #6759 (comment)
Android Firefox Timers paused during sleep #6759 (comment)
iOS 14.6 (iPhone) Safari Timers paused during sleep #6759 (comment)

@shaseley
Copy link
Author

shaseley commented Jul 9, 2021

Some more results, using the same methodology: start a timer for 5 min, sleep for 3 minutes soon after, observe the time. In cases were we say timers paused during sleep, the setTimeout() callback took ~8 minutes to run (computed using Date.now()).

Thanks to @kjd564 for testing iOS and Windows!

Android

Testing was done on a Pixel 3a/Android 11.

  • Firefox 90.1.1: Timer paused during sleep
  • Chrome 91.0.4472.120: Timer paused during sleep

iOS

Testing was done on an iPhone 11 running iOS 14.6.

  • Safari: Timer paused during sleep

Windows

Testing was done on a Windows 10 laptop, running a recent version (10942.1083).

  • Firefox 89.0.2: Timer did not pause during sleep
  • Chrome 91.0.4472.124: Timer did not pause during sleep

Note: On Windows, the timers went off at almost exactly 5 minutes. To verify that the device did in fact sleep, we looked at the event viewer and found sleep/wake logs during the time period that the device was put to sleep, which gives us additional confidence in the result. Note that these results are also in line with the behavior of performance.now(). This was the expected result for Chrome, but not sure about FF.

@smaug----
Copy link

The table is missing linux ;)

@shaseley
Copy link
Author

shaseley commented Jul 9, 2021

Linux

Ran the same test on a machine I had at home running Ubuntu 16.04.7 (Xenial):

  • Firefox 88.0: Timer paused during sleep
  • Chrome 91.0.4472.114: Timer paused during sleep

Any other browser/OS combinations we want to try?

@smaug----
Copy link

Does it matter on Windows if the system is using good old 'S3' or the newer "modern standby" 'S0 Low Power Idle'?

(Modern standby is even buggier on Linux than it is on Windows, but perhaps one has a machine where it works well enough for testing on linux too)

@past
Copy link

past commented Sep 2, 2021

Removing the agenda+ label since this issue has been discussed and no new comments have been added. Please add it back if there is a need to discuss it again.

@dotproto
Copy link

dotproto commented Jan 2, 2024

We've been discussing a similar issue with the Alarms API in the WebExtensions Community Group. Chrome originally introduced the Alarms API as a way to perform setTimeout() and setInterval()-like operations in non-persistent background pages (also known as "event pages" and "lazy background pages"). As such, the original intent of the Alarms API is that it would behave like the web platform methods.

As we discuss inconsistent behavior across browsers, bugs in existing implementations, and developer expectations, we are reconsidering how alarms should behave across sleep/suspend boundaries.

The current consensus opinion is that time should continue to tick for alarms while a device is suspend. We commonly refer to this as using wall-clock time. For example, say it is currently 9:00 AM and an extension schedules an alarm for 9:10 AM. At 9:05 the user suspends the device and unsuspends it at 9:15. When the device wakes, the browser should dispatch events for alarms that were scheduled when the device was asleep. This means the 9:10 alarm will fire at 9:15. (If time did not dick while the device was alseep, the alarm event would not be dispatched until 9:20.)

One of the considerations that lead us in this direction is that the Alarms API has more metadata about the scheduled operation than the web platform APIs. We can see this in the browser.alarms.onAlarm event handler, which receives an Alarms object that contains the following properties:

  • name - either "" or another string provided at alarm creation
  • scheduledTime - the time at which the alarm was scheduled to fire
  • (optional) periodInMinutes - the period at which the alarm will repeat (only included if set at alarm creation)

Browser representatives participating in the WECG felt that this provides the extension developer with enough information to distinguish between alarm events if multiple fire when a device resumes from sleep/suspend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

7 participants
@past @domenic @dotproto @annevk @shaseley @smaug---- and others