3

Since this is most likely an XY problem, I will provide you with more background.

I am developing a library that starts a tokio::net::TcpListener, waits for incoming connections and processes them.

I have implemented a signal handler in a separate thread that catches SIGTERM/SIGINT and initiates a shutdown procedure:

tokio::select! {
    _ = sigterm.recv() => {
        println!("Received SIGTERM.");
    }
    _ = sigint.recv() => {
        println!("Received SIGINT.");
    }
};
// shutdown_server_tx.send(()) ....

When I build a binary instead of a library, run the application, and press CTRL+C, everything works as expected (signal catch and shutdown procedure are executed).

However, instead of building a standalone binary, I want to integrate my Rust code (as a lib) into existing C++ code. I have already created the pub extern "C" fn function and checked that I can correctly call it in C++. Obviously, when building the C++ code and starting the application, the signal is not passed onto my Rust code. This is a problem, because the shutdown routine implemented in Rust will not be executed.

I thought that I can address this issue by creating a tokio::sync::oneshot::channel() in my Rust application and add the receiver to the tokio::select! part. This definitely works, but the problem is that I do not know how to call the sender from C++.

This is what I tried:

lazy_static! {
    pub static ref SH_TX_RX: (Sender<()>, Receiver<()>) = oneshot::channel();
}

#[no_mangle]
pub extern "C" fn run() {
    
    println!("Starting server...");

    match srv::MyServer::new(SH_TX_RX.1) {
        Ok(server) => match server.run() {
            Ok(_) => {}
            Err(e) => {
                error!("Error: {:?}", e);
            }
        },
        Err(e) => {
            // ...
        }
    }

    println!("Exiting...");
}


#[no_mangle]
pub extern "C" fn exit() {
    let sender = &SH_TX_RX.0;
    sender.send(());
    ()
}

I thought that it needs to be static, because I cannot keep a pointer to MyServer in my C++ code. However, I get the following compiler errors:

error[E0507]: cannot move out of dereference of `SH_TX_RX`
  --> src/lib.rs:35:47
   |
35 |     match srv::MyServer::new(SHUTDOWN.1) {
   |                                               ^^^^^^^^^^ move occurs because value has type `tokio::sync::oneshot::Receiver<()>`, which does not implement the `Copy` trait

error[E0507]: cannot move out of `*sender` which is behind a shared reference
  --> src/lib.rs:60:5
   |
60 |     sender.send(());
   |     ^^^^^^^^^^^^^^^ move occurs because `*sender` has type `tokio::sync::oneshot::Sender<()>`, which does not implement the `Copy` trait
John Doe
  • 113
  • 1
  • 2
  • 11
  • Why can't you return the (boxed) receiver to the C++ code? – Chayim Friedman Apr 26 '22 at 01:34
  • Nitpick: don't use lazy_static, use once_cell as it's going to be integrated into std - https://doc.rust-lang.org/stable/std/lazy. – Chayim Friedman Apr 26 '22 at 01:35
  • @ChayimFriedman Maybe I can, I don't know. You mean I can return a pointer to `MyServer` and then call a function that executes `sender.send(())` to start the shutdown procedure? I am a Rust beginner and I still think this is more an XY problem. – John Doe Apr 26 '22 at 07:15
  • 1
    Yes, that is what I meant. – Chayim Friedman Apr 26 '22 at 07:38
  • @ChayimFriedman Okay, I see. Can you please provide some hints on how to return a "boxed receiver"? – John Doe Apr 26 '22 at 07:53
  • `pub extern "C" fn run() -> Box> { let (sender, receiver) = oneshot::channel(); ... Box::new(receiver) }` and `pub extern "C" fn exit(receiver: Box) { ... }`. In C++ just type it as `void*`. – Chayim Friedman Apr 26 '22 at 07:57
  • @ChayimFriedman Unfortunately, your suggestion does not work. `run` starts a new thread, so instead I have to call first a method to create `(sender, receiver)` and return the pointer to the C++ code. Now the problem is that I can use `pub extern "C" fn run(ptr: *mut Shutdown)` and then when I want to exit `pub extern "C" fn exit(ptr: *mut Shutdown)`, but when executing `sender.send(())` the message will never be received, even though the channel is open and the result is `Ok()`. Do you have an idea why this happens? – John Doe May 04 '22 at 18:05

0 Answers0