zng_view_api/
view_process.rs1use std::{env, mem, path::PathBuf, time::Duration};
2
3#[cfg(not(target_arch = "wasm32"))]
4use std::time::Instant;
5
6#[cfg(target_arch = "wasm32")]
7use web_time::Instant;
8
9use parking_lot::Mutex;
10use zng_txt::Txt;
11
12use crate::{VIEW_MODE, VIEW_SERVER, VIEW_VERSION};
13
14#[derive(Clone, Debug)]
16#[non_exhaustive]
17pub struct ViewConfig {
18 pub version: Txt,
22
23 pub server_name: Txt,
28
29 pub headless: bool,
31}
32impl ViewConfig {
33 pub fn new(version: impl Into<Txt>, server_name: impl Into<Txt>, headless: bool) -> Self {
35 Self {
36 version: version.into(),
37 server_name: server_name.into(),
38 headless,
39 }
40 }
41
42 pub fn from_env() -> Option<Self> {
49 if let Ok(version) = env::var(VIEW_VERSION)
50 && let Ok(server_name) = env::var(VIEW_SERVER)
51 {
52 let headless = env::var(VIEW_MODE).map(|m| m == "headless").unwrap_or(false);
53 Some(ViewConfig {
54 version: Txt::from_str(&version),
55 server_name: Txt::from_str(&server_name),
56 headless,
57 })
58 } else {
59 None
60 }
61 }
62
63 pub(crate) fn is_awaiting_same_process() -> bool {
66 matches!(*same_process().lock(), SameProcess::Awaiting)
67 }
68
69 pub(crate) fn set_same_process(cfg: ViewConfig) {
75 if Self::is_awaiting_same_process() {
76 *same_process().lock() = SameProcess::Ready(cfg);
77 } else {
78 unreachable!("use `waiting_same_process` to check, then call `set_same_process` only once")
79 }
80 }
81
82 pub fn wait_same_process() -> Self {
88 let _s = tracing::trace_span!("ViewConfig::wait_same_process").entered();
89
90 if !matches!(*same_process().lock(), SameProcess::Not) {
91 panic!("`wait_same_process` can only be called once");
92 }
93
94 *same_process().lock() = SameProcess::Awaiting;
95
96 let time = Instant::now();
97 let timeout = Duration::from_secs(5);
98 let sleep = Duration::from_millis(10);
99
100 while Self::is_awaiting_same_process() {
101 std::thread::sleep(sleep);
102 if time.elapsed() >= timeout {
103 panic!("timeout, `wait_same_process` waited for `{timeout:?}`");
104 }
105 }
106
107 match mem::replace(&mut *same_process().lock(), SameProcess::Done) {
108 SameProcess::Ready(cfg) => cfg,
109 _ => unreachable!(),
110 }
111 }
112
113 pub fn assert_version(&self, is_same_process: bool) {
120 if self.version != crate::VERSION {
121 let msg = format!(
122 "view API version is not equal, app-process: {}, view-process: {}",
123 self.version,
124 crate::VERSION
125 );
126 if is_same_process {
127 panic!("{}", msg)
128 } else {
129 eprintln!("{msg}");
130 zng_env::exit(i32::from_le_bytes(*b"vapi"));
131 }
132 }
133 }
134
135 pub fn is_version_err(exit_code: Option<i32>, stderr: Option<&str>) -> bool {
139 exit_code.map(|e| e == i32::from_le_bytes(*b"vapi")).unwrap_or(false)
140 || stderr.map(|s| s.contains("view API version is not equal")).unwrap_or(false)
141 }
142}
143
144enum SameProcess {
145 Not,
146 Awaiting,
147 Ready(ViewConfig),
148 Done,
149}
150
151static mut SAME_PROCESS: &Mutex<SameProcess> = &SAME_PROCESS_COLD;
155static SAME_PROCESS_COLD: Mutex<SameProcess> = Mutex::new(SameProcess::Not);
156
157fn same_process() -> &'static Mutex<SameProcess> {
158 unsafe { *std::ptr::addr_of!(SAME_PROCESS) }
160}
161
162pub struct StaticPatch {
165 same_process: *const Mutex<SameProcess>,
166 tracing: tracing_shared::SharedLogger,
167
168 env_res: PathBuf,
169 env_config: PathBuf,
170 env_cache: PathBuf,
171}
172impl StaticPatch {
173 pub fn capture() -> Self {
175 Self {
176 same_process: same_process(),
177 tracing: tracing_shared::SharedLogger::new(),
178 env_res: zng_env::res(""),
179 env_config: zng_env::config(""),
180 env_cache: zng_env::cache(""),
181 }
182 }
183
184 pub unsafe fn install(&self) {
190 unsafe {
192 *std::ptr::addr_of_mut!(SAME_PROCESS) = &*self.same_process;
193 }
194 self.tracing.install();
195 zng_env::init_res(self.env_res.clone());
196 zng_env::init_config(self.env_config.clone());
197 zng_env::init_cache(self.env_cache.clone());
198 }
199}