aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_analysis/src/imp.rs25
-rw-r--r--crates/ra_hir/src/db.rs4
-rw-r--r--crates/ra_hir/src/lib.rs14
-rw-r--r--crates/ra_hir/src/module.rs10
-rw-r--r--crates/ra_hir/src/module/imp.rs4
-rw-r--r--crates/ra_hir/src/module/nameres.rs4
-rw-r--r--crates/ra_hir/src/query_definitions.rs6
-rw-r--r--crates/ra_hir/src/source_binder.rs11
-rw-r--r--crates/ra_lsp_server/Cargo.toml1
-rw-r--r--crates/ra_lsp_server/src/lib.rs1
-rw-r--r--crates/ra_lsp_server/src/main.rs10
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs6
-rw-r--r--crates/ra_lsp_server/src/project_model.rs10
-rw-r--r--crates/ra_lsp_server/src/vfs.rs7
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/support.rs6
-rw-r--r--crates/thread_worker/Cargo.toml11
-rw-r--r--crates/thread_worker/src/lib.rs (renamed from crates/ra_lsp_server/src/thread_watcher.rs)56
17 files changed, 106 insertions, 80 deletions
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 03d17de0d..0de0e2645 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -10,7 +10,7 @@ use ra_syntax::{
10 SyntaxKind::*, 10 SyntaxKind::*,
11 SyntaxNodeRef, TextRange, TextUnit, 11 SyntaxNodeRef, TextRange, TextUnit,
12}; 12};
13use ra_db::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE, SyntaxDatabase, SourceFileQuery}; 13use ra_db::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE, SyntaxDatabase};
14use rayon::prelude::*; 14use rayon::prelude::*;
15use rustc_hash::FxHashSet; 15use rustc_hash::FxHashSet;
16use salsa::{Database, ParallelDatabase}; 16use salsa::{Database, ParallelDatabase};
@@ -43,6 +43,7 @@ impl AnalysisHostImpl {
43 } 43 }
44 pub fn apply_change(&mut self, change: AnalysisChange) { 44 pub fn apply_change(&mut self, change: AnalysisChange) {
45 log::info!("apply_change {:?}", change); 45 log::info!("apply_change {:?}", change);
46 // self.gc_syntax_trees();
46 47
47 for (file_id, text) in change.files_changed { 48 for (file_id, text) in change.files_changed {
48 self.db 49 self.db
@@ -115,6 +116,25 @@ impl AnalysisHostImpl {
115 .set((), Arc::new(crate_graph)) 116 .set((), Arc::new(crate_graph))
116 } 117 }
117 } 118 }
119
120 #[allow(unused)]
121 /// Ideally, we should call this function from time to time to collect heavy
122 /// syntax trees. However, if we actually do that, everything is recomputed
123 /// for some reason. Needs investigation.
124 fn gc_syntax_trees(&mut self) {
125 self.db
126 .query(ra_db::SourceFileQuery)
127 .sweep(salsa::SweepStrategy::default().discard_values());
128 self.db
129 .query(hir::db::FnSyntaxQuery)
130 .sweep(salsa::SweepStrategy::default().discard_values());
131 self.db
132 .query(hir::db::SourceFileItemsQuery)
133 .sweep(salsa::SweepStrategy::default().discard_values());
134 self.db
135 .query(hir::db::FileItemQuery)
136 .sweep(salsa::SweepStrategy::default().discard_values());
137 }
118} 138}
119 139
120pub(crate) struct AnalysisImpl { 140pub(crate) struct AnalysisImpl {
@@ -160,9 +180,6 @@ impl AnalysisImpl {
160 .filter_map(|it| it.ok()) 180 .filter_map(|it| it.ok())
161 .collect() 181 .collect()
162 }; 182 };
163 self.db
164 .query(SourceFileQuery)
165 .sweep(salsa::SweepStrategy::default().discard_values());
166 Ok(query.search(&buf)) 183 Ok(query.search(&buf))
167 } 184 }
168 /// This returns `Vec` because a module may be included from several places. We 185 /// This returns `Vec` because a module may be included from several places. We
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index ff41fd326..62cf9ab17 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -27,20 +27,16 @@ pub trait HirDatabase: SyntaxDatabase
27 } 27 }
28 fn fn_syntax(fn_id: FnId) -> FnDefNode { 28 fn fn_syntax(fn_id: FnId) -> FnDefNode {
29 type FnSyntaxQuery; 29 type FnSyntaxQuery;
30 // Don't retain syntax trees in memory
31 storage dependencies;
32 use fn query_definitions::fn_syntax; 30 use fn query_definitions::fn_syntax;
33 } 31 }
34 32
35 fn file_items(file_id: FileId) -> Arc<SourceFileItems> { 33 fn file_items(file_id: FileId) -> Arc<SourceFileItems> {
36 type SourceFileItemsQuery; 34 type SourceFileItemsQuery;
37 storage dependencies;
38 use fn query_definitions::file_items; 35 use fn query_definitions::file_items;
39 } 36 }
40 37
41 fn file_item(source_item_id: SourceItemId) -> SyntaxNode { 38 fn file_item(source_item_id: SourceItemId) -> SyntaxNode {
42 type FileItemQuery; 39 type FileItemQuery;
43 storage dependencies;
44 use fn query_definitions::file_item; 40 use fn query_definitions::file_item;
45 } 41 }
46 42
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 760524f6b..5941a9ea3 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -109,7 +109,8 @@ pub(crate) type SourceFileItemId = Id<SyntaxNode>;
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
110pub struct SourceItemId { 110pub struct SourceItemId {
111 file_id: FileId, 111 file_id: FileId,
112 item_id: SourceFileItemId, 112 /// None for the whole file.
113 item_id: Option<SourceFileItemId>,
113} 114}
114 115
115/// Maps item's `SyntaxNode`s to `SourceFileItemId` and back. 116/// Maps item's `SyntaxNode`s to `SourceFileItemId` and back.
@@ -143,11 +144,14 @@ impl SourceFileItems {
143 return id; 144 return id;
144 } 145 }
145 // This should not happen. Let's try to give a sensible diagnostics. 146 // This should not happen. Let's try to give a sensible diagnostics.
146 if let Some((_, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) { 147 if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) {
147 panic!( 148 // FIXME(#288): whyyy are we getting here?
149 log::error!(
148 "unequal syntax nodes with the same range:\n{:?}\n{:?}", 150 "unequal syntax nodes with the same range:\n{:?}\n{:?}",
149 item, i 151 item,
150 ) 152 i
153 );
154 return id;
151 } 155 }
152 panic!( 156 panic!(
153 "Can't find {:?} in SourceFileItems:\n{:?}", 157 "Can't find {:?} in SourceFileItems:\n{:?}",
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs
index c6bb76d56..d5866f6ef 100644
--- a/crates/ra_hir/src/module.rs
+++ b/crates/ra_hir/src/module.rs
@@ -271,15 +271,13 @@ pub struct ModuleData {
271 271
272impl ModuleSource { 272impl ModuleSource {
273 // precondition: item_id **must** point to module 273 // precondition: item_id **must** point to module
274 fn new(file_id: FileId, item_id: SourceFileItemId) -> ModuleSource { 274 fn new(file_id: FileId, item_id: Option<SourceFileItemId>) -> ModuleSource {
275 let source_item_id = SourceItemId { file_id, item_id }; 275 let source_item_id = SourceItemId { file_id, item_id };
276 ModuleSource(source_item_id) 276 ModuleSource(source_item_id)
277 } 277 }
278 278
279 pub(crate) fn new_file(db: &impl HirDatabase, file_id: FileId) -> ModuleSource { 279 pub(crate) fn new_file(file_id: FileId) -> ModuleSource {
280 let file_items = db.file_items(file_id); 280 ModuleSource::new(file_id, None)
281 let item_id = file_items.id_of_source_file();
282 ModuleSource::new(file_id, item_id)
283 } 281 }
284 282
285 pub(crate) fn new_inline( 283 pub(crate) fn new_inline(
@@ -290,7 +288,7 @@ impl ModuleSource {
290 assert!(!m.has_semi()); 288 assert!(!m.has_semi());
291 let file_items = db.file_items(file_id); 289 let file_items = db.file_items(file_id);
292 let item_id = file_items.id_of(file_id, m.syntax()); 290 let item_id = file_items.id_of(file_id, m.syntax());
293 ModuleSource::new(file_id, item_id) 291 ModuleSource::new(file_id, Some(item_id))
294 } 292 }
295 293
296 pub fn file_id(self) -> FileId { 294 pub fn file_id(self) -> FileId {
diff --git a/crates/ra_hir/src/module/imp.rs b/crates/ra_hir/src/module/imp.rs
index 9f144e139..4a19842c4 100644
--- a/crates/ra_hir/src/module/imp.rs
+++ b/crates/ra_hir/src/module/imp.rs
@@ -66,7 +66,7 @@ fn create_module_tree<'a>(
66 66
67 let source_root = db.source_root(source_root); 67 let source_root = db.source_root(source_root);
68 for &file_id in source_root.files.iter() { 68 for &file_id in source_root.files.iter() {
69 let source = ModuleSource::new_file(db, file_id); 69 let source = ModuleSource::new_file(file_id);
70 if visited.contains(&source) { 70 if visited.contains(&source) {
71 continue; // TODO: use explicit crate_roots here 71 continue; // TODO: use explicit crate_roots here
72 } 72 }
@@ -126,7 +126,7 @@ fn build_subtree(
126 visited, 126 visited,
127 roots, 127 roots,
128 Some(link), 128 Some(link),
129 ModuleSource::new_file(db, file_id), 129 ModuleSource::new_file(file_id),
130 ), 130 ),
131 }) 131 })
132 .collect::<Cancelable<Vec<_>>>()?; 132 .collect::<Cancelable<Vec<_>>>()?;
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs
index 9afeade9e..5540b827f 100644
--- a/crates/ra_hir/src/module/nameres.rs
+++ b/crates/ra_hir/src/module/nameres.rs
@@ -98,7 +98,7 @@ impl NamedImport {
98 pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange { 98 pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange {
99 let source_item_id = SourceItemId { 99 let source_item_id = SourceItemId {
100 file_id, 100 file_id,
101 item_id: self.file_item_id, 101 item_id: Some(self.file_item_id),
102 }; 102 };
103 let syntax = db.file_item(source_item_id); 103 let syntax = db.file_item(source_item_id);
104 let offset = syntax.borrowed().range().start(); 104 let offset = syntax.borrowed().range().start();
@@ -281,7 +281,7 @@ where
281 module_id, 281 module_id,
282 source_item_id: SourceItemId { 282 source_item_id: SourceItemId {
283 file_id, 283 file_id,
284 item_id: item.id, 284 item_id: Some(item.id),
285 }, 285 },
286 }; 286 };
287 let def_id = def_loc.id(self.db); 287 let def_id = def_loc.id(self.db);
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs
index 37c4f9e4f..efaeb1525 100644
--- a/crates/ra_hir/src/query_definitions.rs
+++ b/crates/ra_hir/src/query_definitions.rs
@@ -38,7 +38,6 @@ pub(super) fn fn_scopes(db: &impl HirDatabase, fn_id: FnId) -> Arc<FnScopes> {
38pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { 38pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> {
39 let mut res = SourceFileItems::new(file_id); 39 let mut res = SourceFileItems::new(file_id);
40 let source_file = db.source_file(file_id); 40 let source_file = db.source_file(file_id);
41 res.alloc(source_file.syntax().owned());
42 let source_file = source_file.borrowed(); 41 let source_file = source_file.borrowed();
43 source_file 42 source_file
44 .syntax() 43 .syntax()
@@ -52,7 +51,10 @@ pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFi
52} 51}
53 52
54pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> SyntaxNode { 53pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> SyntaxNode {
55 db.file_items(source_item_id.file_id)[source_item_id.item_id].clone() 54 match source_item_id.item_id {
55 Some(id) => db.file_items(source_item_id.file_id)[id].clone(),
56 None => db.source_file(source_item_id.file_id).syntax().owned(),
57 }
56} 58}
57 59
58pub(crate) fn submodules( 60pub(crate) fn submodules(
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 0c16ccc24..ce2a0f2e8 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -20,7 +20,7 @@ use crate::{
20 20
21/// Locates the module by `FileId`. Picks topmost module in the file. 21/// Locates the module by `FileId`. Picks topmost module in the file.
22pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> { 22pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> {
23 let module_source = ModuleSource::new_file(db, file_id); 23 let module_source = ModuleSource::new_file(file_id);
24 module_from_source(db, module_source) 24 module_from_source(db, module_source)
25} 25}
26 26
@@ -32,7 +32,7 @@ pub fn module_from_position(
32 let file = db.source_file(position.file_id); 32 let file = db.source_file(position.file_id);
33 let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) { 33 let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) {
34 Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id, m), 34 Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id, m),
35 _ => ModuleSource::new_file(db, position.file_id), 35 _ => ModuleSource::new_file(position.file_id),
36 }; 36 };
37 module_from_source(db, module_source) 37 module_from_source(db, module_source)
38} 38}
@@ -50,7 +50,7 @@ pub fn module_from_child_node(
50 { 50 {
51 ModuleSource::new_inline(db, file_id, m) 51 ModuleSource::new_inline(db, file_id, m)
52 } else { 52 } else {
53 ModuleSource::new_file(db, file_id) 53 ModuleSource::new_file(file_id)
54 }; 54 };
55 module_from_source(db, module_source) 55 module_from_source(db, module_source)
56} 56}
@@ -76,7 +76,10 @@ pub fn function_from_source(
76 let module = ctry!(module_from_child_node(db, file_id, fn_def.syntax())?); 76 let module = ctry!(module_from_child_node(db, file_id, fn_def.syntax())?);
77 let file_items = db.file_items(file_id); 77 let file_items = db.file_items(file_id);
78 let item_id = file_items.id_of(file_id, fn_def.syntax()); 78 let item_id = file_items.id_of(file_id, fn_def.syntax());
79 let source_item_id = SourceItemId { file_id, item_id }; 79 let source_item_id = SourceItemId {
80 file_id,
81 item_id: Some(item_id),
82 };
80 let def_loc = DefLoc { 83 let def_loc = DefLoc {
81 kind: DefKind::Function, 84 kind: DefKind::Function,
82 source_root_id: module.source_root_id, 85 source_root_id: module.source_root_id,
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index 133decc52..30a8d35cd 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -26,6 +26,7 @@ text_unit = { version = "0.1.2", features = ["serde"] }
26smol_str = { version = "0.1.5", features = ["serde"] } 26smol_str = { version = "0.1.5", features = ["serde"] }
27rustc-hash = "1.0" 27rustc-hash = "1.0"
28 28
29thread_worker = { path = "../thread_worker" }
29ra_syntax = { path = "../ra_syntax" } 30ra_syntax = { path = "../ra_syntax" }
30ra_editor = { path = "../ra_editor" } 31ra_editor = { path = "../ra_editor" }
31ra_text_edit = { path = "../ra_text_edit" } 32ra_text_edit = { path = "../ra_text_edit" }
diff --git a/crates/ra_lsp_server/src/lib.rs b/crates/ra_lsp_server/src/lib.rs
index 75c6fa1b8..1d7258c35 100644
--- a/crates/ra_lsp_server/src/lib.rs
+++ b/crates/ra_lsp_server/src/lib.rs
@@ -5,7 +5,6 @@ mod path_map;
5mod project_model; 5mod project_model;
6pub mod req; 6pub mod req;
7mod server_world; 7mod server_world;
8pub mod thread_watcher;
9mod vfs; 8mod vfs;
10 9
11pub type Result<T> = ::std::result::Result<T, ::failure::Error>; 10pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs
index 9ba972562..4497980e5 100644
--- a/crates/ra_lsp_server/src/main.rs
+++ b/crates/ra_lsp_server/src/main.rs
@@ -6,11 +6,11 @@ use ra_lsp_server::Result;
6 6
7fn main() -> Result<()> { 7fn main() -> Result<()> {
8 ::std::env::set_var("RUST_BACKTRACE", "short"); 8 ::std::env::set_var("RUST_BACKTRACE", "short");
9 Logger::with_env_or_str("error") 9 let logger = Logger::with_env_or_str("error").duplicate_to_stderr(Duplicate::All);
10 .duplicate_to_stderr(Duplicate::All) 10 match ::std::env::var("RA_INTERNAL_MODE") {
11 .log_to_file() 11 Ok(ref v) if v == "1" => logger.log_to_file().directory("log").start()?,
12 .directory("log") 12 _ => logger.start()?,
13 .start()?; 13 };
14 log::info!("lifecycle: server started"); 14 log::info!("lifecycle: server started");
15 match ::std::panic::catch_unwind(main_inner) { 15 match ::std::panic::catch_unwind(main_inner) {
16 Ok(res) => { 16 Ok(res) => {
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index 41f70f263..eab82ee85 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -10,6 +10,7 @@ use gen_lsp_server::{
10use languageserver_types::NumberOrString; 10use languageserver_types::NumberOrString;
11use ra_analysis::{Canceled, FileId, LibraryData}; 11use ra_analysis::{Canceled, FileId, LibraryData};
12use rayon; 12use rayon;
13use thread_worker::Worker;
13use threadpool::ThreadPool; 14use threadpool::ThreadPool;
14use rustc_hash::FxHashSet; 15use rustc_hash::FxHashSet;
15use serde::{de::DeserializeOwned, Serialize}; 16use serde::{de::DeserializeOwned, Serialize};
@@ -21,7 +22,6 @@ use crate::{
21 project_model::{workspace_loader, CargoWorkspace}, 22 project_model::{workspace_loader, CargoWorkspace},
22 req, 23 req,
23 server_world::{ServerWorld, ServerWorldState}, 24 server_world::{ServerWorld, ServerWorldState},
24 thread_watcher::Worker,
25 vfs::{self, FileEvent}, 25 vfs::{self, FileEvent},
26 Result, 26 Result,
27}; 27};
@@ -92,8 +92,8 @@ pub fn main_loop(
92 let ws_res = ws_watcher.stop(); 92 let ws_res = ws_watcher.stop();
93 93
94 main_res?; 94 main_res?;
95 fs_res?; 95 fs_res.map_err(|_| format_err!("fs watcher died"))?;
96 ws_res?; 96 ws_res.map_err(|_| format_err!("ws watcher died"))?;
97 97
98 Ok(()) 98 Ok(())
99} 99}
diff --git a/crates/ra_lsp_server/src/project_model.rs b/crates/ra_lsp_server/src/project_model.rs
index cb91ada90..b881f8b6f 100644
--- a/crates/ra_lsp_server/src/project_model.rs
+++ b/crates/ra_lsp_server/src/project_model.rs
@@ -4,11 +4,9 @@ use cargo_metadata::{metadata_run, CargoOpt};
4use ra_syntax::SmolStr; 4use ra_syntax::SmolStr;
5use rustc_hash::{FxHashMap, FxHashSet}; 5use rustc_hash::{FxHashMap, FxHashSet};
6use failure::{format_err, bail}; 6use failure::{format_err, bail};
7use thread_worker::{WorkerHandle, Worker};
7 8
8use crate::{ 9use crate::Result;
9 Result,
10 thread_watcher::{ThreadWatcher, Worker},
11};
12 10
13/// `CargoWorksapce` represents the logical structure of, well, a Cargo 11/// `CargoWorksapce` represents the logical structure of, well, a Cargo
14/// workspace. It pretty closely mirrors `cargo metadata` output. 12/// workspace. It pretty closely mirrors `cargo metadata` output.
@@ -199,8 +197,8 @@ impl TargetKind {
199 } 197 }
200} 198}
201 199
202pub fn workspace_loader() -> (Worker<PathBuf, Result<CargoWorkspace>>, ThreadWatcher) { 200pub fn workspace_loader() -> (Worker<PathBuf, Result<CargoWorkspace>>, WorkerHandle) {
203 Worker::<PathBuf, Result<CargoWorkspace>>::spawn( 201 thread_worker::spawn::<PathBuf, Result<CargoWorkspace>, _>(
204 "workspace loader", 202 "workspace loader",
205 1, 203 1,
206 |input_receiver, output_sender| { 204 |input_receiver, output_sender| {
diff --git a/crates/ra_lsp_server/src/vfs.rs b/crates/ra_lsp_server/src/vfs.rs
index 00ab3e6c3..fcf7693d8 100644
--- a/crates/ra_lsp_server/src/vfs.rs
+++ b/crates/ra_lsp_server/src/vfs.rs
@@ -4,8 +4,7 @@ use std::{
4}; 4};
5 5
6use walkdir::WalkDir; 6use walkdir::WalkDir;
7 7use thread_worker::{WorkerHandle, Worker};
8use crate::thread_watcher::{ThreadWatcher, Worker};
9 8
10#[derive(Debug)] 9#[derive(Debug)]
11pub struct FileEvent { 10pub struct FileEvent {
@@ -18,8 +17,8 @@ pub enum FileEventKind {
18 Add(String), 17 Add(String),
19} 18}
20 19
21pub fn roots_loader() -> (Worker<PathBuf, (PathBuf, Vec<FileEvent>)>, ThreadWatcher) { 20pub fn roots_loader() -> (Worker<PathBuf, (PathBuf, Vec<FileEvent>)>, WorkerHandle) {
22 Worker::<PathBuf, (PathBuf, Vec<FileEvent>)>::spawn( 21 thread_worker::spawn::<PathBuf, (PathBuf, Vec<FileEvent>), _>(
23 "roots loader", 22 "roots loader",
24 128, 23 128,
25 |input_receiver, output_sender| { 24 |input_receiver, output_sender| {
diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs
index 4b75be3ee..07a878a26 100644
--- a/crates/ra_lsp_server/tests/heavy_tests/support.rs
+++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs
@@ -17,11 +17,11 @@ use languageserver_types::{
17use serde::Serialize; 17use serde::Serialize;
18use serde_json::{to_string_pretty, Value}; 18use serde_json::{to_string_pretty, Value};
19use tempdir::TempDir; 19use tempdir::TempDir;
20use thread_worker::{WorkerHandle, Worker};
20use test_utils::{parse_fixture, find_mismatch}; 21use test_utils::{parse_fixture, find_mismatch};
21 22
22use ra_lsp_server::{ 23use ra_lsp_server::{
23 main_loop, req, 24 main_loop, req,
24 thread_watcher::{ThreadWatcher, Worker},
25}; 25};
26 26
27pub fn project(fixture: &str) -> Server { 27pub fn project(fixture: &str) -> Server {
@@ -45,13 +45,13 @@ pub struct Server {
45 messages: RefCell<Vec<RawMessage>>, 45 messages: RefCell<Vec<RawMessage>>,
46 dir: TempDir, 46 dir: TempDir,
47 worker: Option<Worker<RawMessage, RawMessage>>, 47 worker: Option<Worker<RawMessage, RawMessage>>,
48 watcher: Option<ThreadWatcher>, 48 watcher: Option<WorkerHandle>,
49} 49}
50 50
51impl Server { 51impl Server {
52 fn new(dir: TempDir, files: Vec<(PathBuf, String)>) -> Server { 52 fn new(dir: TempDir, files: Vec<(PathBuf, String)>) -> Server {
53 let path = dir.path().to_path_buf(); 53 let path = dir.path().to_path_buf();
54 let (worker, watcher) = Worker::<RawMessage, RawMessage>::spawn( 54 let (worker, watcher) = thread_worker::spawn::<RawMessage, RawMessage, _>(
55 "test server", 55 "test server",
56 128, 56 128,
57 move |mut msg_receiver, mut msg_sender| { 57 move |mut msg_receiver, mut msg_sender| {
diff --git a/crates/thread_worker/Cargo.toml b/crates/thread_worker/Cargo.toml
new file mode 100644
index 000000000..62d66a1a3
--- /dev/null
+++ b/crates/thread_worker/Cargo.toml
@@ -0,0 +1,11 @@
1[package]
2edition = "2018"
3name = "thread_worker"
4version = "0.1.0"
5authors = ["Aleksey Kladov <[email protected]>"]
6
7[dependencies]
8drop_bomb = "0.1.0"
9crossbeam-channel = "0.2.4"
10log = "0.4.3"
11
diff --git a/crates/ra_lsp_server/src/thread_watcher.rs b/crates/thread_worker/src/lib.rs
index 99825d440..e558559ef 100644
--- a/crates/ra_lsp_server/src/thread_watcher.rs
+++ b/crates/thread_worker/src/lib.rs
@@ -1,28 +1,35 @@
1//! Small utility to correctly spawn crossbeam-channel based worker threads.
2
1use std::thread; 3use std::thread;
2 4
3use crossbeam_channel::{bounded, unbounded, Receiver, Sender}; 5use crossbeam_channel::{bounded, unbounded, Receiver, Sender};
4use drop_bomb::DropBomb; 6use drop_bomb::DropBomb;
5use failure::format_err;
6
7use crate::Result;
8 7
9pub struct Worker<I, O> { 8pub struct Worker<I, O> {
10 pub inp: Sender<I>, 9 pub inp: Sender<I>,
11 pub out: Receiver<O>, 10 pub out: Receiver<O>,
12} 11}
13 12
14impl<I, O> Worker<I, O> { 13pub struct WorkerHandle {
15 pub fn spawn<F>(name: &'static str, buf: usize, f: F) -> (Self, ThreadWatcher) 14 name: &'static str,
16 where 15 thread: thread::JoinHandle<()>,
17 F: FnOnce(Receiver<I>, Sender<O>) + Send + 'static, 16 bomb: DropBomb,
18 I: Send + 'static, 17}
19 O: Send + 'static,
20 {
21 let (worker, inp_r, out_s) = worker_chan(buf);
22 let watcher = ThreadWatcher::spawn(name, move || f(inp_r, out_s));
23 (worker, watcher)
24 }
25 18
19pub fn spawn<I, O, F>(name: &'static str, buf: usize, f: F) -> (Worker<I, O>, WorkerHandle)
20where
21 F: FnOnce(Receiver<I>, Sender<O>) + Send + 'static,
22 I: Send + 'static,
23 O: Send + 'static,
24{
25 let (worker, inp_r, out_s) = worker_chan(buf);
26 let watcher = WorkerHandle::spawn(name, move || f(inp_r, out_s));
27 (worker, watcher)
28}
29
30impl<I, O> Worker<I, O> {
31 /// Stops the worker. Returns the message receiver to fetch results which
32 /// have become ready before the worker is stopped.
26 pub fn stop(self) -> Receiver<O> { 33 pub fn stop(self) -> Receiver<O> {
27 self.out 34 self.out
28 } 35 }
@@ -32,30 +39,21 @@ impl<I, O> Worker<I, O> {
32 } 39 }
33} 40}
34 41
35pub struct ThreadWatcher { 42impl WorkerHandle {
36 name: &'static str, 43 fn spawn(name: &'static str, f: impl FnOnce() + Send + 'static) -> WorkerHandle {
37 thread: thread::JoinHandle<()>,
38 bomb: DropBomb,
39}
40
41impl ThreadWatcher {
42 fn spawn(name: &'static str, f: impl FnOnce() + Send + 'static) -> ThreadWatcher {
43 let thread = thread::spawn(f); 44 let thread = thread::spawn(f);
44 ThreadWatcher { 45 WorkerHandle {
45 name, 46 name,
46 thread, 47 thread,
47 bomb: DropBomb::new(format!("ThreadWatcher {} was not stopped", name)), 48 bomb: DropBomb::new(format!("WorkerHandle {} was not stopped", name)),
48 } 49 }
49 } 50 }
50 51
51 pub fn stop(mut self) -> Result<()> { 52 pub fn stop(mut self) -> thread::Result<()> {
52 log::info!("waiting for {} to finish ...", self.name); 53 log::info!("waiting for {} to finish ...", self.name);
53 let name = self.name; 54 let name = self.name;
54 self.bomb.defuse(); 55 self.bomb.defuse();
55 let res = self 56 let res = self.thread.join();
56 .thread
57 .join()
58 .map_err(|_| format_err!("ThreadWatcher {} died", name));
59 match &res { 57 match &res {
60 Ok(()) => log::info!("... {} terminated with ok", name), 58 Ok(()) => log::info!("... {} terminated with ok", name),
61 Err(_) => log::error!("... {} terminated with err", name), 59 Err(_) => log::error!("... {} terminated with err", name),