serenity icon indicating copy to clipboard operation
serenity copied to clipboard

Sharing runtime with a dynamic lib loaded at runtime.

Open Kirottu opened this issue 3 years ago • 3 comments

Hello,

I would like to build a modular bot system with this library in rust, using libloading to load dynamic libraries with command groups. So far I have gotten it to the point that everything works except for the actual running of the commands. Whenever I try to run the commands from a group within a shared library it gives an error about there being no tokio runtime present.

thread '<unnamed>' panicked at 'there is no reactor running, must be called from the context of a Tokio 1.x runtime', /home/kirottu/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.15.0/src/runtime/context.rs:54:26
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

The problem is that there seems to be no obvious way to create a separate runtime for the library only or for sharing the runtime from the main bot with the library. Any help would be appreciated!

Kirottu avatar Jan 05 '22 19:01 Kirottu

I don't know this library specifically, or how it works, but it looks like it's running the command functions in a different system thread than the one tokio is running on.

I tried looking into the issues of the library, and found these two: https://github.com/nagisa/rust_libloading/issues/81 https://github.com/nagisa/rust_libloading/issues/97 that may be useful to figure how to run async code via that library.

vicky5124 avatar Jan 05 '22 20:01 vicky5124

Well running async code would be pretty trivial, create a new runtime, make your async calls and call it a day. But the problem here is how the functions work with serenity. There is no clear way that I can see to make the function defined with serenity #[command] macro use a specified tokio runtime. I don't even know what actually calls the function and from where when the command is sent to a discord channel.

Here is the sample code for the library if it helps:

use serenity::{
    client::Context,
    framework::standard::{
        macros::{command, group},
        CommandGroup, CommandResult,
    },
    model::channel::Message,
};

#[no_mangle]
pub fn setup() -> &'static CommandGroup {
    return &GENERAL_GROUP;
}

#[group]
#[commands(ping)]
struct General;

// ----------------------
// General group commands
// ----------------------

#[command]
#[description("Ping!")]
async fn ping(ctx: &Context, msg: &Message) -> CommandResult {
    msg.reply_ping(ctx, "pong").await?;

    Ok(())
}

Kirottu avatar Jan 06 '22 07:01 Kirottu

Sadly, you can't use the standard framework for this, as all the commands are made at compile time and stored in the framework as 'static, you will have to either use a custom framework of yours that allows this, or modify an existing framework to allow for this (poise seems like the best option for this right now)

vicky5124 avatar Jan 06 '22 19:01 vicky5124

This is not really a serenity issue, rather a tokio(+libloading?) issue. The dynamically loaded library needs to have access to the spawned runtime from the parent process. Here are some pointers https://github.com/tokio-rs/tokio/issues/1964

kangalio avatar Apr 07 '23 17:04 kangalio