// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use super::Resource; use super::ResourceHandle; use super::ResourceHandleFd; use super::ResourceHandleSocket; use crate::error::bad_resource_id; use crate::error::custom_error; use anyhow::Error; use std::borrow::Cow; use std::collections::BTreeMap; use std::rc::Rc; /// 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>, next_rid: ResourceId, } impl ResourceTable { /// Returns the number of resources currently active in the resource table. /// Resources taken from the table do not contribute to this count. pub fn len(&self) -> usize { self.index.len() } /// Returns whether this table is empty. pub fn is_empty(&self) -> bool { self.index.is_empty() } /// 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(&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(&mut self, resource: Rc) -> ResourceId { let resource = resource as Rc; self.add_rc_dyn(resource) } pub fn add_rc_dyn(&mut self, resource: Rc) -> 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(&self, rid: ResourceId) -> Result, Error> { self .index .get(&rid) .and_then(|rc| rc.downcast_rc::()) .cloned() .ok_or_else(bad_resource_id) } pub fn get_any(&self, rid: ResourceId) -> Result, Error> { self.index.get(&rid).cloned().ok_or_else(bad_resource_id) } /// Replaces a resource with a new resource. /// /// Panics if the resource does not exist. pub fn replace(&mut self, rid: ResourceId, resource: T) { let result = self .index .insert(rid, Rc::new(resource) as Rc); 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` 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`. pub fn take(&mut self, rid: ResourceId) -> Result, Error> { let resource = self.get::(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` 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`. pub fn take_any( &mut self, rid: ResourceId, ) -> Result, 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. #[deprecated = "This method may deadlock. Use take() and close() instead."] 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::>(); /// ``` pub fn names(&self) -> impl Iterator)> { self .index .iter() .map(|(&id, resource)| (id, resource.name())) } /// Retrieves the [`ResourceHandleFd`] for a given resource, for potential optimization /// purposes within ops. pub fn get_fd(&self, rid: ResourceId) -> Result { let Some(handle) = self.get_any(rid)?.backing_handle() else { return Err(bad_resource_id()); }; let Some(fd) = handle.as_fd_like() else { return Err(bad_resource_id()); }; if !handle.is_valid() { return Err(custom_error("ReferenceError", "null or invalid handle")); } Ok(fd) } /// Retrieves the [`ResourceHandleSocket`] for a given resource, for potential optimization /// purposes within ops. pub fn get_socket( &self, rid: ResourceId, ) -> Result { let Some(handle) = self.get_any(rid)?.backing_handle() else { return Err(bad_resource_id()); }; let Some(socket) = handle.as_socket_like() else { return Err(bad_resource_id()); }; if !handle.is_valid() { return Err(custom_error("ReferenceError", "null or invalid handle")); } Ok(socket) } /// Retrieves the [`ResourceHandle`] for a given resource, for potential optimization /// purposes within ops. pub fn get_handle( &self, rid: ResourceId, ) -> ::std::result::Result { let Some(handle) = self.get_any(rid)?.backing_handle() else { return Err(bad_resource_id()); }; if !handle.is_valid() { return Err(custom_error("ReferenceError", "null or invalid handle")); } Ok(handle) } }