#![doc = __internal_module_doc!("crate", "", "")]
#![doc = include_str!("./common.md")]
#[rustfmt::skip]
#[doc(hidden)]
pub macro __internal_module_doc_toc() {r#"
- [Binders](#binders)
- [Initialization Order](#initialization-order)
- [Planned Features](#planned-features) "#}
#[rustfmt::skip]
#[doc(hidden)]
#[macropol::macropol] pub macro __internal_module_doc($r3_core:expr, $admonitions:expr, $toc:expr) {
r#"
Bindings ([`Bind`][]), a static storage with [runtime initialization][1] and
[configuration-time][2] borrow checking.
$admonitions
Bindings are essentially fancy global variables defined in a kernel
configuration. They are defined by [`Bind::define`][] and initialized by
provided closures at runtime. They can be consumed or borrowed by the entry
points of [executable kernel objects][4] or the initializers of another
bindings, with a dependency graph explicitly defined by the passing of
[*binders*][15].
The configuration system tracks the usage of bindings and employs static checks
to ensure that the borrowing rules are followed by the users of the bindings. It
aborts the compilation if the rules may be violated.
Bindings use hunks ([`Hunk`][3]) as a storage for their contents. Bindings are
initialized in [startup hooks][14], where [CPU Lock][17] is active, <!--
[ref:startup_hook_cpu_lock_active] --> and therefore most kernel services
are unavailable.
<div class="admonition-follows"></div>
> **Relation to Other Specifications:** [*Resources*][8] in [RTIC 1][7] serve
> the similar need with a quite different design.
> In R3, bindings are defined in modular, encapsulated configuration functions
> and associated to various kernel objects. The configuration system takes all
> definitions and figures out the correct initialization order.
> In RTIC, all resources are defined in one place and initialized by an
> application-provided `#[init]` function.
$toc
# Binders
A *binder* ([`Binder`][]) represents a specific borrow mode of a binding. A
configuration function creates a binding by calling one of [`Bind`][]'s methods
and uses it in the definition of another object where the binding is intended to
be consumed, i.e., borrowed or moved out by its entry point or initializer. The
type a binder produces is called its *materialized* form.
```rust,ignore
use r3::{bind::bind, kernel::StaticTimer, prelude::*};
let count = bind((), || 0).finish(cfg);
// ^^ ^^
// .--' '--------,
// no binders no materialized values
StaticTimer::define()
// n.b. `(x,)` is a one-element tuple
.start_with_bind((count.borrow_mut(),), |count: &mut i32| {
// ^^^^^^^^^^^^^^^^^^ ^^^^^^^^
// | |
// BindBorrowMut<'_, _, i32> &'call mut i32
// gives... for some lifetime 'call
})
.finish(b);
```
The only way for safe code to make a binding available to runtime code in a
meaningful way is to include a binder in a dependency list (e.g., the `binder`
parameter of [`ExecutableDefinerExt::start_with_bind`][]) as shown above or to
call [`Bind::as_ref`][] to create a [`BindRef`][], which is directly consumable
in runtime code. Most binders can't escape from a configuration function.
The following table lists all provided binders:
| `Bind::` | Type | Confers | On binding | On executable |
| ---------------- | ------------------- | ---------------- | :--------: | :-----------: |
| [`borrow`][] | [`BindBorrow`][] | `&'call T` | ✓ | ✓ |
| [`borrow_mut`][] | [`BindBorrowMut`][] | `&'call mut T` | ✓ | ✓ |
| [`take_ref`][] | [`BindTakeRef`][] | `&'static T` | ✓ | ✓ |
| [`take_mut`][] | [`BindTakeMut`][] | `&'static mut T` | ✓ | |
| [`take`][] | [`BindTake`][] | `T` | ✓ | |
| [`as_ref`][] | [`BindRef`][] | `&'static T` | | ✓ |
[`borrow`]: Bind::borrow
[`borrow_mut`]: Bind::borrow_mut
[`take_ref`]: Bind::take_ref
[`take_mut`]: Bind::take_mut
[`take`]: Bind::take
[`as_ref`]: Bind::as_ref
- The **`Bind::`** column shows the methods to create the binders.
- The **Type** column shows the types representing the binders.
- The **Confers** column shows the respective materialized forms of the binders.
The lifetime `'call` represents the call duration of the consuming function.
- For `&'call T` and `&'call mut T`, the caller has the freedom of choosing
an arbitrary lifetime, so the consuming function must be generic over any
lifetimes. In function and closure parameters, reference types without
explicit lifetimes are automatically made generic in this way owing to [the
lifetime elision rules][16].
- The **On binding** column shows which types of binders can be consumed by
another binding's initializer via [`BindDefiner::init_with_bind`][].
- The **On executable** column shows which types of binders can be consumed
by [executable objects][10], viz., [tasks][11], [interrupt handlers][12], and
[timers][13], via [`ExecutableDefinerExt::start_with_bind`][].
- An executable object may execute its entry point for multiple times
throughout its lifetime. For this reason, an executable object is not
allowed to consume `BindTake` (which moves out the value) or `BindTakeMut`
(which mutably borrows the value indefinitely).
# Initialization Order
The configuration system determines the initialization order of the defined
bindings by [topological sorting][5] with a preference toward the definition
order. The specific algorithm is not a part of the stability guarantee.
# Planned Features
The following features are planned and may be implemented in the future:
- Reusing the storage of a binding whose lifetime has ended by having its
contents moved out by [`BindTake`][] or completing its last borrow.
<!-- [tag:bind_storage_reuse] -->
- Pruning unused bindings, unless they are marked as [`unpure`][6].
<!-- [ref:unpure_binding] -->
- Phantom edges to enforce ordering between bindings.
- Pinning.
[1]: BindDefiner::init
[2]: $r3_core#static-configuration
[3]: crate::kernel::Hunk
[4]: ExecutableDefiner
[5]: https://en.wikipedia.org/wiki/Topological_sorting
[6]: BindDefiner::unpure
[7]: https://rtic.rs/1/book/en/
[8]: https://rtic.rs/1/book/en/by-example/resources.html
[9]: https://www.toppers.jp/index.html
[10]: ExecutableDefiner
[11]: crate::kernel::StaticTask
[12]: crate::kernel::StaticInterruptHandler
[13]: crate::kernel::StaticTimer
[14]: crate::kernel::StartupHook
[15]: #binders
[16]: https://doc.rust-lang.org/1.58.1/reference/lifetime-elision.html#lifetime-elision-in-functions
[17]: $r3_core#system-states
"#
}
use core::{cell::UnsafeCell, marker::PhantomData, mem::MaybeUninit};
use crate::{
closure::Closure,
hunk::Hunk,
kernel::{self, cfg, prelude::*, raw, raw_cfg, StartupHook},
utils::{refcell::RefCell, ComptimeVec, ConstAllocator, Init, PhantomInvariant, ZeroInit},
};
mod sorter;
mod tests_impls;
pub const INIT_HOOK_PRIORITY: i32 = 0x4000_0000;
#[doc(hidden)]
#[repr(transparent)]
pub struct BindData<T>(UnsafeCell<MaybeUninit<T>>);
unsafe impl<T> Sync for BindData<T> {}
unsafe impl<T> Send for BindData<T> {}
type BindHunk<System, T> = Hunk<System, BindData<T>>;
impl<T> BindData<T> {
#[inline]
unsafe fn assume_init_ref(&self) -> &T {
unsafe { (*self.0.get()).assume_init_ref() }
}
#[inline]
#[allow(clippy::mut_from_ref)]
unsafe fn assume_init_mut(&self) -> &mut T {
unsafe { (*self.0.get()).assume_init_mut() }
}
}
unsafe impl<T> ZeroInit for BindData<T> {}
pub struct Bind<'pool, System, T> {
hunk: BindHunk<System, T>,
bind_registry: &'pool RefCell<CfgBindRegistry>,
bind_i: usize,
}
impl<System, T> Copy for Bind<'_, System, T> {}
impl<System, T> const Clone for Bind<'_, System, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
pub struct BindBorrow<'pool, System, T>(Bind<'pool, System, T>);
pub struct BindBorrowMut<'pool, System, T>(Bind<'pool, System, T>);
pub struct BindTake<'pool, System, T>(Bind<'pool, System, T>);
pub struct BindTakeRef<'pool, System, T>(Bind<'pool, System, T>);
pub struct BindTakeMut<'pool, System, T>(Bind<'pool, System, T>);
pub struct BindRef<System, T>(BindHunk<System, T>);
impl<System, T> Copy for BindRef<System, T> {}
impl<System, T> const Clone for BindRef<System, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<'pool, System> Bind<'pool, System, ()> {
pub const fn define() -> BindDefiner<
System,
private_bind_definer::BinderUnspecified,
private_bind_definer::FuncUnspecified,
> {
BindDefiner::new()
}
}
impl<'pool, System, T> Bind<'pool, System, T> {
pub const fn borrow(&self) -> BindBorrow<'pool, System, T>
where
T: Sync,
{
BindBorrow(*self)
}
pub const fn borrow_mut(&self) -> BindBorrowMut<'pool, System, T>
where
T: Send,
{
BindBorrowMut(*self)
}
pub const fn take(&self) -> BindTake<'pool, System, T>
where
T: Send,
{
BindTake(*self)
}
pub const fn take_ref(&self) -> BindTakeRef<'pool, System, T>
where
T: Sync,
{
BindTakeRef(*self)
}
pub const fn take_mut(&self) -> BindTakeMut<'pool, System, T>
where
T: Send,
{
BindTakeMut(*self)
}
pub const fn as_ref(&self) -> BindRef<System, T>
where
T: Sync,
{
self.bind_registry.borrow_mut().binds[self.bind_i]
.users
.push((BindUsage::Executable, BindBorrowType::TakeRef));
BindRef(self.hunk)
}
}
impl<'pool, System, T> Bind<'pool, System, T> {
pub const fn hunk(&self) -> BindHunk<System, T> {
self.hunk
}
}
#[doc = include_str!("./common.md")]
#[must_use = "must call `finish()` to complete registration"]
pub struct BindDefiner<System, Binder, Func> {
_phantom: PhantomInvariant<System>,
binder: Binder,
func: Option<Func>,
}
mod private_bind_definer {
pub struct BinderUnspecified;
pub struct FuncUnspecified;
}
impl<System>
BindDefiner<
System,
private_bind_definer::BinderUnspecified,
private_bind_definer::FuncUnspecified,
>
{
const fn new() -> Self {
Self {
_phantom: Init::INIT,
binder: private_bind_definer::BinderUnspecified,
func: Some(private_bind_definer::FuncUnspecified),
}
}
}
impl<System>
BindDefiner<
System,
private_bind_definer::BinderUnspecified,
private_bind_definer::FuncUnspecified,
>
{
pub const fn init<Func>(self, func: Func) -> BindDefiner<System, (), Func> {
BindDefiner {
func: Some(func),
binder: (),
..self
}
}
pub const fn init_with_bind<Binder, Func>(
self,
binder: Binder,
func: Func,
) -> BindDefiner<System, Binder, Func> {
BindDefiner {
func: Some(func),
binder,
..self
}
}
pub const fn zeroed<T: ZeroInit>(self) -> BindDefiner<System, (), FnBindNever<T>> {
unsafe { self.zeroed_unchecked() }
}
pub const unsafe fn zeroed_unchecked<T>(self) -> BindDefiner<System, (), FnBindNever<T>> {
BindDefiner {
func: None,
binder: (),
..self
}
}
pub const unsafe fn uninit_unchecked<T>(self) -> BindDefiner<System, (), FnBindNever<T>> {
unsafe { self.zeroed_unchecked() }
}
}
impl<System, Binder, Func> BindDefiner<System, Binder, Func> {
pub const fn unpure(self) -> Self {
self
}
}
impl<System, Binder, Func> BindDefiner<System, Binder, Func> {
pub const fn finish<'pool, C>(
self,
cfg: &mut cfg::Cfg<'pool, C>,
) -> Bind<'pool, System, <Func as FnBind<Binder>>::Output>
where
C: ~const raw_cfg::CfgBase<System = System>,
System: raw::KernelBase + cfg::KernelStatic,
Func: ~const FnBind<Binder>,
{
let hunk = BindHunk::define().zeroed().finish(cfg);
let bind_registry = &cfg.shared.bind_registry;
let bind_i = bind_registry.borrow().binds.len();
let initializer = if let Some(func) = self.func {
let mut ctx = CfgBindCtx {
_phantom: &(),
usage: BindUsage::Bind(bind_i),
};
let initializer = func.bind(self.binder, &mut ctx);
Some(Closure::from_fn_const(
#[inline]
move || {
let output = initializer();
unsafe { hunk.0.get().write(MaybeUninit::new(output)) };
},
))
} else {
core::mem::forget((self.func, self.binder));
None
};
{
let mut bind_registry = bind_registry.borrow_mut();
assert!(bind_i == bind_registry.binds.len());
let allocator = bind_registry.binds.allocator().clone();
bind_registry
.binds
.push(CfgBindInfo::new(initializer, allocator));
}
Bind {
hunk,
bind_registry,
bind_i,
}
}
}
struct DivideBind<'pool, System, T> {
original_bind: Bind<'pool, System, T>,
collector_bind_i: usize,
}
impl<'pool, System, T> DivideBind<'pool, System, T> {
const fn new(original_bind: Bind<'pool, System, T>) -> Self {
let collector_bind_i;
{
let mut bind_registry = original_bind.bind_registry.borrow_mut();
collector_bind_i = bind_registry.binds.len();
let allocator = bind_registry.binds.allocator().clone();
bind_registry.binds.push(CfgBindInfo::new(None, allocator));
bind_registry.binds[original_bind.bind_i]
.users
.push((BindUsage::Bind(collector_bind_i), BindBorrowType::TakeMut));
}
Self {
original_bind,
collector_bind_i,
}
}
const fn original_hunk(&self) -> BindHunk<System, T> {
self.original_bind.hunk
}
const unsafe fn slice<U>(&self, hunk: BindHunk<System, U>) -> Bind<'pool, System, U> {
let emitter_bind_i;
{
let mut bind_registry = self.original_bind.bind_registry.borrow_mut();
emitter_bind_i = bind_registry.binds.len();
let allocator = bind_registry.binds.allocator().clone();
bind_registry.binds.push(CfgBindInfo::new(None, allocator));
bind_registry.binds[self.collector_bind_i]
.users
.push((BindUsage::Bind(emitter_bind_i), BindBorrowType::TakeRef));
}
Bind {
hunk,
bind_registry: self.original_bind.bind_registry,
bind_i: emitter_bind_i,
}
}
}
#[doc = include_str!("./common.md")]
#[const_trait]
pub trait UnzipBind {
type Target;
fn unzip(self) -> Self::Target;
}
trait TupleFieldOffsets {
const FIELD_OFFSETS: &'static [usize];
}
macro_rules! impl_unzip_bind_for_tuples {
( @start $($x:tt)* ) => {
impl_unzip_bind_for_tuples! { @iter [] [$($x)*] }
};
( @iter
[$(($FieldI:ident, $I:tt))*]
[$next_head:tt $($next_tail:tt)*]
) => {
impl_unzip_bind_for_tuples! { @iter [$(($FieldI, $I))* $next_head] [$($next_tail)*] }
impl<$( $FieldI, )*> TupleFieldOffsets for ($( $FieldI, )*) {
const FIELD_OFFSETS: &'static [usize] = &[
$( memoffset::offset_of_tuple!(Self, $I) ),*
];
}
impl<
System,
$( $FieldI, )*
> TupleFieldOffsets for Bind<'_, System, ($( $FieldI, )*)> {
const FIELD_OFFSETS: &'static [usize] = <($( $FieldI, )*)>::FIELD_OFFSETS;
}
impl<
'pool,
System,
$( $FieldI, )*
> const UnzipBind for Bind<'pool, System, ( $( $FieldI, )* )>
{
type Target = ( $( Bind<'pool, System, $FieldI>, )* );
fn unzip(self) -> Self::Target {
#[allow(unused_unsafe)] #[allow(clippy::unused_unit)] unsafe {
let divide = DivideBind::new(self);
let hunk = divide.original_hunk();
let _ = hunk;
($(
divide.slice::<$FieldI>(
hunk.transmute::<u8>()
.wrapping_offset(Self::FIELD_OFFSETS[$I] as isize)
.transmute()
),
)*)
}
}
}
};
( @iter [$($_discard:tt)*] [] ) => {}
}
seq_macro::seq!(I in 0..16 { impl_unzip_bind_for_tuples! { @start #( (Field~I, I) )* } });
impl<'pool, const LEN: usize, System, T> const UnzipBind for Bind<'pool, System, [T; LEN]> {
type Target = [Bind<'pool, System, T>; LEN];
fn unzip(self) -> Self::Target {
unsafe {
let divide = DivideBind::new(self);
let hunk = divide.original_hunk();
let mut out =
ComptimeVec::new_in(self.bind_registry.borrow().binds.allocator().clone());
let mut i = 0;
while i < LEN {
out.push(divide.slice(hunk.transmute::<BindData<T>>().wrapping_offset(i as isize)));
i += 1;
}
out.to_array()
}
}
}
pub struct BindTable<System> {
_phantom: PhantomInvariant<System>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum GetBindTableError {
BadContext,
}
impl<System> BindTable<System>
where
System: raw::KernelBase + cfg::KernelStatic,
{
#[inline]
pub fn get() -> Result<&'static Self, GetBindTableError> {
if System::is_boot_complete() {
Ok(&Self {
_phantom: Init::INIT,
})
} else {
Err(GetBindTableError::BadContext)
}
}
#[inline]
pub const unsafe fn get_unchecked() -> &'static Self {
&Self {
_phantom: Init::INIT,
}
}
}
impl<System, T> core::ops::Index<BindRef<System, T>> for BindTable<System>
where
System: raw::KernelBase + cfg::KernelStatic,
T: 'static,
{
type Output = T;
#[inline]
fn index(&self, index: BindRef<System, T>) -> &Self::Output {
unsafe { BindHunk::as_ref(index.0).assume_init_ref() }
}
}
pub(crate) struct CfgBindRegistry {
binds: ComptimeVec<CfgBindInfo>,
}
struct CfgBindInfo {
initializer: Option<Closure>,
users: ComptimeVec<(BindUsage, BindBorrowType)>,
}
impl CfgBindRegistry {
pub const fn new_in(allocator: ConstAllocator) -> Self {
Self {
binds: ComptimeVec::new_in(allocator),
}
}
pub const fn finalize<C>(&mut self, cfg: &mut cfg::Cfg<C>)
where
C: ~const raw_cfg::CfgBase,
{
struct Callback<'a> {
binds: &'a [CfgBindInfo],
bind_init_order: ComptimeVec<usize>,
}
impl const sorter::SorterCallback for Callback<'_> {
fn push_bind_order(&mut self, bind_i: usize) {
self.bind_init_order.push(bind_i);
}
fn report_error(&mut self, e: sorter::SorterError<'_>) {
match e {
sorter::SorterError::BindCycle { bind_is } => {
let _ = bind_is; panic!("the binding initialization order contains a cycle");
}
sorter::SorterError::ConflictingIndefiniteBorrow { bind_i } => {
let _ = bind_i; panic!("conflicting indefinite borrows");
}
}
}
fn num_binds(&self) -> usize {
self.binds.len()
}
fn bind_users(&self, bind_i: usize) -> &[(BindUsage, BindBorrowType)] {
&self.binds[bind_i].users
}
}
let allocator = self.binds.allocator();
let mut callback = Callback {
binds: &self.binds,
bind_init_order: ComptimeVec::with_capacity_in(self.binds.len(), allocator.clone()),
};
sorter::sort_bindings(
&mut callback,
&mut ComptimeVec::repeat_in(allocator.clone(), Init::INIT, self.binds.len()),
&mut ComptimeVec::repeat_in(allocator.clone(), Init::INIT, self.binds.len()),
&mut ComptimeVec::new_in(allocator.clone()),
&mut ComptimeVec::new_in(allocator.clone()),
);
let mut i = 0;
while i < callback.bind_init_order.len() {
let bind_i = callback.bind_init_order[i];
if let Some(initializer) = self.binds[bind_i].initializer {
StartupHook::define()
.start(initializer)
.priority(INIT_HOOK_PRIORITY)
.finish(cfg);
}
i += 1;
}
}
}
impl CfgBindInfo {
const fn new(initializer: Option<Closure>, allocator: ConstAllocator) -> Self {
CfgBindInfo {
initializer,
users: ComptimeVec::new_in(allocator),
}
}
}
#[doc(hidden)]
pub struct CfgBindCtx<'pool> {
_phantom: &'pool (),
usage: BindUsage,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum BindUsage {
Bind(usize),
Executable,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum BindBorrowType {
Borrow,
BorrowMut,
Take,
TakeRef,
TakeMut,
}
#[const_trait]
pub unsafe trait ExecutableDefiner: Sized + private::Sealed {
fn start(self, start: Closure) -> Self;
}
mod private {
use super::*;
#[const_trait]
pub trait Sealed {}
impl<System: raw::KernelBase> const Sealed for kernel::task::TaskDefiner<System> {}
impl<System: raw::KernelInterruptLine> const Sealed
for kernel::interrupt::InterruptHandlerDefiner<System>
{
}
impl<System: raw::KernelTimer> const Sealed for kernel::timer::TimerDefiner<System> {}
}
unsafe impl<System: raw::KernelBase> const ExecutableDefiner for kernel::task::TaskDefiner<System> {
fn start(self, start: Closure) -> Self {
self.start(start)
}
}
unsafe impl<System: raw::KernelInterruptLine> const ExecutableDefiner
for kernel::interrupt::InterruptHandlerDefiner<System>
{
fn start(self, start: Closure) -> Self {
self.start(start)
}
}
unsafe impl<System: raw::KernelTimer> const ExecutableDefiner
for kernel::timer::TimerDefiner<System>
{
fn start(self, start: Closure) -> Self {
self.start(start)
}
}
#[const_trait]
pub trait ExecutableDefinerExt {
fn start_with_bind<Binder, Func: ~const FnBind<Binder, Output = ()>>(
self,
binder: Binder,
func: Func,
) -> Self;
}
impl<T: ~const ExecutableDefiner> const ExecutableDefinerExt for T {
fn start_with_bind<Binder, Func: ~const FnBind<Binder, Output = ()>>(
self,
binder: Binder,
func: Func,
) -> Self {
let mut ctx = CfgBindCtx {
_phantom: &(),
usage: BindUsage::Executable,
};
self.start(Closure::from_fn_const(func.bind(binder, &mut ctx)))
}
}
#[const_trait]
pub trait FnBind<Binder> {
type Output: 'static;
type BoundFn: FnOnce() -> Self::Output + Copy + Send + 'static;
fn bind(self, binder: Binder, ctx: &mut CfgBindCtx<'_>) -> Self::BoundFn;
}
macro_rules! impl_fn_bind {
( @start $($x:tt)* ) => {
impl_fn_bind! { @iter [] [$($x)*] }
};
( @iter
[$(($BinderI:ident, $RuntimeBinderI:ident, $fieldI:ident, $I:tt))*]
[$next_head:tt $($next_tail:tt)*]
) => {
impl_fn_bind! { @iter [$(($BinderI, $RuntimeBinderI, $fieldI, $I))* $next_head] [$($next_tail)*] }
const _: () = {
impl<
T,
Output,
$( $BinderI, $RuntimeBinderI, )*
> const FnBind<( $( $BinderI, )* )> for T
where
$( $BinderI: ~const Binder<Runtime = $RuntimeBinderI>, )*
$( $RuntimeBinderI: RuntimeBinder, )*
T: for<'call> FnOnce($( <$BinderI::Runtime as RuntimeBinder>::Target<'call>, )*)
-> Output + Copy + Send + 'static,
Output: 'static,
{
type Output = Output;
type BoundFn = impl FnOnce() -> Output + Copy + Send + 'static;
fn bind(
self,
binder: ( $( $BinderI, )* ),
ctx: &mut CfgBindCtx<'_>,
) -> Self::BoundFn {
Binder::register_dependency(&binder, ctx);
let intermediate = Binder::into_runtime_binder(binder);
move || {
let ($( $fieldI, )*) = unsafe {
<( $( $RuntimeBinderI, )* ) as RuntimeBinder>::materialize(intermediate)
};
self($( $fieldI, )*)
}
}
} }; };
( @iter [$($_discard:tt)*] [] ) => {}
}
seq_macro::seq!(I in 0..16 { impl_fn_bind! { @start #( (Binder~I, RuntimeBinder~I, field~I, I) )* } });
#[doc(hidden)]
#[derive(Clone, Copy)]
pub struct FnBindNever<T> {
_phantom: PhantomData<T>,
uninhabited: core::convert::Infallible,
}
impl<T: 'static, Binder> const FnBind<Binder> for FnBindNever<T>
where
Binder: ~const self::Binder,
{
type Output = T;
type BoundFn = fn() -> T;
fn bind(self, _: Binder, _: &mut CfgBindCtx<'_>) -> Self::BoundFn {
match self.uninhabited {}
}
}
pub const fn fn_bind_map<FnBind, Mapper>(
inner: FnBind,
mapper: Mapper,
) -> FnBindMap<FnBind, Mapper> {
FnBindMap { inner, mapper }
}
pub struct FnBindMap<Inner, Mapper> {
inner: Inner,
mapper: Mapper,
}
impl<Binder, Inner, InnerBoundFn, Output, Mapper, NewOutput> const FnBind<Binder>
for FnBindMap<Inner, Mapper>
where
Inner: ~const FnBind<Binder, Output = Output, BoundFn = InnerBoundFn>,
InnerBoundFn: FnOnce() -> Output + Copy + Send + 'static,
Output: 'static,
Mapper: FnOnce(Output) -> NewOutput + Copy + Send + 'static,
NewOutput: 'static,
{
type Output = NewOutput;
type BoundFn = impl FnOnce() -> NewOutput + Copy + Send + 'static;
fn bind(self, binder: Binder, ctx: &mut CfgBindCtx<'_>) -> Self::BoundFn {
let inner_bound_fn = self.inner.bind(binder, ctx);
move || (self.mapper)(inner_bound_fn())
}
}
#[const_trait]
pub trait Binder {
type Runtime: RuntimeBinder;
fn register_dependency(&self, ctx: &mut CfgBindCtx<'_>);
fn into_runtime_binder(self) -> Self::Runtime;
}
pub trait RuntimeBinder: Send + Copy + 'static {
type Target<'call>;
unsafe fn materialize<'call>(self) -> Self::Target<'call>;
}
pub struct RuntimeBindTake<System, T>(BindHunk<System, T>);
impl<System, T> Clone for RuntimeBindTake<System, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<System, T> Copy for RuntimeBindTake<System, T> {}
impl<T, System> const Binder for BindTake<'_, System, T>
where
T: 'static,
System: raw::KernelBase + cfg::KernelStatic,
{
type Runtime = RuntimeBindTake<System, T>;
fn register_dependency(&self, ctx: &mut CfgBindCtx<'_>) {
if matches!(ctx.usage, BindUsage::Executable) {
panic!(
"an executable object can not consume `BindTake` because the \
executable object may run for multiple times, but the binding \
value can be moved out only once"
);
}
let Bind {
bind_registry,
bind_i,
..
} = self.0;
bind_registry.borrow_mut().binds[bind_i]
.users
.push((ctx.usage, BindBorrowType::Take));
}
fn into_runtime_binder(self) -> Self::Runtime {
RuntimeBindTake(self.0.hunk)
}
}
impl<T, System> RuntimeBinder for RuntimeBindTake<System, T>
where
T: 'static,
System: raw::KernelBase + cfg::KernelStatic,
{
type Target<'call> = T;
#[inline]
unsafe fn materialize<'call>(self) -> Self::Target<'call> {
unsafe { BindHunk::as_ref(self.0).0.get().read().assume_init() }
}
}
pub struct RuntimeBindTakeMut<System, T>(BindHunk<System, T>);
impl<System, T> Clone for RuntimeBindTakeMut<System, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<System, T> Copy for RuntimeBindTakeMut<System, T> {}
impl<T, System> const Binder for BindTakeMut<'_, System, T>
where
T: 'static,
System: raw::KernelBase + cfg::KernelStatic,
{
type Runtime = RuntimeBindTakeMut<System, T>;
fn register_dependency(&self, ctx: &mut CfgBindCtx<'_>) {
if matches!(ctx.usage, BindUsage::Executable) {
panic!(
"an executable object can not consume `BindTakeMut` because the \
executable object may run for multiple times, but multiple \
mutable borrows of the binding are not allowed to exist \
simultaneously"
);
}
let Bind {
bind_registry,
bind_i,
..
} = self.0;
bind_registry.borrow_mut().binds[bind_i]
.users
.push((ctx.usage, BindBorrowType::TakeMut));
}
fn into_runtime_binder(self) -> Self::Runtime {
RuntimeBindTakeMut(self.0.hunk)
}
}
impl<T, System> RuntimeBinder for RuntimeBindTakeMut<System, T>
where
T: 'static,
System: raw::KernelBase + cfg::KernelStatic,
{
type Target<'call> = &'static mut T;
#[inline]
unsafe fn materialize<'call>(self) -> Self::Target<'call> {
unsafe { BindHunk::as_ref(self.0).assume_init_mut() }
}
}
pub struct RuntimeBindTakeRef<System, T>(BindHunk<System, T>);
impl<System, T> Clone for RuntimeBindTakeRef<System, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<System, T> Copy for RuntimeBindTakeRef<System, T> {}
impl<T, System> const Binder for BindTakeRef<'_, System, T>
where
T: 'static + Sync,
System: raw::KernelBase + cfg::KernelStatic,
{
type Runtime = RuntimeBindTakeRef<System, T>;
fn register_dependency(&self, ctx: &mut CfgBindCtx<'_>) {
let Bind {
bind_registry,
bind_i,
..
} = self.0;
bind_registry.borrow_mut().binds[bind_i]
.users
.push((ctx.usage, BindBorrowType::TakeRef));
}
fn into_runtime_binder(self) -> Self::Runtime {
RuntimeBindTakeRef(self.0.hunk)
}
}
impl<T, System> RuntimeBinder for RuntimeBindTakeRef<System, T>
where
T: 'static,
System: raw::KernelBase + cfg::KernelStatic,
{
type Target<'call> = &'static T;
#[inline]
unsafe fn materialize<'call>(self) -> Self::Target<'call> {
unsafe { BindHunk::as_ref(self.0).assume_init_ref() }
}
}
pub struct RuntimeBindBorrow<System, T>(BindHunk<System, T>);
impl<System, T> Clone for RuntimeBindBorrow<System, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<System, T> Copy for RuntimeBindBorrow<System, T> {}
impl<T, System> const Binder for BindBorrow<'_, System, T>
where
T: 'static,
System: raw::KernelBase + cfg::KernelStatic,
{
type Runtime = RuntimeBindBorrow<System, T>;
fn register_dependency(&self, ctx: &mut CfgBindCtx<'_>) {
let Bind {
bind_registry,
bind_i,
..
} = self.0;
let borrow_type = match ctx.usage {
BindUsage::Bind(_) => BindBorrowType::Borrow,
BindUsage::Executable => BindBorrowType::TakeRef,
};
bind_registry.borrow_mut().binds[bind_i]
.users
.push((ctx.usage, borrow_type));
}
fn into_runtime_binder(self) -> Self::Runtime {
RuntimeBindBorrow(self.0.hunk)
}
}
impl<T, System> RuntimeBinder for RuntimeBindBorrow<System, T>
where
T: 'static,
System: raw::KernelBase + cfg::KernelStatic,
{
type Target<'call> = &'call T;
#[inline]
unsafe fn materialize<'call>(self) -> Self::Target<'call> {
unsafe { BindHunk::as_ref(self.0).assume_init_ref() }
}
}
pub struct RuntimeBindBorrowMut<System, T>(BindHunk<System, T>);
impl<System, T> Clone for RuntimeBindBorrowMut<System, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<System, T> Copy for RuntimeBindBorrowMut<System, T> {}
impl<T, System> const Binder for BindBorrowMut<'_, System, T>
where
T: 'static,
System: raw::KernelBase + cfg::KernelStatic,
{
type Runtime = RuntimeBindBorrowMut<System, T>;
fn register_dependency(&self, ctx: &mut CfgBindCtx<'_>) {
let Bind {
bind_registry,
bind_i,
..
} = self.0;
let borrow_type = match ctx.usage {
BindUsage::Bind(_) => BindBorrowType::BorrowMut,
BindUsage::Executable => BindBorrowType::TakeMut,
};
bind_registry.borrow_mut().binds[bind_i]
.users
.push((ctx.usage, borrow_type));
}
fn into_runtime_binder(self) -> Self::Runtime {
RuntimeBindBorrowMut(self.0.hunk)
}
}
impl<T, System> RuntimeBinder for RuntimeBindBorrowMut<System, T>
where
T: 'static,
System: raw::KernelBase + cfg::KernelStatic,
{
type Target<'call> = &'call mut T;
#[inline]
unsafe fn materialize<'call>(self) -> Self::Target<'call> {
unsafe { BindHunk::as_ref(self.0).assume_init_mut() }
}
}
impl<T, System> const Binder for BindRef<System, T>
where
T: 'static,
System: raw::KernelBase + cfg::KernelStatic,
{
type Runtime = Self;
fn register_dependency(&self, ctx: &mut CfgBindCtx<'_>) {
match ctx.usage {
BindUsage::Executable => {
}
BindUsage::Bind(_) => {
panic!(
"`BindRef` can not be consumed by a binding producer; \
consider using `BindTakeRef` instead"
);
}
}
}
fn into_runtime_binder(self) -> Self::Runtime {
self
}
}
impl<T, System> RuntimeBinder for BindRef<System, T>
where
T: 'static,
System: raw::KernelBase + cfg::KernelStatic,
{
type Target<'call> = &'static T;
#[inline]
unsafe fn materialize<'call>(self) -> Self::Target<'call> {
unsafe { &BindTable::get_unchecked()[self] }
}
}
macro_rules! impl_binder_on_tuples {
( @start $($x:tt)* ) => {
impl_binder_on_tuples! { @iter [] [$($x)*] }
};
( @iter
[$(($BinderI:ident, $RuntimeBinderI:ident, $I:tt))*]
[$next_head:tt $($next_tail:tt)*]
) => {
impl_binder_on_tuples! { @iter [$(($BinderI, $RuntimeBinderI, $I))* $next_head] [$($next_tail)*] }
impl<$( $BinderI, )*> const Binder for ($( $BinderI, )*)
where
$( $BinderI: ~const Binder, )*
{
type Runtime = ( $( $BinderI::Runtime, )* );
fn register_dependency(&self, ctx: &mut CfgBindCtx<'_>) {
$( self.$I.register_dependency(ctx); )*
let _ = ctx;
}
#[allow(clippy::unused_unit)]
fn into_runtime_binder(self) -> Self::Runtime {
( $( self.$I.into_runtime_binder(), )* )
}
}
impl<$( $RuntimeBinderI, )*> RuntimeBinder for ($( $RuntimeBinderI, )*)
where
$( $RuntimeBinderI: RuntimeBinder, )*
{
type Target<'call> = ( $( $RuntimeBinderI::Target<'call>, )* );
#[allow(unused_unsafe)]
#[allow(unused_variables)]
#[allow(clippy::unused_unit)]
#[inline]
unsafe fn materialize<'call>(self) -> Self::Target<'call> {
unsafe {
( $( $RuntimeBinderI::materialize(self.$I), )* )
}
}
}
};
( @iter [$($_discard:tt)*] [] ) => {}
}
seq_macro::seq!(I in 0..16 {
impl_binder_on_tuples! { @start #( (Binder~I, RuntimeBinder~I, I) )* }
});
impl<const LEN: usize, Binder> const self::Binder for [Binder; LEN]
where
Binder: ~const self::Binder,
{
type Runtime = [Binder::Runtime; LEN];
fn register_dependency(&self, ctx: &mut CfgBindCtx<'_>) {
let mut i = 0;
while i < LEN {
self[i].register_dependency(ctx);
i += 1;
}
}
fn into_runtime_binder(self) -> Self::Runtime {
unsafe {
let mut out = MaybeUninit::uninit_array();
let this = MaybeUninit::new(self);
let mut i = 0;
while i < LEN {
out[i] = MaybeUninit::new(
this.as_ptr()
.cast::<Binder>()
.wrapping_add(i)
.read()
.into_runtime_binder(),
);
i += 1;
}
MaybeUninit::array_assume_init(out)
}
}
}
impl<const LEN: usize, RuntimeBinder> self::RuntimeBinder for [RuntimeBinder; LEN]
where
RuntimeBinder: self::RuntimeBinder,
{
type Target<'call> = [RuntimeBinder::Target<'call>; LEN];
#[inline]
unsafe fn materialize<'call>(self) -> Self::Target<'call> {
self.map(|x| unsafe { RuntimeBinder::materialize(x) })
}
}