From 10b425188c4599c38342a8c7412271f36a60dfc2 Mon Sep 17 00:00:00 2001 From: Raphael Kubo da Costa Date: Fri, 14 Jun 2024 14:27:18 +0200 Subject: [PATCH] Make PressureObserver.observe() and data delivery algorithm less vague (#283) Related to #282: we need these algorithms to be properly defined in order to be able to support WebDriver and fake pressure states. This somewhat big change intends to clarify what "activate" and "deactivate" data delivery actually mean, as there used to be just a "data delivery" algorithm and no accompanying definitions for those two verbs. Furthermore, the data delivery algorithm itself was confusing: - It referenced a `data` variable in its declaration that was never passed by any callers. - `data` was of an implementation-defined type and format, but the steps assumed it had some associated information like source type that was not set anywhere. Fixing the above has required changes in different layers: - The "platform collector" concept, which used to be an abstract entity with which all globals interacted to retrieve telemetry data for all source types, is now a per-global and per-source type concept. The lower-level concept that represents a cross-global interface for the hardware or OS is now a "pressure source", which contains a snapshot of the latest reading it has retrieved along with a timestamp. - "Data delivery" is now called "data collection". It uses a platform collector and its associated pressure source to retrieve a telemetry sample that is transformed into a pressure state. - There are algorithms for activating and deactivating data collection. Both ensure they data collection cannot be started/stopped if they have already been. - `PressureObserver.observe()`'s had a "is not a valid source type" check that was too vague, as this step determined whether a given source type is supported by the platform or not, but the definition of "valid source type" was something else entirely. This step has been replaced by a sequence of steps that attempts to retrieve an existing platform collector for a source type and, if one does not exist, tries to connect to a corresponding pressure source. This change makes the same platform collector be used for all observers of a given source type and lays out in more detail what it means to check whether a source type is valid or not in this context. Co-authored with @kenchris in #265. It was split off as a separate pull request to make it easier to review and understand. --- index.html | 280 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 225 insertions(+), 55 deletions(-) diff --git a/index.html b/index.html index 9d7cf5a..e41190f 100644 --- a/index.html +++ b/index.html @@ -216,7 +216,7 @@

Sampling and Reporting Rate

The reporting rate for a pressure observer is the rate at which it runs - the [=data delivery=] steps, and it will never exceed the [=sampling rate=]. + the [=data collection=] steps, and it will never exceed the [=sampling rate=].

The [=sampling rate=] differs from the [=requested sampling rate=] when the @@ -243,26 +243,69 @@

Sampling and Reporting Rate

Platform primitives

- The [=platform collector=] refers to a platform interface, with which the [=user agent=] interacts to - obtain the telemetry readings required by this specification. + A pressure source is an abstract, [=implementation-defined=] + interface to hardware counters or an underlying framework that provides + telemetry data about a source type + defined by {{PressureSource}}. A [=pressure source=] can make use of data + fusion with data from additional sources if that provides more precise + results.

- A [=platform collector=] can be defined by the underlying platform (e.g. in a native telemetry - framework) or by the [=user agent=], if it has a direct access to hardware counters. + The telemetry data provided by a [=pressure source=] is represented in this + specification as a pressure source sample, a [=struct=] + consisting of the following [=struct/items=]: +

- A [=platform collector=] can support telemetry for different source types of computing - devices defined by {{PressureSource}}, or there can be multiple [=platform collectors=]. + A [=pressure source=] has an associated latest sample, a [=pressure source + sample=] or null. It is initially null.

- From the implementation perspective [=platform collector=] can be treated as a software proxy for the - corresponding hardware counters. It is possible to have multiple [=platform collector=] simultaneously - interacting with the same underlying hardware if the underlying platform supports it. + A platform collector is an abstract interface responsible for + obtaining telemetry samples from a [=pressure source=], transforming them + into [=pressure states=] and providing them to the [=user agent=].

- In simple cases, a [=platform collector=] represents individual hardware counters, but if the provided - counter readings are a product of data fusion performed in software, the [=platform collector=] - represents the results of the data fusion process. This may happen in user space or in kernel space. + A [=platform collector=] has the following associated data: +

+ +

+ The format of the telemetry data provided by a [=pressure source=] + and stored in its [=pressure source/latest sample=]'s [=pressure source + sample/data=] is [=implementation-defined=], and so is the process through + which a [=platform collector=] transforms it into a [=pressure state=]. +

+

+ For this specification's purposes, [=platform collectors=] are scoped to a + [=global object=] via the [=platform collector mapping=].

As collecting telemetry data often means polling hardware counters, it is not a free operation and thus, @@ -361,7 +404,8 @@

a registered observer list per supported [=source type=], which is initially empty.
  • - a reference to an underlying platform collector as detailed in [[[#platform-primitives]]]. + a platform collector mapping, an [=ordered map=] of [=source + types=] to [=platform collectors=].
  • A registered observer consists of an observer (a {{PressureObserver}} object). @@ -606,13 +650,57 @@

    The observe() method

    Run the following steps [=in parallel=]:
    1. - If |source:PressureSource| is not a [=valid source type=], - [=queue a global task=] on the [=PressureObserver task source=] - given |relevantGlobal| - to reject |promise| {{NotSupportedError}} and abort these steps. + Let |platformCollector| be null. +
    2. +
    3. + If |relevantGlobal|'s [=platform collector mapping=] + [=map/contains=] |source|: +
        +
      1. + Set |platformCollector| to |relevantGlobal|'s [=platform + collector mapping=][|source|]. +
      2. +
      +
    4. +
    5. + Otherwise: +
        +
      1. + Let |newCollector| be a new [=platform collector=] whose + [=platform collector/associated pressure source=] is null. +
      2. +
      3. + Let |pressureSource| be an [=implementation-defined=] + [=pressure source=] that provides telemetry data about + |source|, or null if none exists. +
      4. +
      5. + Set |newCollector|'s [=platform collector/associated pressure + source=] to |pressureSource|. +
      6. +
      7. + If |newCollector|'s [=platform collector/associated + pressure source=] is not null: +
          +
        1. + Set |platformCollector| to |newCollector|. +
        2. +
        3. + Set |relevantGlobal|'s [=platform collector + mapping=][|source|] to |platformCollector|. +
        4. +
        +
      8. +
    6. - Activate [=data delivery=] of |source| data to |relevantGlobal|. + If |platformCollector| is null, [=queue a global task=] on the + [=PressureObserver task source=] given |relevantGlobal| to reject + |promise| {{NotSupportedError}} and abort these steps. +
    7. +
    8. + Invoke [=activate data collection=] with |source| and + |relevantGlobal|.
    9. [=Queue a global task=] on the [=PressureObserver task source=] given @@ -623,7 +711,7 @@

      The observe() method

      1. If |relevantGlobal|'s [=registered observer list=] for |source| is [=list/empty=], - deactivate [=data delivery=] of |source| data to |relevantGlobal|. + invoke [=deactivate data collection=] with |source| and |relevantGlobal|.
      2. Return. @@ -686,9 +774,13 @@

        The unobserve() method

        If |registeredObserverList| is [=list/empty=]:
        1. - Deactivate [=data delivery=] of |source| data to + Invoke [=deactivate data collection=] with |source| and |relevantGlobal|.
        2. +
        3. + [=map/Remove=] |relevantGlobal|'s [=platform collector + mapping=][|source|]. +
      @@ -730,9 +822,13 @@

      The disconnect() method

      If |registeredObserverList| is [=list/empty=]:
      1. - Deactivate [=data delivery=] of |source| data to + Invoke [=deactivate data collection=] with |source| and |relevantGlobal|.
      2. +
      3. + [=map/Remove=] |relevantGlobal|'s [=platform collector + mapping=][|source|]. +
    @@ -1109,47 +1205,111 @@

    Supporting algorithms

    -

    Data delivery

    +

    Data Collection and Delivery

    - [=Data delivery=] from a [=platform collector=] can be activate and deactivated in an - [=implementation-defined=] manner per [=source type=] and [=global object=]. + To activate data collection given a [=source type=] |source| + and |relevantGlobal|, perform the following steps:

    - +
      +
    1. + If |relevantGlobal|'s [=platform collector mapping=] does not + [=map/contain=] |source|, abort these steps. +
    2. +
    3. + Let |platformCollector| be |relevantGlobal|'s [=platform collector + mapping=][|source|]. +
    4. +
    5. + If |platformCollector|'s [=platform collector/activated=] is true, + abort these steps. +
    6. +
    7. + Set |platformCollector|'s [=platform collector/activated=] to true. +
    8. +
    9. + In an [=implementation-defined=] manner, start running the [=data + collection=] steps with |relevantGlobal|, |source|, and + |platformCollector|. + +
    10. +

    - The data delivery steps that are run when - an [=implementation-defined=] |data| sample of [=source type=] |source:PressureSource| is - obtained from [=global object=] |relevantGlobal|'s [=platform collector=], - are as follows: -

      + To deactivate data collection given a [=source type=] |source| + and |relevantGlobal|, perform the following steps: +

      +
        +
      1. + If |relevantGlobal|'s [=platform collector mapping=] does not + [=map/contain=] |source|, abort these steps. +
      2. +
      3. + Let |platformCollector| be |relevantGlobal|'s [=platform collector + mapping=][|source|]. +
      4. +
      5. + If |platformCollector|'s [=platform collector/activated=] is false, + abort these steps. +
      6. +
      7. + In an [=implementation-defined=] manner, stop running the [=data + collection=] steps with |relevantGlobal|, |source|, and + |platformCollector|. +
      8. +
      9. + Set |platformCollector|'s [=platform collector/activated=] to false. +
      10. +
      11. + Perform any [=implementation-defined=] steps to signal to + |platformCollector|'s [=platform collector/associated pressure source=] + to stop retrieving telemetry data. +
      12. +
      +

      + The data collection steps given |relevantGlobal|, |source| and + |platformCollector| are as follows: +

      1. - Let |source:PressureSource| be the [=source type=] of the |data| sample. + Let |pressureSource| be |platformCollector|'s [=platform + collector/associated pressure source=].
      2. - Let |state:PressureState| be an [=adjusted pressure state=] given |data| and |source|. - + If |pressureSource| is null, abort these steps.
      3. - Let |timestamp| be the [=unsafe shared current time=] corresponding - to the moment when |data| was obtained from |relevantGlobal|'s - [=platform collector=]. - + Let |sample| be |pressureSource|'s [=pressure source/latest sample=].
      4. - Let |timeValue| be the [=relative high resolution time=] based on |timestamp| and - |relevantGlobal|. + If |sample| is null, abort these steps. +
      5. +
      6. + Let |state| be an [=adjusted pressure state=] calculated from + |source| and |sample|'s [=pressure source sample/data=]. + +
      7. +
      8. + [=Assert=]: |state| is not null. +
      9. +
      10. + Let |rawTimestamp| be |sample|'s [=pressure source + sample/timestamp=]. +
      11. +
      12. + Let |timeValue| be the [=relative high resolution time=] based on + |rawTimestamp| and |relevantGlobal|.
      13. [=list/For each=] |observer:PressureObserver| in |relevantGlobal|'s @@ -1313,7 +1473,12 @@

        Handling change of [=Document/fully active=] status

        [=registered observer list=] [=ordered map=]:
        1. - Deactivate [=data delivery=] of |source| to |relevantGlobal|. + Invoke [=deactivate data collection=] with |source| and + |relevantGlobal|. +
        2. +
        3. + [=map/Remove=] |relevantGlobal|'s [=platform collector + mapping=][|source|].
      14. @@ -1358,7 +1523,12 @@

        Handling changes to worker status

        [=registered observer list=] [=ordered map=]:
        1. - Deactivate [=data delivery=] of |source| to |relevantGlobal|. + Invoke [=deactivate data collection=] with |source| and + |relevantGlobal|. +
        2. +
        3. + [=map/Remove=] |relevantGlobal|'s [=platform collector + mapping=][|source|].