sysreq/clients/
resolve.rs

1use super::SystemHttpClient;
2use crate::Error;
3use std::{
4	cell::UnsafeCell,
5	sync::atomic::{self, AtomicU8},
6	time::Duration,
7};
8
9const PENDING: u8 = 0;
10const BUSY: u8 = 1;
11const READY: u8 = 2;
12
13struct GlobalResolvedSystemHttpClient {
14	state: AtomicU8,
15	value: UnsafeCell<Option<SystemHttpClient>>,
16}
17impl GlobalResolvedSystemHttpClient {
18	fn get() -> Option<SystemHttpClient> {
19		static HTTP_CLIENT: GlobalResolvedSystemHttpClient = GlobalResolvedSystemHttpClient {
20			state: AtomicU8::new(PENDING),
21			value: UnsafeCell::new(None),
22		};
23
24		let mut spin_wait = Duration::from_millis(50);
25		loop {
26			match HTTP_CLIENT.state.fetch_max(BUSY, atomic::Ordering::SeqCst) {
27				BUSY => (),
28				PENDING => break,
29				READY => return unsafe { *HTTP_CLIENT.value.get() },
30				_ => unreachable!(),
31			}
32			std::thread::sleep(spin_wait);
33			spin_wait = Duration::max(spin_wait * 2, Duration::from_secs(2));
34		}
35
36		let resolved = SystemHttpClient::resolve();
37
38		unsafe { *HTTP_CLIENT.value.get() = resolved };
39
40		HTTP_CLIENT.state.store(READY, atomic::Ordering::Release);
41
42		resolved
43	}
44}
45unsafe impl Sync for GlobalResolvedSystemHttpClient {}
46
47pub(crate) fn resolve() -> Result<SystemHttpClient, Error> {
48	match GlobalResolvedSystemHttpClient::get() {
49		Some(client) => Ok(client),
50		None => Err(Error::SystemHTTPClientNotFound),
51	}
52}
53
54#[must_use]
55/// Returns whether the system has a compatible HTTP client installed
56pub fn installed() -> bool {
57	GlobalResolvedSystemHttpClient::get().is_some()
58}
59
60#[must_use]
61/// Returns the system's compatible HTTP client used for requests, if one is installed.
62pub fn http_client() -> Option<SystemHttpClient> {
63	GlobalResolvedSystemHttpClient::get()
64}