Skip to content

Instantly share code, notes, and snippets.

@rrbutani
Created March 20, 2025 04:05
Show Gist options
  • Select an option

  • Save rrbutani/7766333bb7f3815e99719ccbc10ab04c to your computer and use it in GitHub Desktop.

Select an option

Save rrbutani/7766333bb7f3815e99719ccbc10ab04c to your computer and use it in GitHub Desktop.
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