diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2023-05-04 14:28:42 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-04 14:28:42 -0400 |
commit | 5270c43e412cc636cd9923182169d166d181f78a (patch) | |
tree | 640c90a70f7dd7bc91f5e942e1eaa5a7914ae46b /ext/io/lib.rs | |
parent | 4b645676d62fd595ecac47e24be1b83a3ba636c6 (diff) |
refactor(ext/fs): boxed deno_fs::FileSystem (#18945)
1. Boxed `File` and `FileSystem` to allow more easily passing this
through the CLI code (as shown within this pr).
2. `StdFileResource` is now `FileResource`. `FileResource` now contains
an `Rc<dyn File>`.
Diffstat (limited to 'ext/io/lib.rs')
-rw-r--r-- | ext/io/lib.rs | 623 |
1 files changed, 343 insertions, 280 deletions
diff --git a/ext/io/lib.rs b/ext/io/lib.rs index 73ce72578..49e4ab714 100644 --- a/ext/io/lib.rs +++ b/ext/io/lib.rs @@ -1,6 +1,5 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -use deno_core::error::resource_unavailable; use deno_core::error::AnyError; use deno_core::op; use deno_core::AsyncMutFuture; @@ -13,8 +12,12 @@ use deno_core::CancelTryFuture; use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; -use deno_core::ResourceId; use deno_core::TaskQueue; +use fs::FileResource; +use fs::FsError; +use fs::FsResult; +use fs::FsStat; +use fs3::FileExt; use once_cell::sync::Lazy; use std::borrow::Cow; use std::cell::RefCell; @@ -22,6 +25,7 @@ use std::fs::File as StdFile; use std::io; use std::io::ErrorKind; use std::io::Read; +use std::io::Seek; use std::io::Write; use std::rc::Rc; use tokio::io::AsyncRead; @@ -40,6 +44,8 @@ use winapi::um::processenv::GetStdHandle; #[cfg(windows)] use winapi::um::winbase; +pub mod fs; + // Store the stdio fd/handles in global statics in order to keep them // alive for the duration of the application since the last handle/fd // being dropped will close the corresponding pipe. @@ -89,39 +95,39 @@ deno_core::extension!(deno_io, if let Some(stdio) = options.stdio { let t = &mut state.resource_table; - let rid = t.add(StdFileResource::stdio( - match stdio.stdin { - StdioPipe::Inherit => StdFileResourceInner { - kind: StdFileResourceKind::Stdin, - file: STDIN_HANDLE.try_clone().unwrap(), - }, + let rid = t.add(fs::FileResource::new( + Rc::new(match stdio.stdin { + StdioPipe::Inherit => StdFileResourceInner::new( + StdFileResourceKind::Stdin, + STDIN_HANDLE.try_clone().unwrap(), + ), StdioPipe::File(pipe) => StdFileResourceInner::file(pipe), - }, - "stdin", + }), + "stdin".to_string(), )); assert_eq!(rid, 0, "stdin must have ResourceId 0"); - let rid = t.add(StdFileResource::stdio( - match stdio.stdout { - StdioPipe::Inherit => StdFileResourceInner { - kind: StdFileResourceKind::Stdout, - file: STDOUT_HANDLE.try_clone().unwrap(), - }, + let rid = t.add(FileResource::new( + Rc::new(match stdio.stdout { + StdioPipe::Inherit => StdFileResourceInner::new( + StdFileResourceKind::Stdout, + STDOUT_HANDLE.try_clone().unwrap(), + ), StdioPipe::File(pipe) => StdFileResourceInner::file(pipe), - }, - "stdout", + }), + "stdout".to_string(), )); assert_eq!(rid, 1, "stdout must have ResourceId 1"); - let rid = t.add(StdFileResource::stdio( - match stdio.stderr { - StdioPipe::Inherit => StdFileResourceInner { - kind: StdFileResourceKind::Stderr, - file: STDERR_HANDLE.try_clone().unwrap(), - }, + let rid = t.add(FileResource::new( + Rc::new(match stdio.stderr { + StdioPipe::Inherit => StdFileResourceInner::new( + StdFileResourceKind::Stderr, + STDERR_HANDLE.try_clone().unwrap(), + ), StdioPipe::File(pipe) => StdFileResourceInner::file(pipe), - }, - "stderr", + }), + "stderr".to_string(), )); assert_eq!(rid, 2, "stderr must have ResourceId 2"); } @@ -291,34 +297,88 @@ enum StdFileResourceKind { Stderr, } -struct StdFileResourceInner { +pub struct StdFileResourceInner { kind: StdFileResourceKind, - file: StdFile, + // We can't use an AsyncRefCell here because we need to allow + // access to the resource synchronously at any time and + // asynchronously one at a time in order + cell: RefCell<Option<StdFile>>, + // Used to keep async actions in order and only allow one + // to occur at a time + cell_async_task_queue: TaskQueue, } impl StdFileResourceInner { pub fn file(fs_file: StdFile) -> Self { + StdFileResourceInner::new(StdFileResourceKind::File, fs_file) + } + + fn new(kind: StdFileResourceKind, fs_file: StdFile) -> Self { StdFileResourceInner { - kind: StdFileResourceKind::File, - file: fs_file, + kind, + cell: RefCell::new(Some(fs_file)), + cell_async_task_queue: Default::default(), } } - pub fn with_file<R>(&mut self, f: impl FnOnce(&mut StdFile) -> R) -> R { - f(&mut self.file) + fn with_sync<F, R>(&self, action: F) -> FsResult<R> + where + F: FnOnce(&mut StdFile) -> FsResult<R>, + { + match self.cell.try_borrow_mut() { + Ok(mut cell) if cell.is_some() => action(cell.as_mut().unwrap()), + _ => Err(fs::FsError::FileBusy), + } } - pub fn try_clone(&self) -> Result<Self, std::io::Error> { - Ok(Self { - kind: self.kind, - file: self.file.try_clone()?, + async fn with_inner_blocking_task<F, R: 'static + Send>(&self, action: F) -> R + where + F: FnOnce(&mut StdFile) -> R + Send + 'static, + { + // we want to restrict this to one async action at a time + let _permit = self.cell_async_task_queue.acquire().await; + // we take the value out of the cell, use it on a blocking task, + // then put it back into the cell when we're done + let mut did_take = false; + let mut cell_value = { + let mut cell = self.cell.borrow_mut(); + match cell.as_mut().unwrap().try_clone().ok() { + Some(value) => value, + None => { + did_take = true; + cell.take().unwrap() + } + } + }; + let (cell_value, result) = tokio::task::spawn_blocking(move || { + let result = action(&mut cell_value); + (cell_value, result) }) + .await + .unwrap(); + + if did_take { + // put it back + self.cell.borrow_mut().replace(cell_value); + } + + result } - pub fn write_and_maybe_flush( - &mut self, - buf: &[u8], - ) -> Result<usize, AnyError> { + async fn with_blocking_task<F, R: 'static + Send>(&self, action: F) -> R + where + F: FnOnce() -> R + Send + 'static, + { + // we want to restrict this to one async action at a time + let _permit = self.cell_async_task_queue.acquire().await; + + tokio::task::spawn_blocking(action).await.unwrap() + } +} + +#[async_trait::async_trait(?Send)] +impl crate::fs::File for StdFileResourceInner { + fn write_sync(self: Rc<Self>, buf: &[u8]) -> FsResult<usize> { // Rust will line buffer and we don't want that behavior // (see https://github.com/denoland/deno/issues/948), so flush stdout and stderr. // Although an alternative solution could be to bypass Rust's std by @@ -326,7 +386,7 @@ impl StdFileResourceInner { // that we get solved for free by using Rust's stdio wrappers (see // std/src/sys/windows/stdio.rs in Rust's source code). match self.kind { - StdFileResourceKind::File => Ok(self.file.write(buf)?), + StdFileResourceKind::File => self.with_sync(|file| Ok(file.write(buf)?)), StdFileResourceKind::Stdin => { Err(Into::<std::io::Error>::into(ErrorKind::Unsupported).into()) } @@ -347,14 +407,22 @@ impl StdFileResourceInner { } } - pub fn write_all_and_maybe_flush( - &mut self, - buf: &[u8], - ) -> Result<(), AnyError> { - // this method exists instead of using a `Write` implementation - // so that we can acquire the locks once and do both actions + fn read_sync(self: Rc<Self>, buf: &mut [u8]) -> FsResult<usize> { + match self.kind { + StdFileResourceKind::File | StdFileResourceKind::Stdin => { + self.with_sync(|file| Ok(file.read(buf)?)) + } + StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => { + Err(FsError::NotSupported) + } + } + } + + fn write_all_sync(self: Rc<Self>, buf: &[u8]) -> FsResult<()> { match self.kind { - StdFileResourceKind::File => Ok(self.file.write_all(buf)?), + StdFileResourceKind::File => { + self.with_sync(|file| Ok(file.write_all(buf)?)) + } StdFileResourceKind::Stdin => { Err(Into::<std::io::Error>::into(ErrorKind::Unsupported).into()) } @@ -374,292 +442,292 @@ impl StdFileResourceInner { } } } -} - -impl Read for StdFileResourceInner { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { + async fn write_all(self: Rc<Self>, buf: BufView) -> FsResult<()> { match self.kind { - StdFileResourceKind::File | StdFileResourceKind::Stdin => { - self.file.read(buf) + StdFileResourceKind::File => { + self + .with_inner_blocking_task(move |file| Ok(file.write_all(&buf)?)) + .await } - StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => { - Err(ErrorKind::Unsupported.into()) + StdFileResourceKind::Stdin => { + Err(Into::<std::io::Error>::into(ErrorKind::Unsupported).into()) + } + StdFileResourceKind::Stdout => { + self + .with_blocking_task(move || { + // bypass the file and use std::io::stdout() + let mut stdout = std::io::stdout().lock(); + stdout.write_all(&buf)?; + stdout.flush()?; + Ok(()) + }) + .await + } + StdFileResourceKind::Stderr => { + self + .with_blocking_task(move || { + // bypass the file and use std::io::stderr() + let mut stderr = std::io::stderr().lock(); + stderr.write_all(&buf)?; + stderr.flush()?; + Ok(()) + }) + .await } } } -} -pub struct StdFileResource { - name: String, - // We can't use an AsyncRefCell here because we need to allow - // access to the resource synchronously at any time and - // asynchronously one at a time in order - cell: RefCell<Option<StdFileResourceInner>>, - // Used to keep async actions in order and only allow one - // to occur at a time - cell_async_task_queue: TaskQueue, -} - -impl StdFileResource { - fn stdio(inner: StdFileResourceInner, name: &str) -> Self { - Self { - cell: RefCell::new(Some(inner)), - cell_async_task_queue: Default::default(), - name: name.to_string(), + async fn write( + self: Rc<Self>, + view: BufView, + ) -> FsResult<deno_core::WriteOutcome> { + match self.kind { + StdFileResourceKind::File => { + self + .with_inner_blocking_task(|file| { + let nwritten = file.write(&view)?; + Ok(deno_core::WriteOutcome::Partial { nwritten, view }) + }) + .await + } + StdFileResourceKind::Stdin => { + Err(Into::<std::io::Error>::into(ErrorKind::Unsupported).into()) + } + StdFileResourceKind::Stdout => { + self + .with_blocking_task(|| { + // bypass the file and use std::io::stdout() + let mut stdout = std::io::stdout().lock(); + let nwritten = stdout.write(&view)?; + stdout.flush()?; + Ok(deno_core::WriteOutcome::Partial { nwritten, view }) + }) + .await + } + StdFileResourceKind::Stderr => { + self + .with_blocking_task(|| { + // bypass the file and use std::io::stderr() + let mut stderr = std::io::stderr().lock(); + let nwritten = stderr.write(&view)?; + stderr.flush()?; + Ok(deno_core::WriteOutcome::Partial { nwritten, view }) + }) + .await + } } } - pub fn fs_file(fs_file: StdFile) -> Self { - Self { - cell: RefCell::new(Some(StdFileResourceInner::file(fs_file))), - cell_async_task_queue: Default::default(), - name: "fsFile".to_string(), + fn read_all_sync(self: Rc<Self>) -> FsResult<Vec<u8>> { + match self.kind { + StdFileResourceKind::File | StdFileResourceKind::Stdin => { + let mut buf = Vec::new(); + self.with_sync(|file| Ok(file.read_to_end(&mut buf)?))?; + Ok(buf) + } + StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => { + Err(FsError::NotSupported) + } } } - - fn with_inner<TResult, E>( - &self, - action: impl FnOnce(&mut StdFileResourceInner) -> Result<TResult, E>, - ) -> Option<Result<TResult, E>> { - match self.cell.try_borrow_mut() { - Ok(mut cell) if cell.is_some() => { - let mut file = cell.take().unwrap(); - let result = action(&mut file); - cell.replace(file); - Some(result) + async fn read_all_async(self: Rc<Self>) -> FsResult<Vec<u8>> { + match self.kind { + StdFileResourceKind::File | StdFileResourceKind::Stdin => { + self + .with_inner_blocking_task(|file| { + let mut buf = Vec::new(); + file.read_to_end(&mut buf)?; + Ok(buf) + }) + .await + } + StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => { + Err(FsError::NotSupported) } - _ => None, } } - async fn with_inner_blocking_task<F, R: Send + 'static>(&self, action: F) -> R - where - F: FnOnce(&mut StdFileResourceInner) -> R + Send + 'static, - { - // we want to restrict this to one async action at a time - let _permit = self.cell_async_task_queue.acquire().await; - // we take the value out of the cell, use it on a blocking task, - // then put it back into the cell when we're done - let mut did_take = false; - let mut cell_value = { - let mut cell = self.cell.borrow_mut(); - match cell.as_mut().unwrap().try_clone() { - Ok(value) => value, - Err(_) => { - did_take = true; - cell.take().unwrap() - } - } - }; - let (cell_value, result) = tokio::task::spawn_blocking(move || { - let result = action(&mut cell_value); - (cell_value, result) - }) - .await - .unwrap(); - - if did_take { - // put it back - self.cell.borrow_mut().replace(cell_value); + fn chmod_sync(self: Rc<Self>, _mode: u32) -> FsResult<()> { + #[cfg(unix)] + { + use std::os::unix::prelude::PermissionsExt; + self.with_sync(|file| { + Ok(file.set_permissions(std::fs::Permissions::from_mode(_mode))?) + }) } - - result + #[cfg(not(unix))] + Err(FsError::NotSupported) + } + async fn chmod_async(self: Rc<Self>, _mode: u32) -> FsResult<()> { + #[cfg(unix)] + { + use std::os::unix::prelude::PermissionsExt; + self + .with_inner_blocking_task(move |file| { + Ok(file.set_permissions(std::fs::Permissions::from_mode(_mode))?) + }) + .await + } + #[cfg(not(unix))] + Err(FsError::NotSupported) } - async fn read_byob( - self: Rc<Self>, - mut buf: BufMutView, - ) -> Result<(usize, BufMutView), AnyError> { + fn seek_sync(self: Rc<Self>, pos: io::SeekFrom) -> FsResult<u64> { + self.with_sync(|file| Ok(file.seek(pos)?)) + } + async fn seek_async(self: Rc<Self>, pos: io::SeekFrom) -> FsResult<u64> { self - .with_inner_blocking_task(move |inner| { - let nread = inner.read(&mut buf)?; - Ok((nread, buf)) - }) + .with_inner_blocking_task(move |file| Ok(file.seek(pos)?)) .await } - async fn write( - self: Rc<Self>, - view: BufView, - ) -> Result<deno_core::WriteOutcome, AnyError> { + fn datasync_sync(self: Rc<Self>) -> FsResult<()> { + self.with_sync(|file| Ok(file.sync_data()?)) + } + async fn datasync_async(self: Rc<Self>) -> FsResult<()> { self - .with_inner_blocking_task(move |inner| { - let nwritten = inner.write_and_maybe_flush(&view)?; - Ok(deno_core::WriteOutcome::Partial { nwritten, view }) - }) + .with_inner_blocking_task(|file| Ok(file.sync_data()?)) .await } - async fn write_all(self: Rc<Self>, view: BufView) -> Result<(), AnyError> { + fn sync_sync(self: Rc<Self>) -> FsResult<()> { + self.with_sync(|file| Ok(file.sync_all()?)) + } + async fn sync_async(self: Rc<Self>) -> FsResult<()> { self - .with_inner_blocking_task(move |inner| { - inner.write_all_and_maybe_flush(&view) - }) + .with_inner_blocking_task(|file| Ok(file.sync_all()?)) .await } - fn read_byob_sync(self: Rc<Self>, buf: &mut [u8]) -> Result<usize, AnyError> { - self - .with_inner(|inner| inner.read(buf)) - .ok_or_else(resource_unavailable)? - .map_err(Into::into) + fn stat_sync(self: Rc<Self>) -> FsResult<FsStat> { + self.with_sync(|file| Ok(file.metadata().map(FsStat::from_std)?)) } - - fn write_sync(self: Rc<Self>, data: &[u8]) -> Result<usize, AnyError> { + async fn stat_async(self: Rc<Self>) -> FsResult<FsStat> { self - .with_inner(|inner| inner.write_and_maybe_flush(data)) - .ok_or_else(resource_unavailable)? - } - - fn with_resource<F, R>( - state: &mut OpState, - rid: ResourceId, - f: F, - ) -> Result<R, AnyError> - where - F: FnOnce(Rc<StdFileResource>) -> Result<R, AnyError>, - { - let resource = state.resource_table.get::<StdFileResource>(rid)?; - f(resource) + .with_inner_blocking_task(|file| { + Ok(file.metadata().map(FsStat::from_std)?) + }) + .await } - pub fn with_file<F, R>( - state: &mut OpState, - rid: ResourceId, - f: F, - ) -> Result<R, AnyError> - where - F: FnOnce(&mut StdFile) -> Result<R, AnyError>, - { - Self::with_resource(state, rid, move |resource| { - resource - .with_inner(move |inner| inner.with_file(f)) - .ok_or_else(resource_unavailable)? + fn lock_sync(self: Rc<Self>, exclusive: bool) -> FsResult<()> { + self.with_sync(|file| { + if exclusive { + file.lock_exclusive()?; + } else { + file.lock_shared()?; + } + Ok(()) }) } - - pub fn with_file2<F, R>(self: Rc<Self>, f: F) -> Option<Result<R, io::Error>> - where - F: FnOnce(&mut StdFile) -> Result<R, io::Error>, - { - self.with_inner(move |inner| inner.with_file(f)) - } - - pub async fn with_file_blocking_task<F, R: Send + 'static>( - state: Rc<RefCell<OpState>>, - rid: ResourceId, - f: F, - ) -> Result<R, AnyError> - where - F: (FnOnce(&mut StdFile) -> Result<R, AnyError>) + Send + 'static, - { - let resource = state - .borrow_mut() - .resource_table - .get::<StdFileResource>(rid)?; - - resource - .with_inner_blocking_task(move |inner| inner.with_file(f)) + async fn lock_async(self: Rc<Self>, exclusive: bool) -> FsResult<()> { + self + .with_inner_blocking_task(move |file| { + if exclusive { + file.lock_exclusive()?; + } else { + file.lock_shared()?; + } + Ok(()) + }) .await } - pub async fn with_file_blocking_task2<F, R: Send + 'static>( - self: Rc<Self>, - f: F, - ) -> Result<R, io::Error> - where - F: (FnOnce(&mut StdFile) -> Result<R, io::Error>) + Send + 'static, - { + fn unlock_sync(self: Rc<Self>) -> FsResult<()> { + self.with_sync(|file| Ok(file.unlock()?)) + } + async fn unlock_async(self: Rc<Self>) -> FsResult<()> { self - .with_inner_blocking_task(move |inner| inner.with_file(f)) + .with_inner_blocking_task(|file| Ok(file.unlock()?)) .await } - pub fn clone_file( - state: &mut OpState, - rid: ResourceId, - ) -> Result<StdFile, AnyError> { - Self::with_file(state, rid, move |std_file| { - std_file.try_clone().map_err(AnyError::from) - }) + fn truncate_sync(self: Rc<Self>, len: u64) -> FsResult<()> { + self.with_sync(|file| Ok(file.set_len(len)?)) } - - pub fn as_stdio( - state: &mut OpState, - rid: u32, - ) -> Result<std::process::Stdio, AnyError> { - Self::with_resource(state, rid, |resource| { - resource - .with_inner(|inner| match inner.kind { - StdFileResourceKind::File => { - let file = inner.file.try_clone()?; - Ok(file.into()) - } - _ => Ok(std::process::Stdio::inherit()), - }) - .ok_or_else(resource_unavailable)? - }) - } -} - -impl Resource for StdFileResource { - fn name(&self) -> Cow<str> { - self.name.as_str().into() + async fn truncate_async(self: Rc<Self>, len: u64) -> FsResult<()> { + self + .with_inner_blocking_task(move |file| Ok(file.set_len(len)?)) + .await } - fn read(self: Rc<Self>, limit: usize) -> AsyncResult<deno_core::BufView> { - Box::pin(async move { - let vec = vec![0; limit]; - let buf = BufMutView::from(vec); - let (nread, buf) = StdFileResource::read_byob(self, buf).await?; - let mut vec = buf.unwrap_vec(); - if vec.len() != nread { - vec.truncate(nread); - } - Ok(BufView::from(vec)) + fn utime_sync( + self: Rc<Self>, + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()> { + let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos); + let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos); + + self.with_sync(|file| { + filetime::set_file_handle_times(file, Some(atime), Some(mtime))?; + Ok(()) }) } - - fn read_byob( + async fn utime_async( self: Rc<Self>, - buf: deno_core::BufMutView, - ) -> AsyncResult<(usize, deno_core::BufMutView)> { - Box::pin(StdFileResource::read_byob(self, buf)) - } + atime_secs: i64, + atime_nanos: u32, + mtime_secs: i64, + mtime_nanos: u32, + ) -> FsResult<()> { + let atime = filetime::FileTime::from_unix_time(atime_secs, atime_nanos); + let mtime = filetime::FileTime::from_unix_time(mtime_secs, mtime_nanos); - fn write( - self: Rc<Self>, - view: deno_core::BufView, - ) -> AsyncResult<deno_core::WriteOutcome> { - Box::pin(StdFileResource::write(self, view)) + self + .with_inner_blocking_task(move |file| { + filetime::set_file_handle_times(file, Some(atime), Some(mtime))?; + Ok(()) + }) + .await } - fn write_all(self: Rc<Self>, view: deno_core::BufView) -> AsyncResult<()> { - Box::pin(StdFileResource::write_all(self, view)) + async fn read_byob( + self: Rc<Self>, + mut buf: BufMutView, + ) -> FsResult<(usize, BufMutView)> { + self + .with_inner_blocking_task(|file| { + let nread = file.read(&mut buf)?; + Ok((nread, buf)) + }) + .await } - fn write_sync( - self: Rc<Self>, - data: &[u8], - ) -> Result<usize, deno_core::anyhow::Error> { - StdFileResource::write_sync(self, data) + fn try_clone_inner(self: Rc<Self>) -> FsResult<Rc<dyn fs::File>> { + let inner: &Option<_> = &self.cell.borrow(); + match inner { + Some(inner) => Ok(Rc::new(StdFileResourceInner { + kind: self.kind, + cell: RefCell::new(Some(inner.try_clone()?)), + cell_async_task_queue: Default::default(), + })), + None => Err(FsError::FileBusy), + } } - fn read_byob_sync( - self: Rc<Self>, - data: &mut [u8], - ) -> Result<usize, deno_core::anyhow::Error> { - StdFileResource::read_byob_sync(self, data) + fn as_stdio(self: Rc<Self>) -> FsResult<std::process::Stdio> { + match self.kind { + StdFileResourceKind::File => self.with_sync(|file| { + let file = file.try_clone()?; + Ok(file.into()) + }), + _ => Ok(std::process::Stdio::inherit()), + } } #[cfg(unix)] fn backing_fd(self: Rc<Self>) -> Option<std::os::unix::prelude::RawFd> { use std::os::unix::io::AsRawFd; - self - .with_inner(move |std_file| { - Ok::<_, ()>(std_file.with_file(|f| f.as_raw_fd())) - })? - .ok() + self.with_sync(|file| Ok(file.as_raw_fd())).ok() + } + + #[cfg(windows)] + fn backing_fd(self: Rc<Self>) -> Option<std::os::windows::io::RawHandle> { + use std::os::windows::prelude::AsRawHandle; + self.with_sync(|file| Ok(file.as_raw_handle())).ok() } } @@ -671,12 +739,7 @@ pub fn op_print( is_err: bool, ) -> Result<(), AnyError> { let rid = if is_err { 2 } else { 1 }; - StdFileResource::with_resource(state, rid, move |resource| { - resource - .with_inner(|inner| { - inner.write_all_and_maybe_flush(msg.as_bytes())?; - Ok(()) - }) - .ok_or_else(resource_unavailable)? + FileResource::with_file(state, rid, move |file| { + Ok(file.write_all_sync(msg.as_bytes())?) }) } |