whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
7.86k stars 2.58k forks source link

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

Open shaseley opened 3 years ago

shaseley commented 3 years ago

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).

annevk commented 3 years ago

@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---- commented 3 years ago

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

shaseley commented 3 years ago

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 commented 3 years ago

Let's start a table:

Platform Browser Result Source
macOS Big Sur Chrome Timers paused during sleep https://github.com/whatwg/html/issues/6759#issuecomment-876753343
macOS Big Sur Firefox Timers paused during sleep https://github.com/whatwg/html/issues/6759#issuecomment-876753343
macOS Big Sur Safari Timers paused during sleep https://github.com/whatwg/html/issues/6759#issuecomment-876753343
Windows 10 Chrome Timers did not pause during sleep https://github.com/whatwg/html/issues/6759#issuecomment-877411114
Windows 10 Firefox Timers did not pause during sleep https://github.com/whatwg/html/issues/6759#issuecomment-877411114
Linux Chrome Timers paused during sleep https://github.com/whatwg/html/issues/6759#issuecomment-877497986
Linux Firefox Timers paused during sleep https://github.com/whatwg/html/issues/6759#issuecomment-877497986
Android Chrome Timers paused during sleep https://github.com/whatwg/html/issues/6759#issuecomment-877411114
Android Firefox Timers paused during sleep https://github.com/whatwg/html/issues/6759#issuecomment-877411114
iOS 14.6 (iPhone) Safari Timers paused during sleep https://github.com/whatwg/html/issues/6759#issuecomment-877411114
shaseley commented 2 years ago

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.

iOS

Testing was done on an iPhone 11 running iOS 14.6.

Windows

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

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---- commented 2 years ago

The table is missing linux ;)

shaseley commented 2 years ago

Linux

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

Any other browser/OS combinations we want to try?

smaug---- commented 2 years ago

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 commented 2 years ago

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 commented 6 months ago

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:

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.