• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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