mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-01 21:13:09 +00:00
Bugfix/websockets (#2808)
* retry logic for init status * fix login flashing and sideload hanging * add logging * misc backend bugfixes * use closingObserver instead * always show reinstall button * go back to endWith * show error if sideload fails * refactor more watch channels * navigate to services page on sideload complete * handle error closure events properly * handle error scenario better in sideload websocket * remove a clone --------- Co-authored-by: Matt Hill <mattnine@protonmail.com>
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Poll, Waker};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SyncMutex<T>(std::sync::Mutex<T>);
|
||||
impl<T> SyncMutex<T> {
|
||||
@@ -11,3 +15,140 @@ impl<T> SyncMutex<T> {
|
||||
f(&*self.0.lock().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
struct WatchShared<T> {
|
||||
version: u64,
|
||||
data: T,
|
||||
wakers: Vec<Waker>,
|
||||
}
|
||||
impl<T> WatchShared<T> {
|
||||
fn modified(&mut self) {
|
||||
self.version += 1;
|
||||
for waker in self.wakers.drain(..) {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project::pin_project]
|
||||
pub struct Watch<T> {
|
||||
shared: Arc<SyncMutex<WatchShared<T>>>,
|
||||
version: u64,
|
||||
}
|
||||
impl<T> Clone for Watch<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
shared: self.shared.clone(),
|
||||
version: self.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> Watch<T> {
|
||||
pub fn new(init: T) -> Self {
|
||||
Self {
|
||||
shared: Arc::new(SyncMutex::new(WatchShared {
|
||||
version: 1,
|
||||
data: init,
|
||||
wakers: Vec::new(),
|
||||
})),
|
||||
version: 0,
|
||||
}
|
||||
}
|
||||
pub fn clone_unseen(&self) -> Self {
|
||||
Self {
|
||||
shared: self.shared.clone(),
|
||||
version: 0,
|
||||
}
|
||||
}
|
||||
pub fn poll_changed(&mut self, cx: &mut std::task::Context<'_>) -> Poll<()> {
|
||||
self.shared.mutate(|shared| {
|
||||
if shared.version != self.version {
|
||||
self.version = shared.version;
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
let waker = cx.waker();
|
||||
if !shared.wakers.iter().any(|w| w.will_wake(waker)) {
|
||||
shared.wakers.push(waker.clone());
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
}
|
||||
pub async fn changed(&mut self) {
|
||||
futures::future::poll_fn(|cx| self.poll_changed(cx)).await
|
||||
}
|
||||
pub fn send_if_modified<F: FnOnce(&mut T) -> bool>(&self, modify: F) -> bool {
|
||||
self.shared.mutate(|shared| {
|
||||
let changed = modify(&mut shared.data);
|
||||
if changed {
|
||||
shared.modified();
|
||||
}
|
||||
changed
|
||||
})
|
||||
}
|
||||
pub fn send_modify<U, F: FnOnce(&mut T) -> U>(&self, modify: F) -> U {
|
||||
self.shared.mutate(|shared| {
|
||||
let res = modify(&mut shared.data);
|
||||
shared.modified();
|
||||
res
|
||||
})
|
||||
}
|
||||
pub fn send_replace(&self, new: T) -> T {
|
||||
self.send_modify(|a| std::mem::replace(a, new))
|
||||
}
|
||||
pub fn send(&self, new: T) {
|
||||
self.send_replace(new);
|
||||
}
|
||||
pub fn mark_changed(&self) {
|
||||
self.shared.mutate(|shared| shared.modified())
|
||||
}
|
||||
pub fn mark_unseen(&mut self) {
|
||||
self.version = 0;
|
||||
}
|
||||
pub fn mark_seen(&mut self) {
|
||||
self.shared.peek(|shared| {
|
||||
self.version = shared.version;
|
||||
})
|
||||
}
|
||||
pub fn peek<U, F: FnOnce(&T) -> U>(&self, f: F) -> U {
|
||||
self.shared.peek(|shared| f(&shared.data))
|
||||
}
|
||||
pub fn peek_and_mark_seen<U, F: FnOnce(&T) -> U>(&mut self, f: F) -> U {
|
||||
self.shared.peek(|shared| {
|
||||
self.version = shared.version;
|
||||
f(&shared.data)
|
||||
})
|
||||
}
|
||||
pub fn peek_mut<U, F: FnOnce(&mut T) -> U>(&self, f: F) -> U {
|
||||
self.shared.mutate(|shared| f(&mut shared.data))
|
||||
}
|
||||
}
|
||||
impl<T: Clone> Watch<T> {
|
||||
pub fn read(&self) -> T {
|
||||
self.peek(|a| a.clone())
|
||||
}
|
||||
}
|
||||
impl<T: Clone> futures::Stream for Watch<T> {
|
||||
type Item = T;
|
||||
fn poll_next(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> Poll<Option<Self::Item>> {
|
||||
let this = self.project();
|
||||
this.shared.mutate(|shared| {
|
||||
if shared.version != *this.version {
|
||||
*this.version = shared.version;
|
||||
Poll::Ready(Some(shared.data.clone()))
|
||||
} else {
|
||||
let waker = cx.waker();
|
||||
if !shared.wakers.iter().any(|w| w.will_wake(waker)) {
|
||||
shared.wakers.push(waker.clone());
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(1, None)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user