mindustry logic execution, map- and schematic- parsing and rendering
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::ptr;
use std::sync::atomic::{AtomicU8, Ordering};

const STATE_INIT: u8 = 0;
const STATE_LOCKED: u8 = STATE_INIT + 1;
const STATE_READY: u8 = STATE_LOCKED + 1;

pub struct OnceCell<T> {
    value: UnsafeCell<MaybeUninit<T>>,
    state: AtomicU8,
}

impl<T> OnceCell<T> {
    #[must_use]
    pub const fn new() -> Self {
        Self {
            value: UnsafeCell::new(MaybeUninit::uninit()),
            state: AtomicU8::new(STATE_INIT),
        }
    }

    pub const fn new_init(value: T) -> Self {
        Self {
            value: UnsafeCell::new(MaybeUninit::new(value)),
            state: AtomicU8::new(STATE_READY),
        }
    }

    pub fn get(&self) -> Option<&T> {
        if self.state.load(Ordering::Acquire) == STATE_READY {
            // SAFETY: won't be overwritten for the lifetime of this reference
            Some(unsafe { (*self.value.get()).assume_init_ref() })
        } else {
            None
        }
    }

    pub fn get_or_wait(&self) -> Option<&T> {
        loop {
            match self.state.load(Ordering::Acquire) {
                STATE_INIT => return None,
                STATE_LOCKED => (), // continue
                STATE_READY => return Some(unsafe { (*self.value.get()).assume_init_ref() }),
                x => unreachable!("invalid state {x}"),
            }
        }
    }

    pub fn get_or_init<F: Fn() -> T>(&self, init: F) -> &T {
        loop {
            match self.state.compare_exchange(
                STATE_INIT,
                STATE_LOCKED,
                Ordering::AcqRel,
                Ordering::Acquire,
            ) {
                Ok(..) => {
                    let value = init();
                    let written = &*unsafe { &mut *self.value.get() }.write(value);
                    self.state.store(STATE_READY, Ordering::Release);
                    return written;
                }
                Err(STATE_READY) => return unsafe { (*self.value.get()).assume_init_ref() },
                Err(..) => (), // locked or spurious failure
            }
        }
    }

    pub fn set(&self, value: T) -> Result<&T, T> {
        // don't set state to STATE_READY on success because we have to release afterward anyway
        match self.state.compare_exchange(
            STATE_INIT,
            STATE_LOCKED,
            Ordering::AcqRel,
            Ordering::Acquire,
        ) {
            Ok(..) => {
                // SAFETY: unique because only one thread can lock the atomic state
                let written = &*unsafe { &mut *self.value.get() }.write(value);
                self.state.store(STATE_READY, Ordering::Release);
                Ok(written)
            }
            // SAFETY: guaranteed to be initialized & protected by acquire ordering
            Err(STATE_READY) => return Ok(unsafe { (*self.value.get()).assume_init_ref() }),
            Err(..) => Err(value), // locked or spurious failure
        }
    }

    pub fn set_mut(&mut self, mut value: T) -> Result<Option<T>, T> {
        // don't set state to STATE_READY on success because we have to release afterward anyway
        match self.state.compare_exchange(
            STATE_INIT,
            STATE_LOCKED,
            Ordering::AcqRel,
            Ordering::Acquire,
        ) {
            Ok(..) => {
                self.value.get_mut().write(value);
                self.state.store(STATE_READY, Ordering::Release);
                Ok(None)
            }
            Err(STATE_READY) => {
                // SAFETY: guaranteed to be initialized & protected by acquire ordering
                std::mem::swap(
                    unsafe { self.value.get_mut().assume_init_mut() },
                    &mut value,
                );
                // ensure changes are visible to others acquiring the atomic state
                self.state.store(STATE_READY, Ordering::Release);
                // we've swapped the previous value into this variable
                Ok(Some(value))
            }
            Err(..) => Err(value), // locked or spurious failure
        }
    }

    pub fn into_inner(mut self) -> Option<T> {
        // must be atomic so potential writes during the drop see a valid state
        let inner = match self.state.load(Ordering::Acquire) {
            STATE_INIT => None,
            STATE_LOCKED => unreachable!("consumed cell during initialization"),
            // SAFETY: initialized & we'll forget about it afterwards
            STATE_READY => Some(unsafe { self.value.get_mut().assume_init_read() }),
            x => unreachable!("invalid state {x}"),
        };
        // SAFETY: just in case AtomicU8 has a drop handler
        unsafe {
            ptr::drop_in_place(std::ptr::addr_of_mut!(self.state));
        }
        std::mem::forget(self);
        inner
    }
}

impl<T> Default for OnceCell<T> {
    fn default() -> Self {
        OnceCell::new()
    }
}

impl<T> From<T> for OnceCell<T> {
    fn from(value: T) -> Self {
        OnceCell::new_init(value)
    }
}

impl<T> From<OnceCell<T>> for Option<T> {
    fn from(value: OnceCell<T>) -> Self {
        value.into_inner()
    }
}

impl<T> Drop for OnceCell<T> {
    fn drop(&mut self) {
        match *self.state.get_mut() {
            STATE_INIT => (),
            STATE_LOCKED => unreachable!("dropped cell during initialization"),
            // MaybeUninit requires us to manually drop the value
            STATE_READY => unsafe { self.value.get_mut().assume_init_drop() },
            x => unreachable!("invalid state {x}"),
        }
    }
}

unsafe impl<T: Send> Send for OnceCell<T> {}

unsafe impl<T: Sync> Sync for OnceCell<T> {}