最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Sharing same instance of the shared library betwen two crates in Rust - Stack Overflow

programmeradmin2浏览0评论

I am trying to create a plugin system, and my project is anized as follows:-

├── Cargo.lock
├── Cargo.toml
├── op_api
│   ├── Cargo.toml
│   └── src
├── op_dispatch
│   ├── Cargo.toml
│   └── src
├── op_user
│   ├── Cargo.toml
│   └── src
└── target
    ├── CACHEDIR.TAG
    └── debug

Here op_api and op_user are libraries and op_dispatch is a binary.

op_api/lib.rs contains the trait that the plugin needs to implement.

use std::collections::HashMap;
use std::sync::Mutex;

pub trait Operator {
    fn run(&self);
}

type OperatorConstructor = fn() -> Box<dyn Operator>;

// Static registry for all available operators in a library
lazy_static::lazy_static! {
    static ref REGISTRY: Mutex<HashMap<&'static str, OperatorConstructor>> = Mutex::new(HashMap::new());
}

// Function to register an operator (used inside the plugin)
pub fn register_operator(name: &'static str, constructor: OperatorConstructor) {
    let mut registry = REGISTRY.lock().unwrap();
    println!("Registry address: {:p}", &*registry);
    registry.insert(name, constructor);
    println!("{:?}", registry.keys());
}

// Function to get the registry (used in main app)
pub fn get_registered_operators() -> Vec<&'static str> {
    let registry = REGISTRY.lock().unwrap();
    println!("Registry address: {:p}", &*registry);
    println!("{:?}", registry.keys());
    registry.keys().cloned().collect()
}

// Function to create an operator instance by name
pub fn create_operator(name: &str) -> Option<Box<dyn Operator>> {
    let registry = REGISTRY.lock().unwrap();
    registry.get(name).map(|constructor| constructor())
}

op_user/lib.rs defines two Operators Op1 and Op2. The goal is to be able to load these operators at runtime.

use op_api::{Operator, register_operator};

struct Op1;
impl Operator for Op1 {
    fn run(&self) {
        println!("Running Op1");
    }
}

struct Op2;
impl Operator for Op2 {
    fn run(&self) {
        println!("Running Op2");
    }
}

// Register multiple operators when the library is loaded
#[unsafe(no_mangle)]
pub extern "C" fn register_operators() {
    register_operator("Op1", || Box::new(Op1));
    register_operator("Op2", || Box::new(Op2));
}

Loading and running logic is in op_dispatch/main.rs

fn main() {
    unsafe {
        let lib = Library::new("target/debug/libop_user.so").expect("Failed to load library");

        // Call the register_operators function to populate the registry
        let register: Symbol<fn()> = lib
            .get(b"register_operators")
            .expect("Failed to load register_operators");
        register();

        // Get all registered operators
        let ops = get_registered_operators();
        println!("Available operators: {:?}", ops);
    }
}

Since I want the op_user library to be shared I have added crate-type = ["cdylib"] in its Cargo.toml. I have also added op_api as a dependency in both op_user and op_dispatch using op_api = {path = "../op_api"}.

The issue is that when I print available operators after registering the operators beforehand, I get empty vector. This is caused by the fact that there are two instances of the REGISTRY hashmap (one in op_user and other in op_dispatch).

How to do this correctly? If you think the approach itself is wrong, kindly suggest some other approach to build such plugin system.

发布评论

评论列表(0)

  1. 暂无评论