zng_view_api/
view_process.rs1use std::{env, mem, 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}
168impl StaticPatch {
169 pub fn capture() -> Self {
171 Self {
172 same_process: same_process(),
173 tracing: tracing_shared::SharedLogger::new(),
174 }
175 }
176
177 pub unsafe fn install(&self) {
183 unsafe {
185 *std::ptr::addr_of_mut!(SAME_PROCESS) = &*self.same_process;
186 }
187 self.tracing.install();
188 }
189}