Created
March 20, 2025 04:05
-
-
Save rrbutani/7766333bb7f3815e99719ccbc10ab04c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use std::{ | |
| cell::UnsafeCell, | |
| marker::PhantomData, | |
| sync::{ | |
| OnceLock, | |
| mpsc::{Receiver, Sender, channel}, | |
| }, | |
| }; | |
| // initialized from thread that can call ui stuff | |
| static UI_THREAD_TX: OnceLock<Sender<ReqWrapper<Req, Resp>>> = OnceLock::new(); | |
| #[derive(Clone, Copy)] | |
| struct OpaqueId(usize); | |
| enum ReqWrapper<Req, Resp> { | |
| NewThread { opaque_id_oneshot: Sender<OpaqueId>, sender: Sender<Resp> }, | |
| ActualReq { id: OpaqueId, payload: Req }, | |
| } | |
| pub fn ui_thread() { | |
| let (tx, rx) = channel::<ReqWrapper<Req, Resp>>(); | |
| UI_THREAD_TX.set(tx).unwrap(); | |
| let mut ui = ScaryThreadLocalState { _do_not_send_me: PhantomData }; | |
| use ReqWrapper::*; | |
| let mut receipients = Vec::new(); | |
| while let Ok(msg) = rx.recv() { | |
| match msg { | |
| NewThread { opaque_id_oneshot, sender } => { | |
| let id = OpaqueId(receipients.len()); | |
| receipients.push(sender); | |
| opaque_id_oneshot.send(id).unwrap(); // maybe don't unwrap? | |
| } | |
| ActualReq { id, payload } => { | |
| // also amenable to automation... | |
| let resp = match payload { | |
| Req::GetScreenSize => Resp::GetScreenSize(ui.get_screen_size()), | |
| }; | |
| receipients[id.0].send(resp).unwrap(); // ditto | |
| } | |
| } | |
| } | |
| // todo: this thread should never exit... | |
| } | |
| // directly maps to function args/ret types (amenable to automation...) | |
| enum Req { | |
| GetScreenSize, | |
| } | |
| #[derive(Debug)] | |
| enum Resp { | |
| GetScreenSize((usize, usize)), | |
| } | |
| struct ScaryThreadLocalState { | |
| _do_not_send_me: PhantomData<UnsafeCell<()>>, | |
| } | |
| static_assertions::assert_not_impl_all!(ScaryThreadLocalState: Send, Sync); | |
| impl ScaryThreadLocalState { | |
| fn get_screen_size(&mut self) -> (usize, usize) { | |
| (0, 0) // todo | |
| } | |
| } | |
| thread_local! { | |
| static BIDIR_UI_THREAD_CHANNEL: (Sender<ReqWrapper<Req, Resp>>, OpaqueId, Receiver<Resp>) = { | |
| let tx = UI_THREAD_TX.get().expect("ui thread already initialized"); | |
| let (send_opaque_id, recv_opaque_id) = channel(); | |
| let (send_msg, recv_msg) = channel(); | |
| tx.send(ReqWrapper::NewThread { opaque_id_oneshot: send_opaque_id, sender: send_msg }).unwrap(); | |
| let opaque_id = recv_opaque_id.recv().unwrap(); | |
| (tx.clone(), opaque_id, recv_msg) | |
| }; | |
| } | |
| // "public" interface; for use by other threads | |
| // | |
| // ... also a job for macros | |
| pub mod ui { | |
| use crate::{BIDIR_UI_THREAD_CHANNEL, Req, ReqWrapper, Resp}; | |
| pub fn get_screen_size() -> (usize, usize) { | |
| BIDIR_UI_THREAD_CHANNEL.with(|(tx, id, rx)| { | |
| tx.send(ReqWrapper::ActualReq { id: *id, payload: Req::GetScreenSize }) | |
| .unwrap(); | |
| match rx.recv().unwrap() { | |
| Resp::GetScreenSize(ret) => ret, | |
| other => panic!("unexpected response: {other:?}"), | |
| } | |
| }) | |
| } | |
| } | |
| fn main() { | |
| println!("Hello, world!"); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment