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), Ok(server_name)) = (env::var(VIEW_VERSION), env::var(VIEW_SERVER)) {
50 let headless = env::var(VIEW_MODE).map(|m| m == "headless").unwrap_or(false);
51 Some(ViewConfig {
52 version: Txt::from_str(&version),
53 server_name: Txt::from_str(&server_name),
54 headless,
55 })
56 } else {
57 None
58 }
59 }
60
61 pub(crate) fn is_awaiting_same_process() -> bool {
64 matches!(*same_process().lock(), SameProcess::Awaiting)
65 }
66
67 pub(crate) fn set_same_process(cfg: ViewConfig) {
73 if Self::is_awaiting_same_process() {
74 *same_process().lock() = SameProcess::Ready(cfg);
75 } else {
76 unreachable!("use `waiting_same_process` to check, then call `set_same_process` only once")
77 }
78 }
79
80 pub fn wait_same_process() -> Self {
86 let _s = tracing::trace_span!("ViewConfig::wait_same_process").entered();
87
88 if !matches!(*same_process().lock(), SameProcess::Not) {
89 panic!("`wait_same_process` can only be called once");
90 }
91
92 *same_process().lock() = SameProcess::Awaiting;
93
94 let time = Instant::now();
95 let timeout = Duration::from_secs(5);
96 let sleep = Duration::from_millis(10);
97
98 while Self::is_awaiting_same_process() {
99 std::thread::sleep(sleep);
100 if time.elapsed() >= timeout {
101 panic!("timeout, `wait_same_process` waited for `{timeout:?}`");
102 }
103 }
104
105 match mem::replace(&mut *same_process().lock(), SameProcess::Done) {
106 SameProcess::Ready(cfg) => cfg,
107 _ => unreachable!(),
108 }
109 }
110
111 pub fn assert_version(&self, is_same_process: bool) {
118 if self.version != crate::VERSION {
119 let msg = format!(
120 "view API version is not equal, app-process: {}, view-process: {}",
121 self.version,
122 crate::VERSION
123 );
124 if is_same_process {
125 panic!("{}", msg)
126 } else {
127 eprintln!("{msg}");
128 zng_env::exit(i32::from_le_bytes(*b"vapi"));
129 }
130 }
131 }
132
133 pub fn is_version_err(exit_code: Option<i32>, stderr: Option<&str>) -> bool {
137 exit_code.map(|e| e == i32::from_le_bytes(*b"vapi")).unwrap_or(false)
138 || stderr.map(|s| s.contains("view API version is not equal")).unwrap_or(false)
139 }
140}
141
142enum SameProcess {
143 Not,
144 Awaiting,
145 Ready(ViewConfig),
146 Done,
147}
148
149static mut SAME_PROCESS: &Mutex<SameProcess> = &SAME_PROCESS_COLD;
153static SAME_PROCESS_COLD: Mutex<SameProcess> = Mutex::new(SameProcess::Not);
154
155fn same_process() -> &'static Mutex<SameProcess> {
156 unsafe { *std::ptr::addr_of!(SAME_PROCESS) }
158}
159
160pub struct StaticPatch {
163 same_process: *const Mutex<SameProcess>,
164 tracing: tracing_shared::SharedLogger,
165}
166impl StaticPatch {
167 pub fn capture() -> Self {
169 Self {
170 same_process: same_process(),
171 tracing: tracing_shared::SharedLogger::new(),
172 }
173 }
174
175 pub unsafe fn install(&self) {
181 unsafe {
183 *std::ptr::addr_of_mut!(SAME_PROCESS) = &*self.same_process;
184 }
185 self.tracing.install();
186 }
187}