feat(core): sync io ops in core (#18603)

This commit adds op_read_sync and op_write_sync to core. These ops are
similar to op_read and op_write, but they are synchronous. Just like the
async ops, they operate on generic `deno_core::Resource` objects. These
now have new `read_byob_sync` and `write_sync` methods, with default
implementations throwing "NotSupported" errors, just like the async
counterparts.

There are no `write_all` or `read` equivalents, because the
optimizations they unlock are not useful in synchronous contexts.
This commit is contained in:
Luca Casonato 2023-04-06 00:14:16 +02:00 committed by GitHub
parent ee15b49845
commit 36e8c8dfd7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 77 additions and 54 deletions

View file

@ -412,6 +412,8 @@
readAll: opAsync.bind(null, "op_read_all"), readAll: opAsync.bind(null, "op_read_all"),
write: opAsync.bind(null, "op_write"), write: opAsync.bind(null, "op_write"),
writeAll: opAsync.bind(null, "op_write_all"), writeAll: opAsync.bind(null, "op_write_all"),
readSync: (rid, buffer) => ops.op_read_sync(rid, buffer),
writeSync: (rid, buffer) => ops.op_write_sync(rid, buffer),
shutdown: opAsync.bind(null, "op_shutdown"), shutdown: opAsync.bind(null, "op_shutdown"),
print: (msg, isErr) => ops.op_print(msg, isErr), print: (msg, isErr) => ops.op_print(msg, isErr),
setMacrotaskCallback: (fn) => ops.op_set_macrotask_callback(fn), setMacrotaskCallback: (fn) => ops.op_set_macrotask_callback(fn),

View file

@ -60,6 +60,16 @@ declare namespace Deno {
*/ */
function writeAll(rid: number, buf: Uint8Array): Promise<void>; function writeAll(rid: number, buf: Uint8Array): Promise<void>;
/**
* Synchronously read from a (stream) resource that implements readSync().
*/
function readSync(rid: number, buf: Uint8Array): number;
/**
* Synchronously write to a (stream) resource that implements writeSync().
*/
function writeSync(rid: number, buf: Uint8Array): number;
/** /**
* Print a message to stdout or stderr * Print a message to stdout or stderr
*/ */

View file

@ -33,6 +33,8 @@ crate::extension!(
op_read, op_read,
op_read_all, op_read_all,
op_write, op_write,
op_read_sync,
op_write_sync,
op_write_all, op_write_all,
op_shutdown, op_shutdown,
op_metrics, op_metrics,
@ -279,6 +281,27 @@ async fn op_write(
Ok(resp.nwritten() as u32) Ok(resp.nwritten() as u32)
} }
#[op(fast)]
fn op_read_sync(
state: &mut OpState,
rid: ResourceId,
data: &mut [u8],
) -> Result<u32, Error> {
let resource = state.resource_table.get_any(rid)?;
resource.read_byob_sync(data).map(|n| n as u32)
}
#[op]
fn op_write_sync(
state: &mut OpState,
rid: ResourceId,
data: &[u8],
) -> Result<u32, Error> {
let resource = state.resource_table.get_any(rid)?;
let nwritten = resource.write_sync(data)?;
Ok(nwritten as u32)
}
#[op] #[op]
async fn op_write_all( async fn op_write_all(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,

View file

@ -154,6 +154,18 @@ pub trait Resource: Any + 'static {
}) })
} }
/// The same as [`read_byob()`][Resource::read_byob], but synchronous.
fn read_byob_sync(&self, data: &mut [u8]) -> Result<usize, Error> {
_ = data;
Err(not_supported())
}
/// The same as [`write()`][Resource::write], but synchronous.
fn write_sync(&self, data: &[u8]) -> Result<usize, Error> {
_ = data;
Err(not_supported())
}
/// The shutdown method can be used to asynchronously close the resource. It /// The shutdown method can be used to asynchronously close the resource. It
/// is not automatically called when the resource is dropped or closed. /// is not automatically called when the resource is dropped or closed.
/// ///

View file

