Skip to content

Commit

Permalink
unfinished
Browse files Browse the repository at this point in the history
  • Loading branch information
milyin committed Oct 12, 2023
1 parent def94fb commit 27ebcd5
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 152 deletions.
49 changes: 49 additions & 0 deletions plugins/zenoh-plugin-trait/src/compatibility.rs
Original file line number Diff line number Diff line change
@@ -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, <[email protected]>
//

#[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
}
}
}
45 changes: 2 additions & 43 deletions plugins/zenoh-plugin-trait/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,62 +18,21 @@
//!
//! 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<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());
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 {
type StartArgs;
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> {
Compatibility::new()
}
/// Starts your plugin. Use `Ok` to return your plugin's control structure
fn start(name: &str, args: &Self::StartArgs) -> ZResult<Self::RunningPlugin>;
}
64 changes: 18 additions & 46 deletions plugins/zenoh-plugin-trait/src/loading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
// Contributors:
// ZettaScale Zenoh Team, <[email protected]>
//
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;

Expand Down Expand Up @@ -96,7 +96,6 @@ impl<StartArgs: 'static, RunningPlugin: 'static> PluginsManager<StartArgs, Runni
running_plugins,
..
} = self;
let compat = crate::Compatibility::new().unwrap();
plugin_starters.iter().map(move |p| {
let name = p.name();
let path = p.path();
Expand All @@ -105,29 +104,12 @@ impl<StartArgs: 'static, RunningPlugin: 'static> PluginsManager<StartArgs, Runni
path,
match running_plugins.entry(name.into()) {
std::collections::hash_map::Entry::Occupied(_) => 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),
},
},
)
})
Expand Down Expand Up @@ -218,7 +200,6 @@ trait PluginStarter<StartArgs, RunningPlugin> {
fn name(&self) -> &str;
fn path(&self) -> &str;
fn start(&self, args: &StartArgs) -> ZResult<RunningPlugin>;
fn compatibility(&self) -> Option<ZResult<Compatibility>>;
fn deletable(&self) -> bool;
}

Expand All @@ -244,9 +225,6 @@ where
fn path(&self) -> &str {
"<statically_linked>"
}
fn compatibility(&self) -> Option<ZResult<Compatibility>> {
None
}
fn start(&self, args: &StartArgs) -> ZResult<RunningPlugin> {
P::start(P::STATIC_NAME, args)
}
Expand All @@ -265,10 +243,7 @@ impl<StartArgs, RunningPlugin> PluginStarter<StartArgs, RunningPlugin>
self.path.to_str().unwrap()
}
fn start(&self, args: &StartArgs) -> ZResult<RunningPlugin> {
self.vtable.start(self.name(), args)
}
fn compatibility(&self) -> Option<ZResult<Compatibility>> {
Some(self.vtable.compatibility())
(self.vtable.start)(self.name(), args)
}
fn deletable(&self) -> bool {
true
Expand All @@ -283,21 +258,18 @@ pub struct DynamicPlugin<StartArgs, RunningPlugin> {
}

impl<StartArgs, RunningPlugin> DynamicPlugin<StartArgs, RunningPlugin> {
fn new(name: String, lib: Library, path: PathBuf) -> Result<Self, Option<PluginVTableVersion>> {
fn new(name: String, lib: Library, path: PathBuf) -> Self {
// TODO: check loader version and compatibility
let load_plugin = unsafe {
lib.get::<fn(PluginVTableVersion) -> LoadPluginResult<StartArgs, RunningPlugin>>(
b"load_plugin",
)
.map_err(|_| None)?
lib.get::<fn() -> PluginVTable<StartArgs, RunningPlugin>>(b"load_plugin")
.map_err(|_| None)?

Check failure on line 265 in plugins/zenoh-plugin-trait/src/loading.rs

View workflow job for this annotation

GitHub Actions / Run checks on ubuntu-latest

the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `std::ops::FromResidual`)

Check failure on line 265 in plugins/zenoh-plugin-trait/src/loading.rs

View workflow job for this annotation

GitHub Actions / Run tests on ubuntu-latest

the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `FromResidual`)

Check failure on line 265 in plugins/zenoh-plugin-trait/src/loading.rs

View workflow job for this annotation

GitHub Actions / Run checks on macOS-latest

the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `std::ops::FromResidual`)

Check failure on line 265 in plugins/zenoh-plugin-trait/src/loading.rs

View workflow job for this annotation

GitHub Actions / Run tests on macOS-latest

the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `FromResidual`)

Check failure on line 265 in plugins/zenoh-plugin-trait/src/loading.rs

View workflow job for this annotation

GitHub Actions / Run checks on windows-latest

the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `std::ops::FromResidual`)

Check failure on line 265 in plugins/zenoh-plugin-trait/src/loading.rs

View workflow job for this annotation

GitHub Actions / Run tests on windows-latest

the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `FromResidual`)
};
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,
}
}
}
81 changes: 18 additions & 63 deletions plugins/zenoh-plugin-trait/src/vtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,73 +15,23 @@ use crate::*;
pub use no_mangle::*;
use zenoh_result::ZResult;

pub type PluginVTableVersion = u16;
type LoadPluginResultInner = Result<PluginVTableInner<(), ()>, PluginVTableVersion>;
pub type LoadPluginResult<A, B> = Result<PluginVTable<A, B>, 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<StartArgs, RunningPlugin> = fn(&str, &StartArgs) -> ZResult<RunningPlugin>;

#[repr(C)]
struct PluginVTableInner<StartArgs, RunningPlugin> {
start: StartFn<StartArgs, RunningPlugin>,
compatibility: fn() -> ZResult<crate::Compatibility>,
}

/// 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::<LoadPluginResultInner>()
}
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::<Result<PluginVTable, PluginVTableVersion>>() == 64` (one cache line).
#[repr(C)]
pub struct PluginVTable<StartArgs, RunningPlugin> {
inner: PluginVTableInner<StartArgs, RunningPlugin>,
padding: PluginVTablePadding,
pub start: StartFn<StartArgs, RunningPlugin>,
}

impl<StartArgs, RunningPlugin> PluginVTable<StartArgs, RunningPlugin> {
pub fn new<ConcretePlugin: Plugin<StartArgs = StartArgs, RunningPlugin = RunningPlugin>>(
) -> 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<Self, PluginVTableVersion>,
>::uninit())
};
}

pub fn start(&self, name: &str, start_args: &StartArgs) -> ZResult<RunningPlugin> {
(self.inner.start)(name, start_args)
}
pub fn compatibility(&self) -> ZResult<Compatibility> {
(self.inner.compatibility)()
}
}

pub use no_mangle::*;
Expand All @@ -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>()
}
};
}
Expand Down

0 comments on commit 27ebcd5

Please sign in to comment.