summaryrefslogtreecommitdiff
path: root/core/resources.rs
diff options
context:
space:
mode:
authorMatt Mastracci <matthew@mastracci.com>2023-07-01 18:00:14 -0600
committerGitHub <noreply@github.com>2023-07-02 00:00:14 +0000
commite746b6d80654ba4e4e26370fe6e4f784ce841d92 (patch)
tree153ffad92a96126b9ab8e906dcdabf7648755931 /core/resources.rs
parentb9c0e7cd550ab14fa7da7e33ed87cbeeeb9785a0 (diff)
refactor(core): Extract deno_core (#19658)
`deno_core` is moving out! You'll find it at https://github.com/denoland/deno_core/ once this PR lands.
Diffstat (limited to 'core/resources.rs')
-rw-r--r--core/resources.rs429
1 files changed, 0 insertions, 429 deletions
diff --git a/core/resources.rs b/core/resources.rs
deleted file mode 100644
index 94d2a2306..000000000
--- a/core/resources.rs
+++ /dev/null
@@ -1,429 +0,0 @@
-// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-
-// Think of Resources as File Descriptors. They are integers that are allocated
-// by the privileged side of Deno which refer to various rust objects that need
-// to be persisted between various ops. For example, network sockets are
-// resources. Resources may or may not correspond to a real operating system
-// file descriptor (hence the different name).
-
-use crate::error::bad_resource_id;
-use crate::error::not_supported;
-use crate::io::BufMutView;
-use crate::io::BufView;
-use crate::io::WriteOutcome;
-use anyhow::Error;
-use futures::Future;
-use std::any::type_name;
-use std::any::Any;
-use std::any::TypeId;
-use std::borrow::Cow;
-use std::collections::BTreeMap;
-use std::iter::Iterator;
-use std::pin::Pin;
-use std::rc::Rc;
-
-/// Returned by resource read/write/shutdown methods
-pub type AsyncResult<T> = Pin<Box<dyn Future<Output = Result<T, Error>>>>;
-
-/// Resources are Rust objects that are attached to a [deno_core::JsRuntime].
-/// They are identified in JS by a numeric ID (the resource ID, or rid).
-/// Resources can be created in ops. Resources can also be retrieved in ops by
-/// their rid. Resources are not thread-safe - they can only be accessed from
-/// the thread that the JsRuntime lives on.
-///
-/// Resources are reference counted in Rust. This means that they can be
-/// cloned and passed around. When the last reference is dropped, the resource
-/// is automatically closed. As long as the resource exists in the resource
-/// table, the reference count is at least 1.
-///
-/// ### Readable
-///
-/// Readable resources are resources that can have data read from. Examples of
-/// this are files, sockets, or HTTP streams.
-///
-/// Readables can be read from from either JS or Rust. In JS one can use
-/// `Deno.core.read()` to read from a single chunk of data from a readable. In
-/// Rust one can directly call `read()` or `read_byob()`. The Rust side code is
-/// used to implement ops like `op_slice`.
-///
-/// A distinction can be made between readables that produce chunks of data
-/// themselves (they allocate the chunks), and readables that fill up
-/// bring-your-own-buffers (BYOBs). The former is often the case for framed
-/// protocols like HTTP, while the latter is often the case for kernel backed
-/// resources like files and sockets.
-///
-/// All readables must implement `read()`. If resources can support an optimized
-/// path for BYOBs, they should also implement `read_byob()`. For kernel backed
-/// resources it often makes sense to implement `read_byob()` first, and then
-/// implement `read()` as an operation that allocates a new chunk with
-/// `len == limit`, then calls `read_byob()`, and then returns a chunk sliced to
-/// the number of bytes read. Kernel backed resources can use the
-/// [deno_core::impl_readable_byob] macro to implement optimized `read_byob()`
-/// and `read()` implementations from a single `Self::read()` method.
-///
-/// ### Writable
-///
-/// Writable resources are resources that can have data written to. Examples of
-/// this are files, sockets, or HTTP streams.
-///
-/// Writables can be written to from either JS or Rust. In JS one can use
-/// `Deno.core.write()` to write to a single chunk of data to a writable. In
-/// Rust one can directly call `write()`. The latter is used to implement ops
-/// like `op_slice`.
-pub trait Resource: Any + 'static {
- /// Returns a string representation of the resource which is made available
- /// to JavaScript code through `op_resources`. The default implementation
- /// returns the Rust type name, but specific resource types may override this
- /// trait method.
- fn name(&self) -> Cow<str> {
- type_name::<Self>().into()
- }
-
- /// Read a single chunk of data from the resource. This operation returns a
- /// `BufView` that represents the data that was read. If a zero length buffer
- /// is returned, it indicates that the resource has reached EOF.
- ///
- /// If this method is not implemented, the default implementation will error
- /// with a "not supported" error.
- ///
- /// If a readable can provide an optimized path for BYOBs, it should also
- /// implement `read_byob()`.
- fn read(self: Rc<Self>, limit: usize) -> AsyncResult<BufView> {
- _ = limit;
- Box::pin(futures::future::err(not_supported()))
- }
-
- /// Read a single chunk of data from the resource into the provided `BufMutView`.
- ///
- /// This operation returns the number of bytes read. If zero bytes are read,
- /// it indicates that the resource has reached EOF.
- ///
- /// If this method is not implemented explicitly, the default implementation
- /// will call `read()` and then copy the data into the provided buffer. For
- /// readable resources that can provide an optimized path for BYOBs, it is
- /// strongly recommended to override this method.
- fn read_byob(
- self: Rc<Self>,
- mut buf: BufMutView,
- ) -> AsyncResult<(usize, BufMutView)> {
- Box::pin(async move {
- let read = self.read(buf.len()).await?;
- let nread = read.len();
- buf[..nread].copy_from_slice(&read);
- Ok((nread, buf))
- })
- }
-
- /// Write a single chunk of data to the resource. The operation may not be
- /// able to write the entire chunk, in which case it should return the number
- /// of bytes written. Additionally it should return the `BufView` that was
- /// passed in.
- ///
- /// If this method is not implemented, the default implementation will error
- /// with a "not supported" error.
- fn write(self: Rc<Self>, buf: BufView) -> AsyncResult<WriteOutcome> {
- _ = buf;
- Box::pin(futures::future::err(not_supported()))
- }
-
- /// Write an entire chunk of data to the resource. Unlike `write()`, this will
- /// ensure the entire chunk is written. If the operation is not able to write
- /// the entire chunk, an error is to be returned.
- ///
- /// By default this method will call `write()` repeatedly until the entire
- /// chunk is written. Resources that can write the entire chunk in a single
- /// operation using an optimized path should override this method.
- fn write_all(self: Rc<Self>, view: BufView) -> AsyncResult<()> {
- Box::pin(async move {
- let mut view = view;
- let this = self;
- while !view.is_empty() {
- let resp = this.clone().write(view).await?;
- match resp {
- WriteOutcome::Partial {
- nwritten,
- view: new_view,
- } => {
- view = new_view;
- view.advance_cursor(nwritten);
- }
- WriteOutcome::Full { .. } => break,
- }
- }
- Ok(())
- })
- }
-
- /// The same as [`read_byob()`][Resource::read_byob], but synchronous.
- fn read_byob_sync(self: Rc<Self>, data: &mut [u8]) -> Result<usize, Error> {
- _ = data;
- Err(not_supported())
- }
-
- /// The same as [`write()`][Resource::write], but synchronous.
- fn write_sync(self: Rc<Self>, data: &[u8]) -> Result<usize, Error> {
- _ = data;
- Err(not_supported())
- }
-
- /// The shutdown method can be used to asynchronously close the resource. It
- /// is not automatically called when the resource is dropped or closed.
- ///
- /// If this method is not implemented, the default implementation will error
- /// with a "not supported" error.
- fn shutdown(self: Rc<Self>) -> AsyncResult<()> {
- Box::pin(futures::future::err(not_supported()))
- }
-
- /// Resources may implement the `close()` trait method if they need to do
- /// resource specific clean-ups, such as cancelling pending futures, after a
- /// resource has been removed from the resource table.
- fn close(self: Rc<Self>) {}
-
- /// Resources backed by a file descriptor can let ops know to allow for
- /// low-level optimizations.
- #[cfg(unix)]
- fn backing_fd(self: Rc<Self>) -> Option<std::os::unix::prelude::RawFd> {
- None
- }
-
- /// Resources backed by a file descriptor can let ops know to allow for
- /// low-level optimizations.
- #[cfg(windows)]
- fn backing_fd(self: Rc<Self>) -> Option<std::os::windows::io::RawHandle> {
- None
- }
-
- fn size_hint(&self) -> (u64, Option<u64>) {
- (0, None)
- }
-}
-
-impl dyn Resource {
- #[inline(always)]
- fn is<T: Resource>(&self) -> bool {
- self.type_id() == TypeId::of::<T>()
- }
-
- #[inline(always)]
- #[allow(clippy::needless_lifetimes)]
- pub fn downcast_rc<'a, T: Resource>(self: &'a Rc<Self>) -> Option<&'a Rc<T>> {
- if self.is::<T>() {
- let ptr = self as *const Rc<_> as *const Rc<T>;
- // TODO(piscisaureus): safety comment
- #[allow(clippy::undocumented_unsafe_blocks)]
- Some(unsafe { &*ptr })
- } else {
- None
- }
- }
-}
-
-/// A `ResourceId` is an integer value referencing a resource. It could be
-/// considered to be the Deno equivalent of a `file descriptor` in POSIX like
-/// operating systems. Elsewhere in the code base it is commonly abbreviated
-/// to `rid`.
-// TODO: use `u64` instead?
-pub type ResourceId = u32;
-
-/// Map-like data structure storing Deno's resources (equivalent to file
-/// descriptors).
-///
-/// Provides basic methods for element access. A resource can be of any type.
-/// Different types of resources can be stored in the same map, and provided
-/// with a name for description.
-///
-/// Each resource is identified through a _resource ID (rid)_, which acts as
-/// the key in the map.
-#[derive(Default)]
-pub struct ResourceTable {
- index: BTreeMap<ResourceId, Rc<dyn Resource>>,
- next_rid: ResourceId,
-}
-
-impl ResourceTable {
- /// Inserts resource into the resource table, which takes ownership of it.
- ///
- /// The resource type is erased at runtime and must be statically known
- /// when retrieving it through `get()`.
- ///
- /// Returns a unique resource ID, which acts as a key for this resource.
- pub fn add<T: Resource>(&mut self, resource: T) -> ResourceId {
- self.add_rc(Rc::new(resource))
- }
-
- /// Inserts a `Rc`-wrapped resource into the resource table.
- ///
- /// The resource type is erased at runtime and must be statically known
- /// when retrieving it through `get()`.
- ///
- /// Returns a unique resource ID, which acts as a key for this resource.
- pub fn add_rc<T: Resource>(&mut self, resource: Rc<T>) -> ResourceId {
- let resource = resource as Rc<dyn Resource>;
- self.add_rc_dyn(resource)
- }
-
- pub fn add_rc_dyn(&mut self, resource: Rc<dyn Resource>) -> ResourceId {
- let rid = self.next_rid;
- let removed_resource = self.index.insert(rid, resource);
- assert!(removed_resource.is_none());
- self.next_rid += 1;
- rid
- }
-
- /// Returns true if any resource with the given `rid` exists.
- pub fn has(&self, rid: ResourceId) -> bool {
- self.index.contains_key(&rid)
- }
-
- /// Returns a reference counted pointer to the resource of type `T` with the
- /// given `rid`. If `rid` is not present or has a type different than `T`,
- /// this function returns `None`.
- pub fn get<T: Resource>(&self, rid: ResourceId) -> Result<Rc<T>, Error> {
- self
- .index
- .get(&rid)
- .and_then(|rc| rc.downcast_rc::<T>())
- .map(Clone::clone)
- .ok_or_else(bad_resource_id)
- }
-
- pub fn get_any(&self, rid: ResourceId) -> Result<Rc<dyn Resource>, Error> {
- self
- .index
- .get(&rid)
- .map(Clone::clone)
- .ok_or_else(bad_resource_id)
- }
-
- /// Replaces a resource with a new resource.
- ///
- /// Panics if the resource does not exist.
- pub fn replace<T: Resource>(&mut self, rid: ResourceId, resource: T) {
- let result = self
- .index
- .insert(rid, Rc::new(resource) as Rc<dyn Resource>);
- assert!(result.is_some());
- }
-
- /// Removes a resource of type `T` from the resource table and returns it.
- /// If a resource with the given `rid` exists but its type does not match `T`,
- /// it is not removed from the resource table. Note that the resource's
- /// `close()` method is *not* called.
- ///
- /// Also note that there might be a case where
- /// the returned `Rc<T>` is referenced by other variables. That is, we cannot
- /// assume that `Rc::strong_count(&returned_rc)` is always equal to 1 on success.
- /// In particular, be really careful when you want to extract the inner value of
- /// type `T` from `Rc<T>`.
- pub fn take<T: Resource>(&mut self, rid: ResourceId) -> Result<Rc<T>, Error> {
- let resource = self.get::<T>(rid)?;
- self.index.remove(&rid);
- Ok(resource)
- }
-
- /// Removes a resource from the resource table and returns it. Note that the
- /// resource's `close()` method is *not* called.
- ///
- /// Also note that there might be a
- /// case where the returned `Rc<T>` is referenced by other variables. That is,
- /// we cannot assume that `Rc::strong_count(&returned_rc)` is always equal to 1
- /// on success. In particular, be really careful when you want to extract the
- /// inner value of type `T` from `Rc<T>`.
- pub fn take_any(
- &mut self,
- rid: ResourceId,
- ) -> Result<Rc<dyn Resource>, Error> {
- self.index.remove(&rid).ok_or_else(bad_resource_id)
- }
-
- /// Removes the resource with the given `rid` from the resource table. If the
- /// only reference to this resource existed in the resource table, this will
- /// cause the resource to be dropped. However, since resources are reference
- /// counted, therefore pending ops are not automatically cancelled. A resource
- /// may implement the `close()` method to perform clean-ups such as canceling
- /// ops.
- pub fn close(&mut self, rid: ResourceId) -> Result<(), Error> {
- self
- .index
- .remove(&rid)
- .ok_or_else(bad_resource_id)
- .map(|resource| resource.close())
- }
-
- /// Returns an iterator that yields a `(id, name)` pair for every resource
- /// that's currently in the resource table. This can be used for debugging
- /// purposes or to implement the `op_resources` op. Note that the order in
- /// which items appear is not specified.
- ///
- /// # Example
- ///
- /// ```
- /// # use deno_core::ResourceTable;
- /// # let resource_table = ResourceTable::default();
- /// let resource_names = resource_table.names().collect::<Vec<_>>();
- /// ```
- pub fn names(&self) -> impl Iterator<Item = (ResourceId, Cow<str>)> {
- self
- .index
- .iter()
- .map(|(&id, resource)| (id, resource.name()))
- }
-}
-
-#[macro_export]
-macro_rules! impl_readable_byob {
- () => {
- fn read(self: Rc<Self>, limit: usize) -> AsyncResult<$crate::BufView> {
- Box::pin(async move {
- let mut vec = vec![0; limit];
- let nread = self.read(&mut vec).await?;
- if nread != vec.len() {
- vec.truncate(nread);
- }
- let view = $crate::BufView::from(vec);
- Ok(view)
- })
- }
-
- fn read_byob(
- self: Rc<Self>,
- mut buf: $crate::BufMutView,
- ) -> AsyncResult<(usize, $crate::BufMutView)> {
- Box::pin(async move {
- let nread = self.read(buf.as_mut()).await?;
- Ok((nread, buf))
- })
- }
- };
-}
-
-#[macro_export]
-macro_rules! impl_writable {
- (__write) => {
- fn write(
- self: Rc<Self>,
- view: $crate::BufView,
- ) -> AsyncResult<$crate::WriteOutcome> {
- Box::pin(async move {
- let nwritten = self.write(&view).await?;
- Ok($crate::WriteOutcome::Partial { nwritten, view })
- })
- }
- };
- (__write_all) => {
- fn write_all(self: Rc<Self>, view: $crate::BufView) -> AsyncResult<()> {
- Box::pin(async move {
- self.write_all(&view).await?;
- Ok(())
- })
- }
- };
- () => {
- $crate::impl_writable!(__write);
- };
- (with_all) => {
- $crate::impl_writable!(__write);
- $crate::impl_writable!(__write_all);
- };
-}