1 // Copyright 2023 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //! Implementation of the `declare_handle_map!` macro 16 17 #[macro_export] 18 /// ```ignore 19 /// declare_handle_map! ( 20 /// $handle_module_name, 21 /// $map_dimension_provider, 22 /// $handle_type_name, 23 /// $wrapped_type, 24 /// ) 25 /// ``` 26 /// 27 /// Declares a new public module with name `handle_module_name` which implements handle functionality 28 /// for the given struct `handle_type_name` which is `#[repr(C)]` and represents FFI-accessible 29 /// handles to values of type `wrapped_type`. `handle_type_name` expects a struct with a single u64 30 /// field `handle_id`. 31 /// 32 /// Internal to the generated module, a new static `SingletonHandleMap` is created, where the 33 /// maximum number of active handles and the number of shards are given by 34 /// the dimensions returned by evaluation of the `map_dimension_provider` expression. 35 /// 36 /// Note: `map_dimension_provider` will be evaluated within the defined module's scope, 37 /// so you will likely need to use `super` to refer to definitions in the enclosing scope. 38 /// 39 /// # Example 40 /// The following code defines an FFI-safe type `StringHandle` which references 41 /// the `String` data-type, and uses it to define a (contrived) 42 /// function `sample` which will print "Hello World". 43 /// 44 /// ``` 45 /// #[macro_use]/// 46 /// extern crate lazy_static; 47 /// 48 /// use core::ops::Deref; 49 /// use handle_map::{declare_handle_map, HandleMapDimensions, HandleLike}; 50 /// 51 /// fn get_string_handle_map_dimensions() -> HandleMapDimensions { 52 /// HandleMapDimensions { 53 /// num_shards: 8, 54 /// max_active_handles: 100, 55 /// } 56 /// } 57 /// 58 /// struct StringHandle { 59 /// handle_id: u64 60 /// } 61 /// 62 /// declare_handle_map!( 63 /// string_handle, 64 /// super::get_string_handle_map_dimensions(), 65 /// super::StringHandle, 66 /// String 67 /// ); 68 /// 69 /// fn main() { 70 /// // Note: this method could panic if there are 71 /// // more than 99 outstanding handles. 72 /// 73 /// // Allocate a new string-handle pointing to the string "Hello" 74 /// let handle = StringHandle::allocate(|| { "Hello".to_string() }).unwrap(); 75 /// { 76 /// // Obtain a write-guard on the contents of our handle 77 /// let mut handle_write_guard = handle.get_mut().unwrap(); 78 /// handle_write_guard.push_str(" World"); 79 /// // Write guard is auto-dropped at the end of this block. 80 /// } 81 /// { 82 /// // Obtain a read-guard on the contents of our handle. 83 /// // Note that we had to ensure that the write-guard was 84 /// // dropped prior to doing this, or else execution 85 /// // could potentially hang. 86 /// let handle_read_guard = handle.get().unwrap(); 87 /// println!("{}", handle_read_guard.deref()); 88 /// } 89 /// // Clean up the data behind the created handle 90 /// handle.deallocate().unwrap(); 91 /// } 92 /// 93 /// ``` 94 macro_rules! declare_handle_map { 95 ( 96 $handle_module_name:ident, 97 $map_dimension_provider:expr, 98 $handle_type_name:ty, 99 $wrapped_type:ty 100 ) => { 101 #[doc = ::core::concat!( 102 "Macro-generated (via `handle_map::declare_handle_map!`) module which", 103 " defines the `", ::core::stringify!($handle_module_name), "::", 104 ::core::stringify!($handle_type_name), "` FFI-transmissible handle type ", 105 " which references values of type `", ::core::stringify!($wrapped_type), "`." 106 )] 107 pub mod $handle_module_name { 108 lazy_static! { 109 static ref GLOBAL_HANDLE_MAP: $crate::HandleMap<$wrapped_type> = 110 $crate::HandleMap::with_dimensions($map_dimension_provider); 111 } 112 113 pub (crate) fn get_current_allocation_count() -> u32 { 114 GLOBAL_HANDLE_MAP.get_current_allocation_count() 115 } 116 117 #[doc = ::core::concat!( 118 "A `#[repr(C)]` handle to a value of type `", 119 ::core::stringify!($wrapped_type), "`." 120 )] 121 122 impl $handle_type_name { 123 /// Cast the given raw Handle to this HandleLike 124 pub fn from_handle(handle: $crate::Handle) -> Self { 125 Self { handle_id: handle.get_id() } 126 } 127 128 /// Get this HandleLike as a raw Handle. 129 pub fn get_as_handle(&self) -> $crate::Handle { 130 $crate::Handle::from_id(self.handle_id) 131 } 132 } 133 impl $crate::HandleLike for $handle_type_name { 134 type Object = $wrapped_type; 135 fn try_allocate<E: core::fmt::Debug>( 136 initial_value_provider: impl FnOnce() -> Result<$wrapped_type, E>, 137 ) -> Result<Self, $crate::HandleMapTryAllocateError<E>> { 138 GLOBAL_HANDLE_MAP 139 .try_allocate(initial_value_provider) 140 .map(|derived_handle| Self { handle_id: derived_handle.get_id() }) 141 } 142 fn allocate( 143 initial_value_provider: impl FnOnce() -> $wrapped_type, 144 ) -> Result<Self, $crate::HandleMapFullError> { 145 GLOBAL_HANDLE_MAP 146 .allocate(initial_value_provider) 147 .map(|derived_handle| Self { handle_id: derived_handle.get_id() }) 148 } 149 fn get( 150 &self, 151 ) -> Result<$crate::ObjectReadGuardImpl<$wrapped_type>, $crate::HandleNotPresentError> 152 { 153 GLOBAL_HANDLE_MAP.get(self.get_as_handle()) 154 } 155 fn get_mut( 156 &self, 157 ) -> Result< 158 $crate::ObjectReadWriteGuardImpl<$wrapped_type>, 159 $crate::HandleNotPresentError, 160 > { 161 GLOBAL_HANDLE_MAP.get_mut(self.get_as_handle()) 162 } 163 fn deallocate(self) -> Result<$wrapped_type, $crate::HandleNotPresentError> { 164 GLOBAL_HANDLE_MAP.deallocate(self.get_as_handle()) 165 } 166 } 167 } 168 }; 169 } 170