1

不是我的偏好,但我今天不得不写一些 Rust,所以我试图创建一个只有一个端点的 Rocket 实例,但是在那个端点上我需要访问一个在 main.js 期间创建的变量。该变量需要很长时间才能被实例化,所以这就是我在那里这样做的原因。

我的问题是我找不到安全通过它的方法。无论我做什么,即使库似乎是线程安全的,编译器也会抱怨线程安全:https ://github.com/brave/adblock-rust/pull/130 (在我的本地实例上找到了提交的代码)

这是我得到的错误:

   |
18 | / lazy_static! {
19 | |     static ref rules_engine: Mutex<Vec<Engine>> = Mutex::new(vec![]);
20 | | }
   | |_^ `std::rc::Rc<std::cell::RefCell<lifeguard::CappedCollection<std::vec::Vec<u64>>>>` cannot be sent between threads safely
   | 

...这是我的代码:

#![feature(proc_macro_hygiene, decl_macro)]

#[macro_use]
extern crate rocket;

use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

use lazy_static::lazy_static;
use std::sync::Mutex;

use adblock::engine::Engine;
use adblock::lists::FilterFormat;

use rocket::request::{Form, FormError, FormDataError};

lazy_static! {
    static ref rules_engine: Mutex<Vec<Engine>> = Mutex::new(vec![]);
}

fn main() {
    if !Path::new("./rules.txt").exists() {
        println!("rules file does not exist")
    } else {
        println!("loading rules");

        let mut rules = vec![];

        if let Ok(lines) = read_lines("./rules.txt") {
            for line in lines {
                if let Ok(ip) = line {
                    rules.insert(0, ip)
                }
            }

            let eng = Engine::from_rules(&rules, FilterFormat::Standard);
            rules_engine.lock().unwrap().push(eng);
            rocket().launch();
        }
    }
}

#[derive(Debug, FromForm)]
struct FormInput<> {
    #[form(field = "textarea")]
    text_area: String
}

#[post("/", data = "<sink>")]
fn sink(sink: Result<Form<FormInput>, FormError>) -> String {
    match sink {
        Ok(form) => {
            format!("{:?}", &*form)
        }
        Err(FormDataError::Io(_)) => format!("Form input was invalid UTF-8."),
        Err(FormDataError::Malformed(f)) | Err(FormDataError::Parse(_, f)) => {
            format!("Invalid form input: {}", f)
        }
    }
}

fn rocket() -> rocket::Rocket {
    rocket::ignite().mount("/", routes![sink])
}

fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
    where P: AsRef<Path>, {
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

有什么方法eng可以在sink端点方法中使用?

4

2 回答 2

2

Rc不是线程安全的,即使在互斥体后面也是如此。它看起来像Rc使用在eng.blocker.pool.poolwhich is alifeguard::Pool中。所以不,这Engine不是线程安全的(至少默认情况下)。

幸运的是,adblock crate 似乎有一个称为“对象池”的功能,它启用了该特定功能。删除该功能将(希望)使其线程安全。

于 2020-09-06T10:55:15.720 回答
1

Rocket 使在路由之间共享资源变得非常容易(以及在main您可能产生的任何其他线程之间main)。他们称他们的机制state在此处查看其文档。

举一个简短的例子来说明它是如何工作的:您创建要在应用程序中共享的类型,并在您用于应用程序manage的实例中创建该类型的实例rocket。在指南中,他们给出了这个例子:

use std::sync::atomic::AtomicUsize;

struct HitCount {
    count: AtomicUsize
}

rocket::build().manage(HitCount { count: AtomicUsize::new(0) });

在一条路线中,您可以像这样访问资源(再次来自指南):

use rocket::State;

#[get("/count")]
fn count(hit_count: &State<HitCount>) -> String {
    let current_count = hit_count.count.load(Ordering::Relaxed);
    format!("Number of visits: {}", current_count)
}

当我学习火箭时,我需要共享一个包含 a 的结构String,这本身不是线程安全的。这意味着您需要先将其包装成一个Mutex,然后才能manage使用火箭。

此外,据我了解,任何特定类型的资源只能与manage. 但是在这种情况下,您可以创建不同命名的包装器类型并解决该限制。

于 2021-08-13T12:29:09.247 回答