From f2271c10377c6f873ecd376631b5dea189b7a15f Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Tue, 10 Mar 2026 22:33:04 +0300 Subject: [PATCH] Composed gui, data and network --- app/Cargo.toml | 3 +- app/src/main.rs | 47 +++++--------- data/src/lib.rs | 6 +- data/src/synchronized/iterator.rs | 28 ++++++++ data/src/synchronized/mod.rs | 89 ++++++++++++++++++++++++++ data/src/synchronized/rw_lock.rs | 103 ++++++++++++++++++++++++++++++ data/src/unprotected.rs | 16 +++-- gui/abstract/src/lib.rs | 8 +-- network/abstract/src/lib.rs | 6 +- 9 files changed, 262 insertions(+), 44 deletions(-) create mode 100644 data/src/synchronized/iterator.rs create mode 100644 data/src/synchronized/mod.rs create mode 100644 data/src/synchronized/rw_lock.rs diff --git a/app/Cargo.toml b/app/Cargo.toml index 1f35c45..ddf4e0b 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -8,7 +8,8 @@ workspace = true [dependencies] bgtu-networks-2-network-abstract = { path = "../network/abstract" } -bgtu-networks-2-gui-abstract = { path = "../gui/abstract" } +bgtu-networks-2-gui-egui = { path = "../gui/egui" } +bgtu-networks-2-data = { path = "../data" } [target.'cfg(windows)'.dependencies] bgtu-networks-2-network-windows = { path = "../network/windows" } \ No newline at end of file diff --git a/app/src/main.rs b/app/src/main.rs index 4ddb190..23fc0b2 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -1,8 +1,9 @@ -use bgtu_networks_2_network_abstract::{Address, NetworkContext, NetworkScope, ServersContext}; +use bgtu_networks_2_data::SynchronizedServersStorage; +use bgtu_networks_2_network_abstract::{NetworkContext, NetworkScope}; use bgtu_networks_2_network_windows::winsocks_scope_2_2; -use bgtu_networks_2_gui_abstract::ServersStorage; - -// mod data; +use std::sync::Arc; +use std::thread; +use bgtu_networks_2_gui_egui::run_eframe_gui; fn main() { winsocks_scope_2_2(NetworkMain {}); @@ -14,34 +15,16 @@ impl NetworkScope for NetworkMain { type R = (); fn run(self, network: &mut Ctx) -> Self::R { - let mut servers = Servers2NetworkCtxTestImpl { - loopback: Ctx::Address::parse("127.0.0.1").unwrap(), - ros_com_nadzor: Ctx::Address::parse("ya.ru").unwrap(), - }; + let mut storage = Arc::new(SynchronizedServersStorage::::new(10)); + + thread::scope(|s| { + s.spawn(|| { + Ctx::run_ping_eventloop(&mut storage.as_ref().new_network_ctx()); + }); + + run_eframe_gui(&mut storage.new_gui_ctx()); + }); + - Ctx::run_ping_eventloop(&mut servers) } } - -struct Servers2NetworkCtxTestImpl { - loopback: A, - ros_com_nadzor: A, -} - -impl ServersContext for Servers2NetworkCtxTestImpl { - fn start_measuring(&mut self) -> (u64, impl Iterator) { - return ( - 0, - [self.loopback.clone(), self.ros_com_nadzor.clone()].into_iter(), - ); - } - - fn report_ping(&mut self, addr: A, id: u64, ping_ms: u128) { - println!("{} {ping_ms} ms", addr.to_string()) - } - - fn report_error(&mut self, addr: A, id: u64) { - println!("{} err", addr.to_string()) - - } -} \ No newline at end of file diff --git a/data/src/lib.rs b/data/src/lib.rs index 429d802..33d24c0 100644 --- a/data/src/lib.rs +++ b/data/src/lib.rs @@ -1,2 +1,6 @@ mod cycled_buffer; -mod unprotected; \ No newline at end of file +mod unprotected; +mod synchronized; + +pub use unprotected::ServersStorage; +pub use synchronized::SynchronizedServersStorage; \ No newline at end of file diff --git a/data/src/synchronized/iterator.rs b/data/src/synchronized/iterator.rs new file mode 100644 index 0000000..f18db50 --- /dev/null +++ b/data/src/synchronized/iterator.rs @@ -0,0 +1,28 @@ +use super::rw_lock::LockReadGuard; + +pub(super) struct SyncedIterator<'g, It: Iterator> { + it: Option<(It, LockReadGuard<'g>)>, +} +impl<'g, It: Iterator> SyncedIterator<'g, It> { + pub fn wrap(it: It, guard: LockReadGuard<'g>) -> Self { + return Self { + it: Some((it, guard)), + }; + } +} +impl Iterator for SyncedIterator<'_, It> { + type Item = It::Item; + + fn next(&mut self) -> Option { + match &mut self.it { + None => return None, + Some((it, s)) => match it.next() { + Some(e) => return Some(e), + None => { + self.it = None; + return None; + } + }, + } + } +} diff --git a/data/src/synchronized/mod.rs b/data/src/synchronized/mod.rs new file mode 100644 index 0000000..8d4b344 --- /dev/null +++ b/data/src/synchronized/mod.rs @@ -0,0 +1,89 @@ +use crate::ServersStorage; +use crate::synchronized::iterator::SyncedIterator; +use bgtu_networks_2_gui_abstract::ServersStorage as ServersGuiCtx; +use bgtu_networks_2_network_abstract::{Address, ServersContext as ServersNetworkCtx}; +use std::cell::UnsafeCell; +use std::hash::Hash; +use std::ptr::NonNull; +mod iterator; + +mod rw_lock; +use rw_lock::CustomRwLock; + +pub struct SynchronizedServersStorage { + mutex: CustomRwLock, + data: UnsafeCell>, +} + + +unsafe impl Sync for SynchronizedServersStorage {} + +impl SynchronizedServersStorage { + pub fn new(history_capacity: usize) -> Self { + return Self { + mutex: CustomRwLock::new(), + data: UnsafeCell::new(ServersStorage::new(history_capacity)), + }; + } + + pub fn new_network_ctx(&self) -> impl ServersNetworkCtx { + return _Impl { data: self }; + } + + pub fn new_gui_ctx(&self) -> impl ServersGuiCtx
{ + return _Impl { data: self }; + } +} + +struct _Impl<'o, A: Address + Hash + Eq> { + data: &'o SynchronizedServersStorage, +} + +impl _Impl<'_, A> { + unsafe fn get_unprotected(&self) -> &mut ServersStorage { + return NonNull::new_unchecked(self.data.data.get()).as_mut(); + } +} + +impl ServersNetworkCtx for _Impl<'_, A> { + fn start_measuring(&mut self) -> (u64, impl Iterator) { + let w_lock_scope = self.data.mutex.write(); + let data = unsafe { self.get_unprotected() }.start_measuring(); + let r_lock_scope = w_lock_scope.write_to_read(); + return (data.0, SyncedIterator::wrap(data.1, r_lock_scope)); + } + + fn report_ping(&mut self, addr: A, id: u64, ping_ms: u128) { + let lock_scope = self.data.mutex.read(); + unsafe { self.get_unprotected() }.report_ping(addr, id, ping_ms); + } + + fn report_error(&mut self, addr: A, id: u64) { + let lock_scope = self.data.mutex.read(); + unsafe { self.get_unprotected() }.report_error(addr, id); + } +} + +impl ServersGuiCtx for _Impl<'_, A> { + type Address = A; + + fn add_server(&mut self, addr: Self::Address, memo: String) { + let w_lock_scope = self.data.mutex.write(); + unsafe { self.get_unprotected() }.add_server(addr, memo); + } + + fn remove_server(&mut self, addr: &Self::Address) { + let w_lock_scope = self.data.mutex.write(); + unsafe { self.get_unprotected() }.remove_server(addr); + } + + fn iter_servers( + &self, + ) -> impl Iterator>)> { + let r_lock_scope = self.data.mutex.read(); + return SyncedIterator::wrap( + unsafe { self.get_unprotected() }.iter_servers(), + r_lock_scope, + ); + } +} diff --git a/data/src/synchronized/rw_lock.rs b/data/src/synchronized/rw_lock.rs new file mode 100644 index 0000000..a5dfc51 --- /dev/null +++ b/data/src/synchronized/rw_lock.rs @@ -0,0 +1,103 @@ +use std::mem::ManuallyDrop; +use std::ops::{Deref, DerefMut}; +use std::sync::{Condvar, Mutex}; + +enum RwLockState { + Free, + Write, + Read(usize), +} + +pub(super) struct CustomRwLock { + counter: Mutex, + condvar: Condvar, +} + +impl CustomRwLock { + pub fn new() -> Self { + return Self { + counter: Mutex::new(RwLockState::Free), + condvar: Condvar::new(), + }; + } + + pub fn write(&self) -> LockWriteGuard<'_> { + let mut lock_scope = self.counter.lock().unwrap(); + loop { + match lock_scope.deref() { + RwLockState::Free => { + *lock_scope.deref_mut() = RwLockState::Write; + return LockWriteGuard { parent: self }; + } + RwLockState::Write | RwLockState::Read(_) => { + lock_scope = self.condvar.wait(lock_scope).unwrap(); + continue; + } + } + } + } + + pub fn read(&self) -> LockReadGuard<'_> { + let mut lock_scope = self.counter.lock().unwrap(); + loop { + match lock_scope.deref() { + RwLockState::Free => { + *lock_scope.deref_mut() = RwLockState::Read(1); + return LockReadGuard { parent: self }; + } + RwLockState::Read(cnt) => { + *lock_scope.deref_mut() = RwLockState::Read(*cnt + 1); + return LockReadGuard { parent: self }; + } + RwLockState::Write => { + lock_scope = self.condvar.wait(lock_scope).unwrap(); + continue; + } + } + } + } +} + +pub(super) struct LockWriteGuard<'p> { + parent: &'p CustomRwLock, +} + +impl<'p> LockWriteGuard<'p> { + pub fn write_to_read(self) -> LockReadGuard<'p> { + *self.parent.counter.lock().unwrap().deref_mut() = RwLockState::Read(1); + self.parent.condvar.notify_all(); + let new_guard = LockReadGuard { + parent: self.parent, + }; + let _ = ManuallyDrop::new(self); + return new_guard; + } +} + +impl Drop for LockWriteGuard<'_> { + fn drop(&mut self) { + *self.parent.counter.lock().unwrap().deref_mut() = RwLockState::Free; + self.parent.condvar.notify_all(); + } +} + +pub(super) struct LockReadGuard<'p> { + parent: &'p CustomRwLock, +} + +impl Drop for LockReadGuard<'_> { + fn drop(&mut self) { + let mut lock_scope = self.parent.counter.lock().unwrap(); + match lock_scope.deref() { + RwLockState::Free | RwLockState::Write => panic!("Unexpected state"), + RwLockState::Read(cnt) => { + if *cnt - 1 == 0 { + *lock_scope.deref_mut() = RwLockState::Free; + self.parent.condvar.notify_all(); + } else { + *lock_scope.deref_mut() = RwLockState::Read(*cnt - 1); + } + } + } + } +} diff --git a/data/src/unprotected.rs b/data/src/unprotected.rs index 11ea8fd..3923aa2 100644 --- a/data/src/unprotected.rs +++ b/data/src/unprotected.rs @@ -11,6 +11,14 @@ pub struct ServersStorage { } impl ServersStorage { + pub fn new(history_capacity: usize) -> Self { + return Self { + history_capacity, + last_stored_req_id: 0, + map: HashMap::new(), + }; + } + fn _get_offset(&self, req_id: u64) -> usize { return self.last_stored_req_id.wrapping_sub(req_id) as usize; } @@ -37,9 +45,9 @@ impl ServersNetworkCtx for ServersStorage { } impl ServersGuiCtx for ServersStorage { - type A = A; + type Address = A; - fn add_server(&mut self, addr: Self::A, memo: String) { + fn add_server(&mut self, addr: Self::Address, memo: String) { if self.map.get(&addr).is_some() { todo!() }; @@ -48,13 +56,13 @@ impl ServersGuiCtx for ServersStorage { .insert(addr, (memo, CycledBuffer::new(self.history_capacity))); } - fn remove_server(&mut self, addr: &Self::A) { + fn remove_server(&mut self, addr: &Self::Address) { self.map.remove(addr); } fn iter_servers( &self, - ) -> impl Iterator>)> { + ) -> impl Iterator>)> { return self .map .iter() diff --git a/gui/abstract/src/lib.rs b/gui/abstract/src/lib.rs index c2be72e..3b2226c 100644 --- a/gui/abstract/src/lib.rs +++ b/gui/abstract/src/lib.rs @@ -1,11 +1,11 @@ use bgtu_networks_2_network_abstract::Address; pub trait ServersStorage { - type A: Address; + type Address: Address; - fn add_server(&mut self, addr: Self::A, memo: String); - fn remove_server(&mut self, addr: &Self::A); + fn add_server(&mut self, addr: Self::Address, memo: String); + fn remove_server(&mut self, addr: &Self::Address); // type TimeIterator: Iterator>; - fn iter_servers(&self) -> impl Iterator>)>; + fn iter_servers(&self) -> impl Iterator>)>; } diff --git a/network/abstract/src/lib.rs b/network/abstract/src/lib.rs index 66c6b32..7846f9e 100644 --- a/network/abstract/src/lib.rs +++ b/network/abstract/src/lib.rs @@ -1,10 +1,12 @@ -pub trait Address: Sized + Clone { +use std::hash::Hash; + +pub trait Address: Sized + Clone + Send { fn parse(raw: &str) -> Result; fn to_string(&self) -> String; } pub trait NetworkContext { - type Address: Address ; + type Address: Address + Hash + Eq ; fn run_ping_eventloop(ctx: &mut impl ServersContext); }