-
Notifications
You must be signed in to change notification settings - Fork 154
Description
Background: I'm trying to write a service that receives source code via HTTP, parses it using syntect and then returns some information about it. I'm using Iron for it, but this issue would also occur with other uses. In Iron, the Handler
trait which handles requests is required to be Send
and Sync
, meaning it can be reused between threads.
So, the simplest implementation is to just call SyntaxSet::load_defaults_nonewlines()
on every request, then figure out which SyntaxDefinition
to use, and then parse the text. Unfortunately, this means that the syntax definitions have to be loaded for each request, which is quite a bit of work that could be avoided.
You can see where this is going. I'd like to have a way to only load SyntaxSet
once at startup and then share it between all the request handlers. Or if that's not possible, at least share the SyntaxDefinition
between threads (and have a mutex protecting SyntaxSet
).
If we try to write that with the current syntect, it would look something like this:
#[test]
fn threading_with_shared_syntax_set() {
let syntax_set = SyntaxSet::load_defaults_nonewlines();
let syntax = syntax_set.find_syntax_by_name("rust").unwrap();
thread::spawn(|| {
let parse_state = ParseState::new(&syntax);
let mut f = File::open("testdata/parser.rs").unwrap();
let mut reader = BufReader::new(f);
for line in reader.lines() {
let line = line.unwrap();
parse_state.parse_line(&line);
}
});
}
And it currently fails to compile like this:
[101] % cargo test threading
Compiling syntect v1.0.0 (file:///Users/rstocker/Projects/rust/syntect)
error[E0277]: the trait bound `std::rc::Rc<std::cell::RefCell<syntect::parsing::syntax_definition::Context>>: std::marker::Sync` is not satisfied
--> tests/threading.rs:14:5
|
14 | thread::spawn(|| {
| ^^^^^^^^^^^^^
|
= note: `std::rc::Rc<std::cell::RefCell<syntect::parsing::syntax_definition::Context>>` cannot be shared between threads safely
= note: required because it appears within the type `std::option::Option<std::rc::Rc<std::cell::RefCell<syntect::parsing::syntax_definition::Context>>>`
= note: required because it appears within the type `syntect::parsing::SyntaxDefinition`
= note: required because it appears within the type `&syntect::parsing::SyntaxDefinition`
= note: required because of the requirements on the impl of `std::marker::Send` for `&&syntect::parsing::SyntaxDefinition`
= note: required because it appears within the type `[closure@tests/threading.rs:14:19: 23:6 syntax:&&syntect::parsing::SyntaxDefinition]`
= note: required by `std::thread::spawn`
I tried to work around this with some unsafe code, but it panics (as expected) when multiple threads use the same SyntaxDefinition
.
As I understand the code currently, it is structured the way it is to avoid having to compile all the regexes eagerly. I wonder if there's a simple way to preserve that while still keeping that nice property. Alternatively, it would be cool if there was a way to precompile a SyntaxDefinition
and get a new type that is immutable and thus Send
and Sync
. By the way, apart from this the library works beautifully :).