1 | //! [![github]](https://github.com/dtolnay/inventory) [![crates-io]](https://crates.io/crates/inventory) [![docs-rs]](https://docs.rs/inventory) |
2 | //! |
3 | //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github |
4 | //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust |
5 | //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs |
6 | //! |
7 | //! <br> |
8 | //! |
9 | //! **Typed distributed plugin registration.** |
10 | //! |
11 | //! This crate provides a way to set up a plugin registry into which plugins |
12 | //! can be registered from any source file linked into your application. There |
13 | //! does not need to be a central list of all the plugins. |
14 | //! |
15 | //! # Examples |
16 | //! |
17 | //! Suppose we are writing a command line flags library and want to allow any |
18 | //! source file in the application to register command line flags that are |
19 | //! relevant to it. |
20 | //! |
21 | //! This is the flag registration style used by [gflags] and is better suited |
22 | //! for large scale development than maintaining a single central list of flags, |
23 | //! as the central list would become an endless source of merge conflicts in an |
24 | //! application developed simultaneously by thousands of developers. |
25 | //! |
26 | //! [gflags]: https://gflags.github.io/gflags/ |
27 | //! |
28 | //! ## Instantiating the plugin registry |
29 | //! |
30 | //! Let's use a `struct Flag` as the plugin type, which will contain the short |
31 | //! name of the flag like `-v`, the full name like `--verbose`, and maybe other |
32 | //! information like argument type and help text. We instantiate a plugin |
33 | //! registry with an invocation of `inventory::collect!`. |
34 | //! |
35 | //! ``` |
36 | //! pub struct Flag { |
37 | //! short: char, |
38 | //! name: &'static str, |
39 | //! /* ... */ |
40 | //! } |
41 | //! |
42 | //! impl Flag { |
43 | //! pub const fn new(short: char, name: &'static str) -> Self { |
44 | //! Flag { short, name } |
45 | //! } |
46 | //! } |
47 | //! |
48 | //! inventory::collect!(Flag); |
49 | //! ``` |
50 | //! |
51 | //! This `collect!` call must be in the same crate that defines the plugin type. |
52 | //! This macro does not "run" anything so place it outside of any function body. |
53 | //! |
54 | //! ## Registering plugins |
55 | //! |
56 | //! Now any crate with access to the `Flag` type can register flags as a plugin. |
57 | //! Plugins can be registered by the same crate that declares the plugin type, |
58 | //! or by any downstream crate. |
59 | //! |
60 | //! ``` |
61 | //! # struct Flag; |
62 | //! # |
63 | //! # impl Flag { |
64 | //! # const fn new(short: char, name: &'static str) -> Self { |
65 | //! # Flag |
66 | //! # } |
67 | //! # } |
68 | //! # |
69 | //! # inventory::collect!(Flag); |
70 | //! # |
71 | //! inventory::submit! { |
72 | //! Flag::new('v' , "verbose" ) |
73 | //! } |
74 | //! # |
75 | //! # fn main() {} |
76 | //! ``` |
77 | //! |
78 | //! The `submit!` macro does not "run" anything so place it outside of any |
79 | //! function body. In particular, note that all `submit!` invocations across all |
80 | //! source files linked into your application all take effect simultaneously. A |
81 | //! `submit!` invocation is not a statement that needs to be called from `main` |
82 | //! in order to execute. |
83 | //! |
84 | //! ## Iterating over plugins |
85 | //! |
86 | //! The value `inventory::iter::<T>` is an iterator with element type `&'static |
87 | //! T` that iterates over all plugins registered of type `T`. |
88 | //! |
89 | //! ``` |
90 | //! # struct Flag { |
91 | //! # short: char, |
92 | //! # name: &'static str, |
93 | //! # } |
94 | //! # |
95 | //! # inventory::collect!(Flag); |
96 | //! # |
97 | //! for flag in inventory::iter::<Flag> { |
98 | //! println!("-{}, --{}" , flag.short, flag.name); |
99 | //! } |
100 | //! ``` |
101 | //! |
102 | //! There is no guarantee about the order that plugins of the same type are |
103 | //! visited by the iterator. They may be visited in any order. |
104 | //! |
105 | //! ## WebAssembly and constructors |
106 | //! |
107 | //! `inventory` supports all WebAssembly targets, including |
108 | //! `wasm*-unknown-unknown`. However, in unusual circumstances, ensuring that |
109 | //! constructors run may require some extra effort. The Wasm linker will |
110 | //! synthesize a function `extern "C" unsafe fn __wasm_call_ctors()` which calls |
111 | //! all constructors when invoked; this function will *not* be exported from the |
112 | //! module unless you do so explicitly. Depending on the result of a heuristic, |
113 | //! the linker may or may not insert a call to this function from the beginning |
114 | //! of every function that your module exports. Specifically, it regards a |
115 | //! module as having "command-style linkage" if: |
116 | //! |
117 | //! * it is not relocatable; |
118 | //! * it is not a position-independent executable; |
119 | //! * and it does not call `__wasm_call_ctors`, directly or indirectly, from any |
120 | //! exported function. |
121 | //! |
122 | //! The linker expects that the embedder will call into a command-style module |
123 | //! only once per instantiation. Violation of this expectation can result in |
124 | //! `__wasm_call_ctors` being called multiple times. This is dangerous in |
125 | //! general, but safe and mostly harmless in the case of constructors generated |
126 | //! by `inventory`, which are idempotent. |
127 | //! |
128 | //! If you are building a module which relies on constructors and may be called |
129 | //! into multiple times per instance, you should export `__wasm_call_ctors` (or |
130 | //! a wrapper around it) and ensure that the embedder calls it immediately after |
131 | //! instantiation. Even though `inventory` may work fine without this, it is |
132 | //! still good practice, because it avoids unnecessary overhead from repeated |
133 | //! constructor invocation. It also can prevent unsoundness if some of your |
134 | //! constructors are generated by other crates or other programming languages. |
135 | //! |
136 | //! ``` |
137 | //! #[cfg(target_family = "wasm" )] |
138 | //! unsafe extern "C" { |
139 | //! fn __wasm_call_ctors(); |
140 | //! } |
141 | //! |
142 | //! fn main() { |
143 | //! #[cfg (target_family = "wasm" )] |
144 | //! unsafe { |
145 | //! __wasm_call_ctors(); |
146 | //! } |
147 | //! } |
148 | //! ``` |
149 | |
150 | #![doc (html_root_url = "https://docs.rs/inventory/0.3.20" )] |
151 | #![no_std ] |
152 | #![deny (unsafe_op_in_unsafe_fn)] |
153 | #![allow ( |
154 | clippy::doc_markdown, |
155 | clippy::empty_enum, |
156 | clippy::expl_impl_clone_on_copy, |
157 | clippy::let_underscore_untyped, |
158 | clippy::let_unit_value, |
159 | clippy::must_use_candidate, |
160 | clippy::new_without_default, |
161 | clippy::semicolon_if_nothing_returned, // https://github.com/rust-lang/rust-clippy/issues/7324 |
162 | )] |
163 | |
164 | // Not public API. |
165 | #[doc (hidden)] |
166 | pub extern crate core; |
167 | |
168 | use core::cell::UnsafeCell; |
169 | use core::marker::PhantomData; |
170 | use core::ops::Deref; |
171 | use core::ptr; |
172 | #[cfg (target_family = "wasm" )] |
173 | use core::sync::atomic::AtomicBool; |
174 | use core::sync::atomic::{AtomicPtr, Ordering}; |
175 | |
176 | // Not public API. Used by generated code. |
177 | #[doc (hidden)] |
178 | pub struct Registry { |
179 | head: AtomicPtr<Node>, |
180 | } |
181 | |
182 | // Not public API. Used by generated code. |
183 | #[doc (hidden)] |
184 | pub struct Node { |
185 | pub value: &'static dyn ErasedNode, |
186 | pub next: UnsafeCell<Option<&'static Node>>, |
187 | #[cfg (target_family = "wasm" )] |
188 | pub initialized: AtomicBool, |
189 | } |
190 | |
191 | // The `value` is Sync, and `next` is only mutated during submit, which is prior |
192 | // to any reads. |
193 | unsafe impl Sync for Node {} |
194 | |
195 | // Not public API. Used by generated code. |
196 | #[doc (hidden)] |
197 | pub trait ErasedNode: Sync { |
198 | // SAFETY: requires *node.value is of type Self. |
199 | unsafe fn submit(&self, node: &'static Node); |
200 | } |
201 | |
202 | impl<T: Collect> ErasedNode for T { |
203 | unsafe fn submit(&self, node: &'static Node) { |
204 | unsafe { |
205 | T::registry().submit(new:node); |
206 | } |
207 | } |
208 | } |
209 | |
210 | /// Trait bound corresponding to types that can be iterated by inventory::iter. |
211 | /// |
212 | /// This trait cannot be implemented manually. Instead use the [`collect`] macro |
213 | /// which expands to an implementation of this trait for the given type. |
214 | /// |
215 | /// # Examples |
216 | /// |
217 | /// ``` |
218 | /// use inventory::Collect; |
219 | /// |
220 | /// fn count_plugins<T: Collect>() -> usize { |
221 | /// inventory::iter::<T>.into_iter().count() |
222 | /// } |
223 | /// ``` |
224 | pub trait Collect: Sync + Sized + 'static { |
225 | #[doc (hidden)] |
226 | fn registry() -> &'static Registry; |
227 | } |
228 | |
229 | impl Registry { |
230 | // Not public API. Used by generated code. |
231 | pub const fn new() -> Self { |
232 | Registry { |
233 | head: AtomicPtr::new(ptr::null_mut()), |
234 | } |
235 | } |
236 | |
237 | // SAFETY: requires type of *new.value matches the $ty surrounding the |
238 | // declaration of this registry in inventory::collect macro. |
239 | unsafe fn submit(&'static self, new: &'static Node) { |
240 | // The WebAssembly linker uses an unreliable heuristic to determine |
241 | // whether a module is a "command-style" linkage, for which it will |
242 | // insert a call to `__wasm_call_ctors` at the top of every exported |
243 | // function. It expects that the embedder will call into such modules |
244 | // only once per instantiation. If this heuristic goes wrong, we can end |
245 | // up having our constructors invoked multiple times, which without this |
246 | // safeguard would lead to our registry's linked list becoming circular. |
247 | // On non-Wasm platforms, this check is unnecessary, so we skip it. |
248 | #[cfg (target_family = "wasm" )] |
249 | if new.initialized.swap(true, Ordering::Relaxed) { |
250 | return; |
251 | } |
252 | |
253 | let mut head = self.head.load(Ordering::Relaxed); |
254 | loop { |
255 | unsafe { |
256 | *new.next.get() = head.as_ref(); |
257 | } |
258 | let new_ptr = new as *const Node as *mut Node; |
259 | match self |
260 | .head |
261 | .compare_exchange(head, new_ptr, Ordering::Release, Ordering::Relaxed) |
262 | { |
263 | Ok(_) => return, |
264 | Err(prev) => head = prev, |
265 | } |
266 | } |
267 | } |
268 | } |
269 | |
270 | /// An iterator over plugins registered of a given type. |
271 | /// |
272 | /// The value `inventory::iter::<T>` is an iterator with element type `&'static |
273 | /// T`. |
274 | /// |
275 | /// There is no guarantee about the order that plugins of the same type are |
276 | /// visited by the iterator. They may be visited in any order. |
277 | /// |
278 | /// # Examples |
279 | /// |
280 | /// ``` |
281 | /// # struct Flag { |
282 | /// # short: char, |
283 | /// # name: &'static str, |
284 | /// # } |
285 | /// # |
286 | /// # inventory::collect!(Flag); |
287 | /// # |
288 | /// # const IGNORE: &str = stringify! { |
289 | /// use my_flags::Flag; |
290 | /// # }; |
291 | /// |
292 | /// fn main() { |
293 | /// for flag in inventory::iter::<Flag> { |
294 | /// println!("-{}, --{}" , flag.short, flag.name); |
295 | /// } |
296 | /// } |
297 | /// ``` |
298 | /// |
299 | /// Refer to the [crate level documentation](index.html) for a complete example |
300 | /// of instantiating a plugin registry and submitting plugins. |
301 | #[allow (non_camel_case_types)] |
302 | pub type iter<T> = private::iter<T>; |
303 | |
304 | mod void_iter { |
305 | enum Void {} |
306 | |
307 | #[repr (C, packed)] |
308 | pub struct Iter<T>([*const T; 0], Void); |
309 | |
310 | unsafe impl<T> Send for Iter<T> {} |
311 | unsafe impl<T> Sync for Iter<T> {} |
312 | } |
313 | |
314 | mod value_iter { |
315 | #[doc (hidden)] |
316 | pub use crate::private::iter::iter; |
317 | } |
318 | |
319 | mod private { |
320 | // Based on https://github.com/dtolnay/ghost |
321 | #[allow (non_camel_case_types)] |
322 | pub enum iter<T> { |
323 | __Phantom(crate::void_iter::Iter<T>), |
324 | iter, |
325 | } |
326 | |
327 | #[doc (hidden)] |
328 | pub use crate::value_iter::*; |
329 | } |
330 | |
331 | #[doc (hidden)] |
332 | pub use crate::private::*; |
333 | |
334 | const _: () = { |
335 | fn into_iter<T: Collect>() -> Iter<T> { |
336 | let head = T::registry().head.load(Ordering::Acquire); |
337 | Iter { |
338 | // Head pointer is always null or valid &'static Node. |
339 | node: unsafe { head.as_ref() }, |
340 | marker: PhantomData, |
341 | } |
342 | } |
343 | |
344 | impl<T: Collect> IntoIterator for iter<T> { |
345 | type Item = &'static T; |
346 | type IntoIter = Iter<T>; |
347 | |
348 | fn into_iter(self) -> Self::IntoIter { |
349 | into_iter() |
350 | } |
351 | } |
352 | |
353 | #[doc (hidden)] |
354 | impl<T: Collect> Deref for iter<T> { |
355 | type Target = fn() -> Iter<T>; |
356 | fn deref(&self) -> &Self::Target { |
357 | &(into_iter as fn() -> Iter<T>) |
358 | } |
359 | } |
360 | |
361 | pub struct Iter<T: 'static> { |
362 | node: Option<&'static Node>, |
363 | marker: PhantomData<T>, |
364 | } |
365 | |
366 | impl<T: 'static> Iterator for Iter<T> { |
367 | type Item = &'static T; |
368 | |
369 | fn next(&mut self) -> Option<Self::Item> { |
370 | let node = self.node?; |
371 | unsafe { |
372 | let value_ptr = (node.value as *const dyn ErasedNode).cast::<T>(); |
373 | self.node = *node.next.get(); |
374 | Some(&*value_ptr) |
375 | } |
376 | } |
377 | } |
378 | |
379 | impl<T> Clone for Iter<T> { |
380 | fn clone(&self) -> Self { |
381 | Self { |
382 | node: self.node, |
383 | marker: PhantomData, |
384 | } |
385 | } |
386 | } |
387 | }; |
388 | |
389 | /// Associate a plugin registry with the specified type. |
390 | /// |
391 | /// This call must be in the same crate that defines the plugin type. This macro |
392 | /// does not "run" anything so place it outside of any function body. |
393 | /// |
394 | /// # Examples |
395 | /// |
396 | /// Suppose we are writing a command line flags library and want to allow any |
397 | /// source file in the application to register command line flags that are |
398 | /// relevant to it. |
399 | /// |
400 | /// This is the flag registration style used by [gflags] and is better suited |
401 | /// for large scale development than maintaining a single central list of flags, |
402 | /// as the central list would become an endless source of merge conflicts. |
403 | /// |
404 | /// [gflags]: https://gflags.github.io/gflags/ |
405 | /// |
406 | /// ``` |
407 | /// pub struct Flag { |
408 | /// short: char, |
409 | /// name: &'static str, |
410 | /// /* ... */ |
411 | /// } |
412 | /// |
413 | /// inventory::collect!(Flag); |
414 | /// ``` |
415 | /// |
416 | /// Refer to the [crate level documentation](index.html) for a complete example |
417 | /// of submitting plugins and iterating a plugin registry. |
418 | #[macro_export ] |
419 | macro_rules! collect { |
420 | ($ty:ty) => { |
421 | impl $crate::Collect for $ty { |
422 | #[inline] |
423 | fn registry() -> &'static $crate::Registry { |
424 | static REGISTRY: $crate::Registry = $crate::Registry::new(); |
425 | ®ISTRY |
426 | } |
427 | } |
428 | }; |
429 | } |
430 | |
431 | /// Enter an element into the plugin registry corresponding to its type. |
432 | /// |
433 | /// This call may be in the same crate that defines the type, or downstream in |
434 | /// any crate that depends on that crate. |
435 | /// |
436 | /// This macro does not "run" anything so place it outside of any function body. |
437 | /// In particular, note that all `submit!` invocations across all source files |
438 | /// linked into your application all take effect simultaneously. A `submit!` |
439 | /// invocation is not a statement that needs to be called from `main` in order |
440 | /// to execute. |
441 | /// |
442 | /// # Examples |
443 | /// |
444 | /// Put `submit!` invocations outside of any function body. |
445 | /// |
446 | /// ``` |
447 | /// # struct Flag; |
448 | /// # |
449 | /// # impl Flag { |
450 | /// # const fn new(short: char, name: &'static str) -> Self { |
451 | /// # Flag |
452 | /// # } |
453 | /// # } |
454 | /// # |
455 | /// # inventory::collect!(Flag); |
456 | /// # |
457 | /// inventory::submit! { |
458 | /// Flag::new('v' , "verbose" ) |
459 | /// } |
460 | /// # |
461 | /// # fn main() {} |
462 | /// ``` |
463 | /// |
464 | /// Do not try to invoke `submit!` from inside of a function body as it does not |
465 | /// do what you want. |
466 | /// |
467 | /// ```compile_fail |
468 | /// // Do not do this. |
469 | /// fn submit_flags(has_verbose_flag: bool) { |
470 | /// if has_verbose_flag { |
471 | /// inventory::submit! { |
472 | /// Flag::new('v' , "verbose" ) |
473 | /// } |
474 | /// } |
475 | /// } |
476 | /// ``` |
477 | /// |
478 | /// Refer to the [crate level documentation](index.html) for a complete example |
479 | /// of instantiating and iterating a plugin registry. |
480 | #[macro_export ] |
481 | macro_rules! submit { |
482 | ($($value:tt)*) => { |
483 | $crate::__do_submit! { |
484 | { $($value)* } |
485 | { $($value)* } |
486 | } |
487 | }; |
488 | } |
489 | |
490 | // Not public API. |
491 | #[cfg (target_family = "wasm" )] |
492 | #[doc (hidden)] |
493 | pub mod __private { |
494 | #[doc (hidden)] |
495 | pub use rustversion::attr; |
496 | } |
497 | |
498 | // Not public API. |
499 | #[doc (hidden)] |
500 | #[macro_export ] |
501 | macro_rules! __do_submit { |
502 | (used={ $($used:tt)+ } $($value:tt)*) => { |
503 | #[allow(non_upper_case_globals)] |
504 | const _: () = { |
505 | static __INVENTORY: $crate::Node = $crate::Node { |
506 | value: &{ $($value)* }, |
507 | next: $crate::core::cell::UnsafeCell::new($crate::core::option::Option::None), |
508 | #[cfg(target_family = "wasm" )] |
509 | initialized: $crate::core::sync::atomic::AtomicBool::new(false), |
510 | }; |
511 | |
512 | #[cfg_attr(any(target_os = "linux" , target_os = "android" ), link_section = ".text.startup" )] |
513 | unsafe extern "C" fn __ctor() { |
514 | unsafe { $crate::ErasedNode::submit(__INVENTORY.value, &__INVENTORY) } |
515 | } |
516 | |
517 | // Linux/ELF: https://www.exploit-db.com/papers/13234 |
518 | // |
519 | // macOS: https://blog.timac.org/2016/0716-constructor-and-destructor-attributes/ |
520 | // |
521 | // Why .CRT$XCU on Windows? https://www.cnblogs.com/sunkang/archive/2011/05/24/2055635.html |
522 | // 'I'=C init, 'C'=C++ init, 'P'=Pre-terminators and 'T'=Terminators |
523 | $($used)+ |
524 | #[cfg_attr( |
525 | all( |
526 | not(target_family = "wasm" ), |
527 | any( |
528 | target_os = "linux" , |
529 | target_os = "android" , |
530 | target_os = "dragonfly" , |
531 | target_os = "freebsd" , |
532 | target_os = "haiku" , |
533 | target_os = "illumos" , |
534 | target_os = "netbsd" , |
535 | target_os = "openbsd" , |
536 | target_os = "none" , |
537 | ) |
538 | ), |
539 | link_section = ".init_array" , |
540 | )] |
541 | #[cfg_attr( |
542 | target_family = "wasm" , |
543 | $crate::__private::attr( |
544 | any(all(stable, since(1.85)), since(2024-12-18)), |
545 | link_section = ".init_array" , |
546 | ), |
547 | )] |
548 | #[cfg_attr( |
549 | any(target_os = "macos" , target_os = "ios" ), |
550 | link_section = "__DATA,__mod_init_func" , |
551 | )] |
552 | #[cfg_attr(windows, link_section = ".CRT$XCU" )] |
553 | static __CTOR: unsafe extern "C" fn() = __ctor; |
554 | }; |
555 | }; |
556 | |
557 | ({ #![used($($used:tt)+)] $($value:tt)* } { $pound:tt $bang:tt $brackets:tt $($dup:tt)* }) => { |
558 | $crate::__do_submit! { |
559 | used={ $pound $brackets } |
560 | $($value)* |
561 | } |
562 | }; |
563 | |
564 | ({ $($value:tt)* } { $($dup:tt)* }) => { |
565 | $crate::__do_submit! { |
566 | used={ #[used] } |
567 | $($value)* |
568 | } |
569 | }; |
570 | } |
571 | |