diff --git a/plugins/zenoh-plugin-trait/src/compatibility.rs b/plugins/zenoh-plugin-trait/src/compatibility.rs new file mode 100644 index 0000000000..3360baa05b --- /dev/null +++ b/plugins/zenoh-plugin-trait/src/compatibility.rs @@ -0,0 +1,49 @@ +// +// Copyright (c) 2023 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// + +#[repr(C)] +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Compatibility { + major: u64, + minor: u64, + patch: u64, + stable: bool, + commit: &'static str, +} +const RELEASE_AND_COMMIT: (&str, &str) = zenoh_macros::rustc_version_release!(); +impl Compatibility { + pub fn new() -> Self { + let (release, commit) = RELEASE_AND_COMMIT; + let (release, stable) = if let Some(p) = release.chars().position(|c| c == '-') { + (&release[..p], false) + } else { + (release, true) + }; + let mut split = release.split('.').map(|s| s.trim()); + Compatibility { + major: split.next().unwrap().parse().unwrap(), + minor: split.next().unwrap().parse().unwrap(), + patch: split.next().unwrap().parse().unwrap(), + stable, + commit, + } + } + pub fn are_compatible(a: &Self, b: &Self) -> bool { + if a.stable && b.stable { + a.major == b.major && a.minor == b.minor && a.patch == b.patch + } else { + a == b + } + } +} diff --git a/plugins/zenoh-plugin-trait/src/lib.rs b/plugins/zenoh-plugin-trait/src/lib.rs index 486aa8fbe7..7605cb2cab 100644 --- a/plugins/zenoh-plugin-trait/src/lib.rs +++ b/plugins/zenoh-plugin-trait/src/lib.rs @@ -18,49 +18,14 @@ //! //! If building a plugin for [`zenohd`](https://crates.io/crates/zenoh), you should use the types exported in [`zenoh::plugins`](https://docs.rs/zenoh/latest/zenoh/plugins) to fill [`Plugin`]'s associated types. //! To check your plugin typing for `zenohd`, have your plugin implement [`zenoh::plugins::ZenohPlugin`](https://docs.rs/zenoh/latest/zenoh/plugins/struct.ZenohPlugin) +pub mod compatibility; pub mod loading; pub mod vtable; use zenoh_result::ZResult; -#[repr(C)] -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct Compatibility { - major: u64, - minor: u64, - patch: u64, - stable: bool, - commit: &'static str, -} -const RELEASE_AND_COMMIT: (&str, &str) = zenoh_macros::rustc_version_release!(); -impl Compatibility { - pub fn new() -> ZResult { - let (release, commit) = RELEASE_AND_COMMIT; - let (release, stable) = if let Some(p) = release.chars().position(|c| c == '-') { - (&release[..p], false) - } else { - (release, true) - }; - let mut split = release.split('.').map(|s| s.trim()); - Ok(Compatibility { - major: split.next().unwrap().parse().unwrap(), - minor: split.next().unwrap().parse().unwrap(), - patch: split.next().unwrap().parse().unwrap(), - stable, - commit, - }) - } - pub fn are_compatible(a: &Self, b: &Self) -> bool { - if a.stable && b.stable { - a.major == b.major && a.minor == b.minor && a.patch == b.patch - } else { - a == b - } - } -} - pub mod prelude { - pub use crate::{loading::*, vtable::*, Plugin}; + pub use crate::{compatibility::*, loading::*, vtable::*, Plugin}; } pub trait Plugin: Sized + 'static { @@ -68,12 +33,6 @@ pub trait Plugin: Sized + 'static { type RunningPlugin; /// Your plugins' default name when statically linked. const STATIC_NAME: &'static str; - /// You probabky don't need to override this function. - /// - /// Returns some build information on your plugin, allowing the host to detect potential ABI changes that would break it. - fn compatibility() -> ZResult { - Compatibility::new() - } /// Starts your plugin. Use `Ok` to return your plugin's control structure fn start(name: &str, args: &Self::StartArgs) -> ZResult; } diff --git a/plugins/zenoh-plugin-trait/src/loading.rs b/plugins/zenoh-plugin-trait/src/loading.rs index 0f7475a651..d97ef01561 100644 --- a/plugins/zenoh-plugin-trait/src/loading.rs +++ b/plugins/zenoh-plugin-trait/src/loading.rs @@ -11,12 +11,12 @@ // Contributors: // ZettaScale Zenoh Team, // -use crate::vtable::*; use crate::*; use libloading::Library; use std::collections::hash_map::Entry; use std::collections::HashMap; use std::path::PathBuf; +use vtable::PluginVTable; use zenoh_result::{bail, zerror, ZResult}; use zenoh_util::LibLoader; @@ -96,7 +96,6 @@ impl PluginsManager PluginsManager Ok(None), - std::collections::hash_map::Entry::Vacant(e) => { - let compatible = match p.compatibility() { - Some(Ok(c)) => { - if Compatibility::are_compatible(&compat, &c) { - Ok(()) - } else { - Err(zerror!("Plugin compatibility mismatch: host: {:?} - plugin: {:?}. This could lead to segfaults, so wer'e not starting it.", &compat, &c)) - } - } - Some(Err(e)) => Err(zerror!(e => "Plugin {} (from {}) compatibility couldn't be recovered. This likely means it's very broken.", name, path)), - None => Ok(()), - }; - if let Err(e) = compatible { - Err(e.into()) - } else { - match p.start(args) { - Ok(p) => Ok(Some(unsafe { - std::mem::transmute(&e.insert((path.into(), p)).1) - })), - Err(e) => Err(e), - } - } - } + std::collections::hash_map::Entry::Vacant(e) => match p.start(args) { + Ok(p) => Ok(Some(unsafe { + std::mem::transmute(&e.insert((path.into(), p)).1) + })), + Err(e) => Err(e), + }, }, ) }) @@ -218,7 +200,6 @@ trait PluginStarter { fn name(&self) -> &str; fn path(&self) -> &str; fn start(&self, args: &StartArgs) -> ZResult; - fn compatibility(&self) -> Option>; fn deletable(&self) -> bool; } @@ -244,9 +225,6 @@ where fn path(&self) -> &str { "" } - fn compatibility(&self) -> Option> { - None - } fn start(&self, args: &StartArgs) -> ZResult { P::start(P::STATIC_NAME, args) } @@ -265,10 +243,7 @@ impl PluginStarter self.path.to_str().unwrap() } fn start(&self, args: &StartArgs) -> ZResult { - self.vtable.start(self.name(), args) - } - fn compatibility(&self) -> Option> { - Some(self.vtable.compatibility()) + (self.vtable.start)(self.name(), args) } fn deletable(&self) -> bool { true @@ -283,21 +258,18 @@ pub struct DynamicPlugin { } impl DynamicPlugin { - fn new(name: String, lib: Library, path: PathBuf) -> Result> { + fn new(name: String, lib: Library, path: PathBuf) -> Self { + // TODO: check loader version and compatibility let load_plugin = unsafe { - lib.get:: LoadPluginResult>( - b"load_plugin", - ) - .map_err(|_| None)? + lib.get:: PluginVTable>(b"load_plugin") + .map_err(|_| None)? }; - match load_plugin(PLUGIN_VTABLE_VERSION) { - Ok(vtable) => Ok(DynamicPlugin { - _lib: lib, - vtable, - name, - path, - }), - Err(plugin_version) => Err(Some(plugin_version)), + let vtable = load_plugin(); + Self { + _lib: lib, + vtable, + name, + path, } } } diff --git a/plugins/zenoh-plugin-trait/src/vtable.rs b/plugins/zenoh-plugin-trait/src/vtable.rs index 6bb3eb7dd5..51193450ed 100644 --- a/plugins/zenoh-plugin-trait/src/vtable.rs +++ b/plugins/zenoh-plugin-trait/src/vtable.rs @@ -15,73 +15,23 @@ use crate::*; pub use no_mangle::*; use zenoh_result::ZResult; -pub type PluginVTableVersion = u16; -type LoadPluginResultInner = Result, PluginVTableVersion>; -pub type LoadPluginResult = Result, PluginVTableVersion>; - -/// This number should change any time the internal structure of [`PluginVTable`] changes -pub const PLUGIN_VTABLE_VERSION: PluginVTableVersion = 1; +pub type PluginLoaderVersion = u64; +pub const PLUGIN_LOADER_VERSION: PluginLoaderVersion = 1; type StartFn = fn(&str, &StartArgs) -> ZResult; -#[repr(C)] -struct PluginVTableInner { - start: StartFn, - compatibility: fn() -> ZResult, -} - -/// Automagical padding such that [PluginVTable::init]'s result is the size of a cache line -#[repr(C)] -struct PluginVTablePadding { - __padding: [u8; PluginVTablePadding::padding_length()], -} -impl PluginVTablePadding { - const fn padding_length() -> usize { - 64 - std::mem::size_of::() - } - fn new() -> Self { - PluginVTablePadding { - __padding: [0; Self::padding_length()], - } - } -} - -/// For use with dynamically loaded plugins. Its size will not change accross versions, but its internal structure might. -/// -/// To ensure compatibility, its size and alignment must allow `size_of::>() == 64` (one cache line). #[repr(C)] pub struct PluginVTable { - inner: PluginVTableInner, - padding: PluginVTablePadding, + pub start: StartFn, } impl PluginVTable { pub fn new>( ) -> Self { - PluginVTable { - inner: PluginVTableInner { - start: ConcretePlugin::start, - compatibility: ConcretePlugin::compatibility, - }, - padding: PluginVTablePadding::new(), + Self { + start: ConcretePlugin::start, } } - - /// Ensures [PluginVTable]'s size stays the same between versions - fn __size_check() { - unsafe { - std::mem::transmute::<_, [u8; 64]>(std::mem::MaybeUninit::< - Result, - >::uninit()) - }; - } - - pub fn start(&self, name: &str, start_args: &StartArgs) -> ZResult { - (self.inner.start)(name, start_args) - } - pub fn compatibility(&self) -> ZResult { - (self.inner.compatibility)() - } } pub use no_mangle::*; @@ -92,17 +42,22 @@ pub mod no_mangle { macro_rules! declare_plugin { ($ty: path) => { #[no_mangle] - fn load_plugin( - version: $crate::prelude::PluginVTableVersion, - ) -> $crate::prelude::LoadPluginResult< + fn get_plugin_loader_version() -> $crate::prelude::PluginLoaderVersion { + $crate::prelude::PLUGIN_LOADER_VERSION + } + + #[no_mangle] + fn get_plugin_compatibility() -> $crate::Compatibility { + // TODO: add vtable version (including type parameters) to the compatibility information + Compatibility::new() + } + + #[no_mangle] + fn load_plugin() -> $crate::prelude::PluginVTable< <$ty as $crate::prelude::Plugin>::StartArgs, <$ty as $crate::prelude::Plugin>::RunningPlugin, > { - if version == $crate::prelude::PLUGIN_VTABLE_VERSION { - Ok($crate::prelude::PluginVTable::new::<$ty>()) - } else { - Err($crate::prelude::PLUGIN_VTABLE_VERSION) - } + $crate::prelude::PluginVTable::new::<$ty>() } }; }