tokio/fs/
write.rs

1#[cfg(all(
2    tokio_unstable,
3    feature = "io-uring",
4    feature = "rt",
5    feature = "fs",
6    target_os = "linux"
7))]
8use crate::io::blocking;
9use crate::{fs::asyncify, util::as_ref::OwnedBuf};
10
11use std::{io, path::Path};
12
13/// Creates a future that will open a file for writing and write the entire
14/// contents of `contents` to it.
15///
16/// This is the async equivalent of [`std::fs::write`][std].
17///
18/// This operation is implemented by running the equivalent blocking operation
19/// on a separate thread pool using [`spawn_blocking`].
20///
21/// [`spawn_blocking`]: crate::task::spawn_blocking
22/// [std]: fn@std::fs::write
23///
24/// # Examples
25///
26/// ```no_run
27/// use tokio::fs;
28///
29/// # async fn dox() -> std::io::Result<()> {
30/// fs::write("foo.txt", b"Hello world!").await?;
31/// # Ok(())
32/// # }
33/// ```
34pub async fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> io::Result<()> {
35    let path = path.as_ref();
36
37    #[cfg(all(
38        tokio_unstable,
39        feature = "io-uring",
40        feature = "rt",
41        feature = "fs",
42        target_os = "linux"
43    ))]
44    {
45        let handle = crate::runtime::Handle::current();
46        let driver_handle = handle.inner.driver().io();
47        if driver_handle.check_and_init()? {
48            let mut buf = blocking::Buf::with_capacity(contents.as_ref().len());
49            buf.copy_from(contents.as_ref(), contents.as_ref().len());
50            return write_uring(path, buf).await;
51        }
52    }
53
54    let contents = crate::util::as_ref::upgrade(contents);
55    write_spawn_blocking(path, contents).await
56}
57
58#[cfg(all(
59    tokio_unstable,
60    feature = "io-uring",
61    feature = "rt",
62    feature = "fs",
63    target_os = "linux"
64))]
65async fn write_uring(path: &Path, mut buf: blocking::Buf) -> io::Result<()> {
66    use crate::{fs::OpenOptions, io::uring::utils::ArcFd, runtime::driver::op::Op};
67    use std::sync::Arc;
68
69    let file = OpenOptions::new()
70        .write(true)
71        .create(true)
72        .truncate(true)
73        .open(path)
74        .await?;
75
76    let mut fd: ArcFd = Arc::new(
77        file.try_into_std()
78            .expect("unexpected in-flight operation detected"),
79    );
80
81    let mut file_offset: u64 = 0;
82    while !buf.is_empty() {
83        let (n, _buf, _fd) = Op::write_at(fd, buf, file_offset).await;
84        // TODO: handle EINT here
85        let n = n?;
86        if n == 0 {
87            return Err(io::ErrorKind::WriteZero.into());
88        }
89
90        buf = _buf;
91        fd = _fd;
92        file_offset += n as u64;
93    }
94
95    Ok(())
96}
97
98async fn write_spawn_blocking(path: &Path, contents: OwnedBuf) -> io::Result<()> {
99    let path = path.to_owned();
100    asyncify(move || std::fs::write(path, contents)).await
101}