Help required with threads

Objective my code is:
Displaying the cam output that is read through OpenCV at web browser <img> tag.

Threads in process:

  1. Server backed by rocker.rs
  2. WebView backed by web-view.rs
  3. OpenCV backed by opencv-rust

I think each once of the above required to be run in a separate thread to get things done smoothly, I wrote the below code:

use web_view::*;
use std::thread;
use opencv::{core, highgui, prelude::*, videoio, core::Size2i, imgcodecs};

use rocket_contrib::serve::StaticFiles;

fn main() {
    thread::spawn(move || {
        rocket::ignite()
        .mount("/static",
           StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/static")))
       .launch();
    });

    let webview:WebView<()> = web_view::builder()
         .title("My Project")
         .content(Content::Url("http://localhost:8000/static/"))
         .size(800, 600)
         .resizable(false)
         .debug(true)
         .user_data(())
         .invoke_handler(invoke_handler)
         .build()
         .unwrap();
    webview.run().unwrap();
}

fn invoke_handler(wv: &mut WebView<()>, arg: &str) -> WVResult {
        match arg {
            "Run openCV" => openCV(wv),
            _ => (),
        }
    Ok(())
}

fn openCV(wv: &mut WebView<()>){
    run(wv).unwrap();
}

fn refresh(wv: &mut WebView<()>){
    wv.eval(&format!(r#"
        var dt = new Date();
        img = document.getElementById('img');
        img.src="c:/photos/savedImage.jpg"+ "?" + dt.getTime();
    "#)).unwrap();
}

fn run(wv: &mut WebView<()>) -> opencv::Result<()> {
    let window = "video capture";
    let filename = "static/savedImage.jpg";
    highgui::named_window(window, 1)?;
    highgui::set_window_property(window, highgui::WND_PROP_FULLSCREEN, highgui::WINDOW_NORMAL as f64)?;
    #[cfg(feature = "opencv-32")]
    let mut cam = videoio::VideoCapture::new_default(0)?;  // 0 is the default camera
    #[cfg(not(feature = "opencv-32"))]
    let mut cam = videoio::VideoCapture::new(0, videoio::CAP_ANY)?;  // 0 is the default camera

    let opened = videoio::VideoCapture::is_opened(&cam)?;

    if !opened {
        panic!("Unable to open default camera!");
    }
    loop {
        let mut frame = core::Mat::default()?;
        cam.read(&mut frame)?;
        if frame.size()?.width > 0 {
            highgui::imshow(window, &mut frame)?;
            imgcodecs::imwrite(filename, &frame, &opencv::types::VectorOfi32::new());

            // refresh(wv);  // This one is not working
                thread::spawn(move || {
                    refresh(wv);
                });
        }
        let key = highgui::wait_key(10)?;
        if key > 0 && key != 255 {
            break;
        }
    }
    highgui::destroy_all_windows();
    Ok(())
}

With the below Cargo.toml

[package]
name = "wv"
version = "0.1.0"
authors = ["Hasan Yousef"]
edition = "2018"

[dependencies]
opencv = {version = "0.34", features = ["buildtime-bindgen"]}
web-view = "0.6.2"
rocket = "0.4.4"
rocket_contrib = "0.4.4"

And below static/index.html:

<!doctype html>
<html>
	<body>
        <button onclick="external.invoke('Run openCV')">OpenCV</button><br><br><br>
        <img id='img' src='/static/savedImage.jpg' alt='Lamp' width='320' height='320'/>
    </body>
</html>

With the above, server, WebView and OpenCV are working fine, but the refresh command is not working, and once I tried to get it in a separate thread as below, I got error:
Note: The refresh function is a rust function calling the WebView to execute JavaScript function that refresh the saved image.

thread::spawn(move || {
        refresh(wv);
})

The error I got is:

error[E0277]: `*mut webview_sys::CWebView` cannot be sent between threads safely
   --> src\main.rs:71:14
    |
71  |              thread::spawn(move || {
    |              ^^^^^^^^^^^^^ `*mut webview_sys::CWebView` cannot be sent between threads safely
    | 
   ::: C:\Users\Yara Yousef\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\src\libstd\thread\mod.rs:616:8
    |
616 |     F: Send + 'static,
    |        ---- required by this bound in `std::thread::spawn`
    |
    = help: within `web_view::WebView<'_, ()>`, the trait `std::marker::Send` is not implemented for `*mut webview_sys::CWebView`
    = note: required because it appears within the type `web_view::WebView<'_, ()>`
    = note: required because of the requirements on the impl of `std::marker::Send` for `&mut web_view::WebView<'_, ()>`
    = note: required because it appears within the type `[closure@src\main.rs:71:28: 73:15 wv:&mut web_view::WebView<'_, ()>]`

Well, the error you can't do that in a separate thread.

Any work around way?

Do it in the same thread.

Just to explain more, this is the typical kind of error you get when the Rust type system sees that an operation is not thread safe. In this case the WebView is not marked Send. Either it's not marked that because the authors haven't thought of it, or it's deliberate, because it's not safe to use it in this way. If it's a mature library, the latter is likely, but you can always ask what their multithreading story is.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.