1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// SPDX-FileCopyrightText: Copyright (c) 2026 Mike Li/Mikewolfli/Wei Li(mikewolfli@163.com)
// SPDX-License-Identifier: MIT
//! The shared TTL wrapper for environment facts (P-02 / P-201).
//!
//! # The defect this removes
//!
//! The style layer re-reads the platform's environment revision on a bounded 250 ms poll
//! ([`crate::style::environment::ENVIRONMENT_POLL_INTERVAL_MS`]), and every backend that derives
//! its revision asks the operating system for the facts behind it. Windows answered that with a
//! 1 s cache per registry-backed fact, while `high_contrast`, `reduce_motion` and `system_locale`
//! were re-read on every poll; the macOS and JNI-backed Android backends had no cache at all. The
//! same mechanism therefore cost an order of magnitude more on some backends than on others.
//!
//! # Why the wrapper is shared rather than copied
//!
//! A per-backend cache drifts in two ways that matter here: the TTL values diverge, and a
//! notification that is supposed to drop the cache reaches only the backend that remembered to
//! write the invalidation. Both are properties of the *mechanism*, so the mechanism lives once,
//! here, and the backends only declare their slot.
//!
//! # Invalidation
//!
//! Two things make a cached reading stale:
//!
//! * **Time.** [`ENVIRONMENT_TTL`] bounds how long a reading is reused. It is the same one second
//! Windows already used, far below the rate at which anyone changes a system preference.
//! * **A host notification.** [`crate::platform::environment_notify::bump`] advances a
//! process-wide revision that
//! [`Platform::notify_environment_changed`](crate::platform::Platform::notify_environment_changed)
//! calls by default. Every reading records the revision it was taken at, so a notification
//! invalidates **every** slot at once — which is exactly the trait's documented promise that
//! "a backend that caches the facts drops that cache so the next read goes to the host". Without
//! this, a notified change would still be answered from the old value for up to a second, which
//! is the mismatch P-201 measured.
//!
//! The cache is not a value source: it never invents a fact. A miss runs the caller's reader, and
//! whatever that reader answers — including `None` for "this backend cannot observe it" — is what
//! is cached and returned.
use crate;
/// How long one environment reading is reused.
///
/// One second, the value the Windows backend already used before this module existed: long enough
/// that a 250 ms poll does not repeat a registry or JNI transaction four times, short enough that a
/// change reaches the screen without a notification source.
pub const ENVIRONMENT_TTL: Duration = from_secs;
/// One fact's slot: when it was read, the notification revision it was read at, and the value.
///
/// The revision is stored **beside** the time rather than folded into it because the two
/// invalidation triggers are independent: a notification at 900 ms must not reuse a reading taken
/// at 100 ms even though the TTL has not expired.
pub type EnvironmentFactCache<T> = ;
/// Returns a cached copy of an environment fact, re-reading it when the TTL or a host notification
/// says the stored one is stale.
///
/// `read` runs only on a miss (a first call, an expired reading, or a notification since the last
/// one), so the OS-facing query is what stays inside the closure and the common path is a
/// mutex-guarded copy. The lock is never held across `read`: a reader that touches a toolkit object
/// must not block every other reader while it waits for that object.
pub