Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7594f8c7 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes Iac8f6a8e,I85f0dac1

* changes:
  rusty-gd: GDDI now caches instances & allows injecting config
  rusty-gd: initial commit for GDDI
parents 62867200 d2c93e60
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ rust_library {
      "libgrpcio",
      "libprotobuf",
      "libtokio",
      "libgddi",
    ],
    host_supported: true,
}
+36 −19
Original line number Diff line number Diff line
@@ -14,20 +14,31 @@ use rootservice::*;
use rootservice_grpc::{create_root_facade, RootFacade};

use bt_hal::facade::HciHalFacadeService;
use bt_hal::rootcanal_hal::{RootcanalConfig, RootcanalHal};
use bt_hal::hal_facade_module;
use bt_hal::rootcanal_hal::RootcanalConfig;
use bt_hci::facade::HciLayerFacadeService;
use bt_hci::Hci;
use bt_hci::hci_module;

use tokio::runtime::Runtime;
use tokio::sync::mpsc::{channel, Sender};
use tokio::sync::oneshot;

use gddi::{module, Registry, RegistryBuilder};

use grpcio::*;

use std::sync::Arc;

use futures::executor::block_on;

module! {
    stack_module,
    submodules {
        hal_facade_module,
        hci_module,
    }
}

/// Bluetooth testing root facade service
#[derive(Clone)]
pub struct RootFacadeService {
@@ -94,13 +105,29 @@ impl FacadeServiceManager {
    fn create(rt: Arc<Runtime>, grpc_port: u16, rootcanal_port: Option<u16>) -> Self {
        let (tx, mut rx) = channel::<LifecycleCommand>(1);
        let local_rt = rt.clone();
        local_rt.spawn(async move {
        rt.spawn(async move {
            let mut server: Option<Server> = None;
            while let Some(cmd) = rx.recv().await {
                match cmd {
                    LifecycleCommand::Start { req, done } => {
                        server =
                            Some(Self::start_internal(&rt, req, grpc_port, rootcanal_port).await);
                        let registry = {
                            let mut builder = RegistryBuilder::new();
                            builder.register_module(stack_module);
                            if rootcanal_port.is_some() {
                                builder.register_module(bt_hal::rootcanal_hal_module);
                            }

                            Arc::new(builder.build())
                        };

                        registry.inject(local_rt.clone()).await;
                        if let Some(rc_port) = rootcanal_port {
                            registry
                                .inject(RootcanalConfig::new(rc_port, "127.0.0.1"))
                                .await;
                        }

                        server = Some(Self::start_internal(&registry, req, grpc_port).await);
                        done.send(()).unwrap();
                    }
                    LifecycleCommand::Stop { done } => {
@@ -135,30 +162,20 @@ impl FacadeServiceManager {
        Ok(())
    }

    // TODO this is messy and needs to be overhauled to support bringing up the stack to partial
    // layers. Will be cleaned up soon.
    async fn start_internal(
        rt: &Arc<Runtime>,
        registry: &Arc<Registry>,
        req: StartStackRequest,
        grpc_port: u16,
        rootcanal_port: Option<u16>,
    ) -> Server {
        let hal_exports = RootcanalHal::start(
            RootcanalConfig::new(rootcanal_port.unwrap(), "127.0.0.1"),
            Arc::clone(&rt),
        )
        .await
        .unwrap();
        let mut services = Vec::new();
        match req.get_module_under_test() {
            BluetoothModule::HAL => {
                services.push(HciHalFacadeService::create(hal_exports, Arc::clone(&rt)));
                services.push(registry.get::<HciHalFacadeService>().await.create_grpc());
            }
            BluetoothModule::HCI => {
                let hci_exports = Hci::start(hal_exports, Arc::clone(&rt));
                services.push(HciLayerFacadeService::create(hci_exports, Arc::clone(&rt)));
                services.push(registry.get::<HciLayerFacadeService>().await.create_grpc());
            }
            _ => unimplemented!()
            _ => unimplemented!(),
        }

        FacadeServiceManager::start_server(services, grpc_port)
+11 −0
Original line number Diff line number Diff line
rust_proc_macro {
    name: "libgddi_macros",
    crate_name: "gddi_macros",
    srcs: ["src/lib.rs"],
    edition: "2018",
    rustlibs: [
        "libproc_macro2",
        "libquote",
        "libsyn",
    ],
}
+150 −0
Original line number Diff line number Diff line
//! Core dependency injection macros

extern crate proc_macro;
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::parse::{Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
use syn::{braced, parse, parse_macro_input, FnArg, Ident, ItemFn, Token, Type};

/// Defines a provider function, with generated helper that implicitly fetches argument instances from the registry
#[proc_macro_attribute]
pub fn provides(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let function: ItemFn = parse(item).expect("can only be applied to functions");

    // Create the info needed to refer to the function & the injected version we generate
    let ident = function.sig.ident.clone();
    let injected_ident = format_ident!("__gddi_{}_injected", ident);

    // Create the info needed to generate the call to the original function
    let inputs = function.sig.inputs.iter().map(|arg| {
        if let FnArg::Typed(t) = arg {
            return t.ty.clone();
        }
        panic!("can't be applied to struct methods");
    });
    let local_var_idents = (0..inputs.len()).map(|i| format_ident!("__input{}", i));
    let local_var_idents_for_call = local_var_idents.clone();

    let emitted_code = quote! {
        // Injecting wrapper
        fn #injected_ident(registry: std::sync::Arc<gddi::Registry>) -> std::pin::Pin<gddi::ProviderFutureBox> {
            Box::pin(async move {
                // Create a local variable for each argument, to ensure they get generated in a
                // deterministic order (compiler complains otherwise)
                #(let #local_var_idents = registry.get::<#inputs>().await;)*

                // Actually call the original function
                Box::new(#ident(#(#local_var_idents_for_call),*).await) as Box<dyn std::any::Any>
            })
        }
        #function
    };
    emitted_code.into()
}

struct ModuleDef {
    name: Ident,
    providers: Punctuated<ProviderDef, Token![,]>,
    submodules: Punctuated<Ident, Token![,]>,
}

enum ModuleEntry {
    Providers(Punctuated<ProviderDef, Token![,]>),
    Submodules(Punctuated<Ident, Token![,]>),
}

struct ProviderDef {
    ty: Type,
    ident: Ident,
}

impl Parse for ModuleDef {
    fn parse(input: ParseStream) -> Result<Self> {
        // first thing is the module name followed by a comma
        let name = input.parse()?;
        input.parse::<Token![,]>()?;
        // Then comes submodules or provider sections, in any order
        let entries: Punctuated<ModuleEntry, Token![,]> = Punctuated::parse_terminated(input)?;
        let mut providers = Punctuated::new();
        let mut submodules = Punctuated::new();
        for entry in entries.into_iter() {
            match entry {
                ModuleEntry::Providers(value) => {
                    if !providers.is_empty() {
                        panic!("providers specified more than once");
                    }
                    providers = value;
                },
                ModuleEntry::Submodules(value) => {
                    if !submodules.is_empty() {
                        panic!("submodules specified more than once");
                    }
                    submodules = value;
                },
            }
        }
        Ok(ModuleDef {
            name,
            providers,
            submodules,
        })
    }
}

impl Parse for ProviderDef {
    fn parse(input: ParseStream) -> Result<Self> {
        // A provider definition follows this format: <Type> -> <function name>
        let ty = input.parse()?;
        input.parse::<Token![=>]>()?;
        let ident = input.parse()?;
        Ok(ProviderDef { ty, ident })
    }
}

impl Parse for ModuleEntry {
    fn parse(input: ParseStream) -> Result<Self> {
        match input.parse::<Ident>()?.to_string().as_str() {
            "providers" => {
                let entries;
                braced!(entries in input);
                Ok(ModuleEntry::Providers(
                    entries.parse_terminated(ProviderDef::parse)?,
                ))
            }
            "submodules" => {
                let entries;
                braced!(entries in input);
                Ok(ModuleEntry::Submodules(
                    entries.parse_terminated(Ident::parse)?,
                ))
            }
            keyword => {
                panic!("unexpected keyword: {}", keyword);
            }
        }
    }
}

/// Emits a module function that registers submodules & providers with the registry
#[proc_macro]
pub fn module(item: TokenStream) -> TokenStream {
    let module = parse_macro_input!(item as ModuleDef);
    let init_ident = module.name.clone();
    let types = module.providers.iter().map(|p| p.ty.clone());
    let provider_idents = module
        .providers
        .iter()
        .map(|p| format_ident!("__gddi_{}_injected", p.ident.clone()));
    let submodule_idents = module.submodules.iter();
    let emitted_code = quote! {
        #[doc(hidden)]
        pub fn #init_ident(builder: &mut gddi::RegistryBuilder) {
            // Register all providers on this module
            #(builder.register_provider::<#types>(Box::new(#provider_idents));)*
            // Register all submodules on this module
            #(builder.register_module(#submodule_idents);)*
        }
    };
    emitted_code.into()
}
+9 −0
Original line number Diff line number Diff line
rust_library {
    name: "libgddi",
    crate_name: "gddi",
    srcs: ["src/lib.rs"],
    host_supported: true,
    edition: "2018",
    proc_macros: ["libgddi_macros"],
    rustlibs: ["libtokio"],
}
Loading