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
//! Broker-level advertised price `P`, in credits/hour.
//!
//! Thread-safe, lock-free storage so the price can be shared into the tick
//! loop and request handlers and mutated live via `zc price <value>` / the
//! local-only `POST /price` control endpoint.
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use super::worker::default_price_per_hour;
/// A broker's advertised price (credits/hour), shared and mutable across
/// threads via `Arc<AtomicU64>` storing the `f64` bit pattern.
#[derive(Clone)]
pub struct BrokerPrice(Arc<AtomicU64>);
impl BrokerPrice {
/// Create a new `BrokerPrice` initialized to `initial`.
pub fn new(initial: f64) -> Self {
Self(Arc::new(AtomicU64::new(initial.to_bits())))
}
/// Current price, in credits/hour.
pub fn get(&self) -> f64 {
f64::from_bits(self.0.load(Ordering::Relaxed))
}
/// Set the price. Non-finite values (NaN/inf) are ignored (no-op);
/// negative values are clamped to `0.0`.
pub fn set(&self, price: f64) {
if !price.is_finite() {
return;
}
let clamped = price.max(0.0);
self.0.store(clamped.to_bits(), Ordering::Relaxed);
}
/// Build from `ZAKURO_BROKER_PRICE` (parsed as a finite, non-negative
/// `f64`), falling back to `default_price_per_hour()` (3.6) when the env
/// var is unset, unparsable, non-finite, or negative.
pub fn from_env_or_default() -> Self {
let price = std::env::var("ZAKURO_BROKER_PRICE")
.ok()
.and_then(|v| v.parse::<f64>().ok())
.filter(|v| v.is_finite() && *v >= 0.0)
.unwrap_or_else(default_price_per_hour);
Self::new(price)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn broker_price_set_get_and_rejects_bad() {
let p = BrokerPrice::new(3.6);
assert_eq!(p.get(), 3.6);
p.set(5.0);
assert_eq!(p.get(), 5.0);
p.set(f64::NAN); // ignored
assert_eq!(p.get(), 5.0);
p.set(-1.0); // clamped to 0
assert_eq!(p.get(), 0.0);
}
#[test]
fn broker_price_from_env_default() {
// no env var set in this test process -> default 3.6
let p = BrokerPrice::from_env_or_default();
assert!(p.get() > 0.0);
}
}