@ -93,27 +93,19 @@ function* iterSync(
} }
function readSync(rid, buffer) { function readSync(rid, buffer) {
if (buffer.length === 0) { if (buffer.length === 0) return 0;
return 0; const nread = core.readSync(rid, buffer);
}
const nread = ops.op_read_sync(rid, buffer);
return nread === 0 ? null : nread; return nread === 0 ? null : nread;
} }
async function read(rid, buffer) { async function read(rid, buffer) {
if (buffer.length === 0) { if (buffer.length === 0) return 0;
return 0;
}
const nread = await core.read(rid, buffer); const nread = await core.read(rid, buffer);
return nread === 0 ? null : nread; return nread === 0 ? null : nread;
} }
function writeSync(rid, data) { function writeSync(rid, data) {
return ops.op_write_sync(rid, data); return core.writeSync(rid, data);
} }
function write(rid, data) { function write(rid, data) {

View file

@ -78,7 +78,6 @@ pub static STDERR_HANDLE: Lazy<StdFile> = Lazy::new(|| {
deno_core::extension!(deno_io, deno_core::extension!(deno_io,
deps = [ deno_web ], deps = [ deno_web ],
ops = [op_read_sync, op_write_sync],
esm = [ "12_io.js" ], esm = [ "12_io.js" ],
options = { options = {
stdio: Option<Stdio>, stdio: Option<Stdio>,
@ -454,7 +453,7 @@ impl StdFileResource {
} }
fn with_inner_and_metadata<TResult>( fn with_inner_and_metadata<TResult>(
self: Rc<Self>, &self,
action: impl FnOnce( action: impl FnOnce(
&mut StdFileResourceInner, &mut StdFileResourceInner,
&Arc<Mutex<FileMetadata>>, &Arc<Mutex<FileMetadata>>,
@ -471,10 +470,7 @@ impl StdFileResource {
} }
} }
async fn with_inner_blocking_task<F, R: Send + 'static>( async fn with_inner_blocking_task<F, R: Send + 'static>(&self, action: F) -> R
self: Rc<Self>,
action: F,
) -> R
where where
F: FnOnce(&mut StdFileResourceInner) -> R + Send + 'static, F: FnOnce(&mut StdFileResourceInner) -> R + Send + 'static,
{ {
@ -540,6 +536,14 @@ impl StdFileResource {
.await .await
} }
fn read_byob_sync(&self, buf: &mut [u8]) -> Result<usize, AnyError> {
self.with_inner_and_metadata(|inner, _| inner.read(buf).map_err(Into::into))
}
fn write_sync(&self, data: &[u8]) -> Result<usize, AnyError> {
self.with_inner_and_metadata(|inner, _| inner.write_and_maybe_flush(data))
}
fn with_resource<F, R>( fn with_resource<F, R>(
state: &mut OpState, state: &mut OpState,
rid: ResourceId, rid: ResourceId,
@ -632,7 +636,7 @@ impl Resource for StdFileResource {
Box::pin(async move { Box::pin(async move {
let vec = vec![0; limit]; let vec = vec![0; limit];
let buf = BufMutView::from(vec); let buf = BufMutView::from(vec);
let (nread, buf) = self.read_byob(buf).await?; let (nread, buf) = StdFileResource::read_byob(self, buf).await?;
let mut vec = buf.unwrap_vec(); let mut vec = buf.unwrap_vec();
if vec.len() != nread { if vec.len() != nread {
vec.truncate(nread); vec.truncate(nread);
@ -645,17 +649,29 @@ impl Resource for StdFileResource {
self: Rc<Self>, self: Rc<Self>,
buf: deno_core::BufMutView, buf: deno_core::BufMutView,
) -> AsyncResult<(usize, deno_core::BufMutView)> { ) -> AsyncResult<(usize, deno_core::BufMutView)> {
Box::pin(self.read_byob(buf)) Box::pin(StdFileResource::read_byob(self, buf))
} }
fn write( fn write(
self: Rc<Self>, self: Rc<Self>,
view: deno_core::BufView, view: deno_core::BufView,
) -> AsyncResult<deno_core::WriteOutcome> { ) -> AsyncResult<deno_core::WriteOutcome> {
Box::pin(self.write(view)) Box::pin(StdFileResource::write(self, view))
} }
fn write_all(self: Rc<Self>, view: deno_core::BufView) -> AsyncResult<()> { fn write_all(self: Rc<Self>, view: deno_core::BufView) -> AsyncResult<()> {
Box::pin(self.write_all(view)) Box::pin(StdFileResource::write_all(self, view))
}
fn write_sync(&self, data: &[u8]) -> Result<usize, deno_core::anyhow::Error> {
StdFileResource::write_sync(self, data)
}
fn read_byob_sync(
&self,
data: &mut [u8],
) -> Result<usize, deno_core::anyhow::Error> {
StdFileResource::read_byob_sync(self, data)
} }
#[cfg(unix)] #[cfg(unix)]
@ -684,35 +700,3 @@ pub fn op_print(
}) })
}) })
} }
#[op(fast)]
fn op_read_sync(
state: &mut OpState,
rid: u32,
buf: &mut [u8],
) -> Result<u32, AnyError> {
StdFileResource::with_resource(state, rid, move |resource| {
resource.with_inner_and_metadata(|inner, _| {
inner
.read(buf)
.map(|n: usize| n as u32)
.map_err(AnyError::from)
})
})
}
#[op(fast)]
fn op_write_sync(
state: &mut OpState,
rid: u32,
buf: &mut [u8],
) -> Result<u32, AnyError> {
StdFileResource::with_resource(state, rid, move |resource| {
resource.with_inner_and_metadata(|inner, _| {
inner
.write_and_maybe_flush(buf)
.map(|nwritten: usize| nwritten as u32)
.map_err(AnyError::from)
})
})
}