1
Fork 0
mirror of https://github.com/RGBCube/cstree synced 2025-07-27 17:17:45 +00:00

Make SyntaxNode Send and Sync (#12)

This commit is contained in:
DQ 2021-02-04 15:58:17 +01:00 committed by GitHub
parent 4116ed0138
commit fb463aef18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 175 additions and 0 deletions

18
Cargo.lock generated
View file

@ -15,6 +15,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.2" version = "1.4.2"
@ -27,10 +33,22 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crossbeam-utils"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
dependencies = [
"autocfg",
"cfg-if",
"lazy_static",
]
[[package]] [[package]]
name = "cstree" name = "cstree"
version = "0.0.2" version = "0.0.2"
dependencies = [ dependencies = [
"crossbeam-utils",
"fxhash", "fxhash",
"lasso", "lasso",
"m_lexer", "m_lexer",

View file

@ -25,6 +25,7 @@ features = ["derive"]
m_lexer = "0.0.4" m_lexer = "0.0.4"
serde_json = "1.0.61" serde_json = "1.0.61"
serde_test = "1.0.119" serde_test = "1.0.119"
crossbeam-utils = "0.8"
[features] [features]
default = [] default = []

View file

@ -38,6 +38,9 @@ pub struct SyntaxNode<L: Language, D: 'static = (), R: 'static = ()> {
data: *mut NodeData<L, D, R>, data: *mut NodeData<L, D, R>,
} }
unsafe impl<L: Language, D: 'static, R: 'static> Send for SyntaxNode<L, D, R> {}
unsafe impl<L: Language, D: 'static, R: 'static> Sync for SyntaxNode<L, D, R> {}
impl<L: Language, D, R> SyntaxNode<L, D, R> { impl<L: Language, D, R> SyntaxNode<L, D, R> {
pub fn debug(&self, resolver: &impl Resolver, recursive: bool) -> String { pub fn debug(&self, resolver: &impl Resolver, recursive: bool) -> String {
// NOTE: `fmt::Write` methods on `String` never fail // NOTE: `fmt::Write` methods on `String` never fail

153
tests/sendsync.rs Normal file
View file

@ -0,0 +1,153 @@
#![allow(clippy::redundant_clone)]
#[allow(unused)]
mod common;
use crossbeam_utils::thread::scope;
use std::{thread, time::Duration};
use common::{build_recursive, Element, SyntaxNode};
use cstree::GreenNodeBuilder;
use lasso::Resolver;
fn build_tree<D>(root: &Element<'_>) -> SyntaxNode<D, impl Resolver> {
let mut builder = GreenNodeBuilder::new();
build_recursive(root, &mut builder, 0);
let (node, interner) = builder.finish();
SyntaxNode::new_root_with_resolver(node, interner.unwrap().into_resolver())
}
fn two_level_tree() -> Element<'static> {
use Element::*;
Node(vec![
Node(vec![Token("0.0"), Token("0.1")]),
Node(vec![Token("1.0")]),
Node(vec![Token("2.0"), Token("2.1"), Token("2.2")]),
])
}
#[test]
#[cfg_attr(miri, ignore)]
fn send() {
let tree = two_level_tree();
let tree = build_tree::<()>(&tree);
let thread_tree = tree.clone();
let thread = thread::spawn(move || {
let leaf1_0 = thread_tree
.children()
.nth(1)
.unwrap()
.children_with_tokens()
.next()
.unwrap();
let leaf1_0 = leaf1_0.into_token().unwrap();
leaf1_0.resolve_text(thread_tree.resolver().as_ref()).to_string()
});
assert_eq!(thread.join().unwrap(), "1.0");
}
#[test]
#[cfg_attr(miri, ignore)]
fn send_data() {
let tree = two_level_tree();
let tree = build_tree::<String>(&tree);
let thread_tree = tree.clone();
{
let node2 = tree.children().nth(2).unwrap();
assert_eq!(*node2.try_set_data("data".into()).unwrap(), "data");
let data = node2.get_data().unwrap();
assert_eq!(data.as_str(), "data");
node2.set_data("payload".into());
let data = node2.get_data().unwrap();
assert_eq!(data.as_str(), "payload");
}
let t = thread::spawn(move || {
let node2 = thread_tree.children().nth(2).unwrap();
assert!(node2.try_set_data("already present".into()).is_err());
let data = node2.get_data().unwrap();
assert_eq!(data.as_str(), "payload");
node2.set_data("new data".into());
});
// wait for t to finish
t.join().unwrap();
{
let node2 = tree.children().nth(2).unwrap();
let data = node2.get_data().unwrap();
assert_eq!(data.as_str(), "new data");
node2.clear_data();
// re-use `data` after node data was cleared
assert_eq!(data.as_str(), "new data");
}
let thread_tree = tree.clone();
thread::spawn(move || {
let node2 = thread_tree.children().nth(2).unwrap();
assert_eq!(node2.get_data(), None);
})
.join()
.unwrap();
}
#[test]
#[cfg_attr(miri, ignore)]
fn sync() {
let tree = two_level_tree();
let tree = build_tree::<()>(&tree);
let thread_tree = &tree;
let result = scope(move |s| {
s.spawn(move |_| {
let leaf1_0 = thread_tree
.children()
.nth(1)
.unwrap()
.children_with_tokens()
.next()
.unwrap();
let leaf1_0 = leaf1_0.into_token().unwrap();
leaf1_0.resolve_text(thread_tree.resolver().as_ref()).to_string()
})
.join()
.unwrap()
});
assert_eq!(result.unwrap(), "1.0");
}
#[test]
#[cfg_attr(miri, ignore)]
fn drop_send() {
let tree = two_level_tree();
let tree = build_tree::<()>(&tree);
let thread_tree = tree.clone();
let thread = thread::spawn(move || {
drop(thread_tree);
});
thread.join().unwrap();
thread::sleep(Duration::from_millis(500));
drop(tree);
let tree = two_level_tree();
let tree = build_tree::<()>(&tree);
let thread_tree = tree.clone();
drop(tree);
let thread = thread::spawn(move || {
thread::sleep(Duration::from_millis(500));
drop(thread_tree);
});
thread.join().unwrap();
}
#[test]
#[cfg_attr(miri, ignore)]
#[allow(clippy::drop_ref)]
fn drop_sync() {
let tree = two_level_tree();
let tree = build_tree::<()>(&tree);
let thread_tree = &tree;
scope(move |s| {
s.spawn(move |_| {
drop(thread_tree);
});
})
.unwrap();
thread::sleep(Duration::from_millis(500));
drop(tree);
}