// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Implementation of the `declare_handle_map!` macro #[macro_export] /// ```ignore /// declare_handle_map! ( /// $handle_module_name, /// $map_dimension_provider, /// $handle_type_name, /// $wrapped_type, /// ) /// ``` /// /// Declares a new public module with name `handle_module_name` which implements handle functionality /// for the given struct `handle_type_name` which is `#[repr(C)]` and represents FFI-accessible /// handles to values of type `wrapped_type`. `handle_type_name` expects a struct with a single u64 /// field `handle_id`. /// /// Internal to the generated module, a new static `SingletonHandleMap` is created, where the /// maximum number of active handles and the number of shards are given by /// the dimensions returned by evaluation of the `map_dimension_provider` expression. /// /// Note: `map_dimension_provider` will be evaluated within the defined module's scope, /// so you will likely need to use `super` to refer to definitions in the enclosing scope. /// /// # Example /// The following code defines an FFI-safe type `StringHandle` which references /// the `String` data-type, and uses it to define a (contrived) /// function `sample` which will print "Hello World". /// /// ``` /// #[macro_use]/// /// extern crate lazy_static; /// /// use core::ops::Deref; /// use handle_map::{declare_handle_map, HandleMapDimensions, HandleLike}; /// /// fn get_string_handle_map_dimensions() -> HandleMapDimensions { /// HandleMapDimensions { /// num_shards: 8, /// max_active_handles: 100, /// } /// } /// /// struct StringHandle { /// handle_id: u64 /// } /// /// declare_handle_map!( /// string_handle, /// super::get_string_handle_map_dimensions(), /// super::StringHandle, /// String /// ); /// /// fn main() { /// // Note: this method could panic if there are /// // more than 99 outstanding handles. /// /// // Allocate a new string-handle pointing to the string "Hello" /// let handle = StringHandle::allocate(|| { "Hello".to_string() }).unwrap(); /// { /// // Obtain a write-guard on the contents of our handle /// let mut handle_write_guard = handle.get_mut().unwrap(); /// handle_write_guard.push_str(" World"); /// // Write guard is auto-dropped at the end of this block. /// } /// { /// // Obtain a read-guard on the contents of our handle. /// // Note that we had to ensure that the write-guard was /// // dropped prior to doing this, or else execution /// // could potentially hang. /// let handle_read_guard = handle.get().unwrap(); /// println!("{}", handle_read_guard.deref()); /// } /// // Clean up the data behind the created handle /// handle.deallocate().unwrap(); /// } /// /// ``` macro_rules! declare_handle_map { ( $handle_module_name:ident, $map_dimension_provider:expr, $handle_type_name:ty, $wrapped_type:ty ) => { #[doc = ::core::concat!( "Macro-generated (via `handle_map::declare_handle_map!`) module which", " defines the `", ::core::stringify!($handle_module_name), "::", ::core::stringify!($handle_type_name), "` FFI-transmissible handle type ", " which references values of type `", ::core::stringify!($wrapped_type), "`." )] pub mod $handle_module_name { lazy_static! { static ref GLOBAL_HANDLE_MAP: $crate::HandleMap<$wrapped_type> = $crate::HandleMap::with_dimensions($map_dimension_provider); } pub (crate) fn get_current_allocation_count() -> u32 { GLOBAL_HANDLE_MAP.get_current_allocation_count() } #[doc = ::core::concat!( "A `#[repr(C)]` handle to a value of type `", ::core::stringify!($wrapped_type), "`." )] impl $handle_type_name { /// Cast the given raw Handle to this HandleLike pub fn from_handle(handle: $crate::Handle) -> Self { Self { handle_id: handle.get_id() } } /// Get this HandleLike as a raw Handle. pub fn get_as_handle(&self) -> $crate::Handle { $crate::Handle::from_id(self.handle_id) } } impl $crate::HandleLike for $handle_type_name { type Object = $wrapped_type; fn try_allocate( initial_value_provider: impl FnOnce() -> Result<$wrapped_type, E>, ) -> Result> { GLOBAL_HANDLE_MAP .try_allocate(initial_value_provider) .map(|derived_handle| Self { handle_id: derived_handle.get_id() }) } fn allocate( initial_value_provider: impl FnOnce() -> $wrapped_type, ) -> Result { GLOBAL_HANDLE_MAP .allocate(initial_value_provider) .map(|derived_handle| Self { handle_id: derived_handle.get_id() }) } fn get( &self, ) -> Result<$crate::ObjectReadGuardImpl<$wrapped_type>, $crate::HandleNotPresentError> { GLOBAL_HANDLE_MAP.get(self.get_as_handle()) } fn get_mut( &self, ) -> Result< $crate::ObjectReadWriteGuardImpl<$wrapped_type>, $crate::HandleNotPresentError, > { GLOBAL_HANDLE_MAP.get_mut(self.get_as_handle()) } fn deallocate(self) -> Result<$wrapped_type, $crate::HandleNotPresentError> { GLOBAL_HANDLE_MAP.deallocate(self.get_as_handle()) } } } }; }