Skip to content

Commit eae03d9

Browse files
authored
chore(file-handle): Replace FileHandle futures with oneshot channel (#313)
1 parent 9d3869e commit eae03d9

1 file changed

Lines changed: 55 additions & 108 deletions

File tree

src/file_handle/native.rs

Lines changed: 55 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,9 @@
11
use std::{
2-
future::Future,
2+
io,
33
path::{Path, PathBuf},
4-
pin::Pin,
5-
sync::{Arc, Mutex},
6-
task::{Context, Poll, Waker},
74
};
85

9-
#[derive(Default)]
10-
struct ReaderState {
11-
res: Option<std::io::Result<Vec<u8>>>,
12-
waker: Option<Waker>,
13-
}
14-
15-
struct Reader {
16-
state: Arc<Mutex<ReaderState>>,
17-
}
18-
19-
impl Reader {
20-
fn new(path: &Path) -> Self {
21-
let state: Arc<Mutex<ReaderState>> = Arc::new(Mutex::new(Default::default()));
22-
23-
{
24-
let path = path.to_owned();
25-
let state = state.clone();
26-
std::thread::Builder::new()
27-
.name("rfd_file_read".into())
28-
.spawn(move || {
29-
let res = std::fs::read(path);
30-
31-
let mut state = state.lock().unwrap();
32-
state.res.replace(res);
33-
34-
if let Some(waker) = state.waker.take() {
35-
waker.wake();
36-
}
37-
})
38-
.unwrap();
39-
}
40-
41-
Self { state }
42-
}
43-
}
44-
45-
impl Future for Reader {
46-
type Output = Vec<u8>;
47-
48-
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
49-
let mut state = self.state.lock().unwrap();
50-
if let Some(res) = state.res.take() {
51-
Poll::Ready(res.unwrap())
52-
} else {
53-
state.waker.replace(ctx.waker().clone());
54-
Poll::Pending
55-
}
56-
}
57-
}
58-
59-
struct WriterState {
60-
waker: Option<Waker>,
61-
res: Option<std::io::Result<()>>,
62-
}
63-
64-
struct Writer {
65-
state: Arc<Mutex<WriterState>>,
66-
}
67-
68-
impl Writer {
69-
fn new(path: &Path, bytes: &[u8]) -> Self {
70-
let state = Arc::new(Mutex::new(WriterState {
71-
waker: None,
72-
res: None,
73-
}));
74-
75-
{
76-
let path = path.to_owned();
77-
let bytes = bytes.to_owned();
78-
let state = state.clone();
79-
std::thread::Builder::new()
80-
.name("rfd_file_write".into())
81-
.spawn(move || {
82-
let res = std::fs::write(path, bytes);
83-
84-
let mut state = state.lock().unwrap();
85-
state.res.replace(res);
86-
87-
if let Some(waker) = state.waker.take() {
88-
waker.wake();
89-
}
90-
})
91-
.unwrap();
92-
}
93-
94-
Self { state }
95-
}
96-
}
97-
98-
impl Future for Writer {
99-
type Output = std::io::Result<()>;
100-
101-
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
102-
let mut state = self.state.lock().unwrap();
103-
if let Some(res) = state.res.take() {
104-
Poll::Ready(res)
105-
} else {
106-
state.waker.replace(ctx.waker().clone());
107-
Poll::Pending
108-
}
109-
}
110-
}
6+
use super::super::oneshot;
1117

1128
/// FileHandle is a way of abstracting over a file returned by a dialog
1139
#[derive(Clone)]
@@ -143,7 +39,22 @@ impl FileHandle {
14339
///
14440
/// `This fn exists solely to keep native api in pair with async only web api.`
14541
pub async fn read(&self) -> Vec<u8> {
146-
Reader::new(&self.0).await
42+
let (tx, rx) = oneshot::channel();
43+
let path = self.0.clone();
44+
45+
std::thread::Builder::new()
46+
.name("rfd_file_read".into())
47+
.spawn(move || {
48+
tx.send(std::fs::read(path)).unwrap();
49+
})
50+
.unwrap();
51+
52+
match rx.await {
53+
Ok(res) => res,
54+
Err(_) => Err(io::Error::other("Read tread panicked")),
55+
}
56+
// TODO: Move to io::Result
57+
.unwrap()
14758
}
14859

14960
/// Writes a file asynchronously.
@@ -152,7 +63,22 @@ impl FileHandle {
15263
///
15364
/// `This fn exists solely to keep native api in pair with async only web api.`
15465
pub async fn write(&self, data: &[u8]) -> std::io::Result<()> {
155-
Writer::new(&self.0, data).await
66+
let (tx, rx) = oneshot::channel();
67+
68+
let path = self.0.clone();
69+
let bytes = data.to_owned();
70+
71+
std::thread::Builder::new()
72+
.name("rfd_file_write".into())
73+
.spawn(move || {
74+
tx.send(std::fs::write(path, bytes)).unwrap();
75+
})
76+
.unwrap();
77+
78+
match rx.await {
79+
Ok(res) => res,
80+
Err(_) => Err(io::Error::other("Write tread panicked")),
81+
}
15682
}
15783

15884
/// Unwraps a `FileHandle` and returns inner type.
@@ -193,3 +119,24 @@ impl From<&FileHandle> for PathBuf {
193119
PathBuf::from(file_handle.path())
194120
}
195121
}
122+
123+
#[cfg(test)]
124+
mod tests {
125+
use super::*;
126+
127+
#[cfg(target_family = "unix")]
128+
#[test]
129+
fn write_and_read() {
130+
futures::executor::block_on(async {
131+
let path = "/tmp/rfd_test_write_read.txt";
132+
let handle = FileHandle(path.into());
133+
134+
handle.write(b"Hello world").await.unwrap();
135+
let bytes = handle.read().await;
136+
137+
assert_eq!(bytes, b"Hello world");
138+
139+
std::fs::remove_file(path).unwrap();
140+
});
141+
}
142+
}

0 commit comments

Comments
 (0)