Skip to content

Commit

Permalink
fix: SharedPromise::join deadlock
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed Dec 9, 2024
1 parent 797d958 commit 15752bc
Show file tree
Hide file tree
Showing 8 changed files with 43 additions and 25 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ repository = "https://github.com/erg-lang/erg"
homepage = "https://erg-lang.org/"

[features]
default = ["parallel"]
# when "debug" feature is turned on, that of the following crates will also be turned on.
debug = ["erg_common/debug", "erg_parser/debug", "erg_compiler/debug", "erg_linter/debug"] # "els/debug"
backtrace = ["erg_common/backtrace", "els/backtrace"]
Expand Down Expand Up @@ -69,9 +70,8 @@ gal = ["erg_common/gal", "erg_compiler/gal"]
els = ["erg_common/els", "erg_compiler/els", "dep:els"]
full-repl = ["erg_common/full-repl"]
full = ["els", "full-repl", "unicode", "pretty"]
experimental = ["erg_common/experimental", "erg_parser/experimental", "erg_compiler/experimental", "erg_linter/experimental", "parallel"]
experimental = ["erg_common/experimental", "erg_parser/experimental", "erg_compiler/experimental", "erg_linter/experimental"]
log-level-error = ["erg_common/log-level-error", "erg_parser/log-level-error", "erg_compiler/log-level-error", "erg_linter/log-level-error"]
# The parallelizing compiler was found to contain a bug that caused it to hang in complex dependencies, so it is disabled by default.
parallel = ["erg_common/parallel", "erg_parser/parallel", "erg_compiler/parallel", "erg_linter/parallel"]

[workspace.dependencies]
Expand Down
3 changes: 2 additions & 1 deletion crates/erg_common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ repository.workspace = true
homepage.workspace = true

[features]
default = ["parallel"]
debug = ["dep:backtrace-on-stack-overflow", "dep:w-boson"]
backtrace = ["dep:backtrace-on-stack-overflow", "dep:w-boson"]
japanese = []
Expand All @@ -23,7 +24,7 @@ py_compat = []
gal = []
no_std = []
full-repl = ["dep:crossterm"]
experimental = ["parallel"]
experimental = []
pylib = ["dep:pyo3"]
log-level-error = []
parallel = []
Expand Down
3 changes: 2 additions & 1 deletion crates/erg_compiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ repository.workspace = true
homepage.workspace = true

[features]
default = ["parallel"]
# when "debug" feature is turned on, that of parser will also be turned on.
debug = ["erg_common/debug", "erg_parser/debug"]
japanese = ["erg_common/japanese", "erg_parser/japanese"]
Expand Down Expand Up @@ -39,7 +40,7 @@ gal = ["erg_common/gal"]
els = ["erg_common/els"]
no_std = ["erg_common/no_std"]
full-repl = ["erg_common/full-repl"]
experimental = ["erg_common/experimental", "erg_parser/experimental", "parallel"]
experimental = ["erg_common/experimental", "erg_parser/experimental"]
pylib = ["dep:pyo3", "erg_common/pylib", "erg_parser/pylib"]
pylib_compiler = ["pylib"]
log-level-error = ["erg_common/log-level-error", "erg_parser/log-level-error"]
Expand Down
46 changes: 30 additions & 16 deletions crates/erg_compiler/module/promise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ pub enum Promise {
parent: ThreadId,
handle: JoinHandle<()>,
},
Joining,
Joining {
id: ThreadId,
},
Joined,
}

Expand All @@ -27,7 +29,7 @@ impl fmt::Display for Promise {
Self::Running { handle, .. } => {
write!(f, "running on thread {:?}", handle.thread().id())
}
Self::Joining => write!(f, "joining"),
Self::Joining { id } => write!(f, "joining (thread {id:?})"),
Self::Joined => write!(f, "joined"),
}
}
Expand All @@ -44,36 +46,40 @@ impl Promise {
/// can be joined if `true`
pub fn is_finished(&self) -> bool {
match self {
Self::Joined => true,
Self::Joining => false,
Self::Joined { .. } => true,
Self::Joining { .. } => false,
Self::Running { handle, .. } => handle.is_finished(),
}
}

pub const fn is_joined(&self) -> bool {
matches!(self, Self::Joined)
matches!(self, Self::Joined { .. })
}

pub const fn is_joining(&self) -> bool {
matches!(self, Self::Joining)
matches!(self, Self::Joining { .. })
}

pub fn thread_id(&self) -> Option<ThreadId> {
match self {
Self::Joined | Self::Joining => None,
Self::Joined => None,
Self::Joining { id } => Some(*id),
Self::Running { handle, .. } => Some(handle.thread().id()),
}
}

pub fn parent_thread_id(&self) -> Option<ThreadId> {
match self {
Self::Joined | Self::Joining => None,
Self::Joined { .. } | Self::Joining { .. } => None,
Self::Running { parent, .. } => Some(*parent),
}
}

pub fn take(&mut self) -> Self {
std::mem::replace(self, Self::Joining)
let joining = Self::Joining {
id: self.thread_id().unwrap(),
};
std::mem::replace(self, joining)
}
}

Expand Down Expand Up @@ -197,7 +203,12 @@ impl SharedPromises {
return Ok(());
}
}
while let Some(Promise::Joining) | None = self.promises.borrow().get(path) {
// prevent deadlock
if self.thread_id(path).is_some_and(|id| id == current().id()) {
*self.promises.borrow_mut().get_mut(path).unwrap() = Promise::Joined;
return Ok(());
}
while let Some(Promise::Joining { .. }) | None = self.promises.borrow().get(path) {
safe_yield();
}
if self.is_joined(path) {
Expand All @@ -209,10 +220,6 @@ impl SharedPromises {
self.wait_until_finished(path);
return Ok(());
};
if handle.thread().id() == current().id() {
*self.promises.borrow_mut().get_mut(path).unwrap() = Promise::Joined;
return Ok(());
}
let res = handle.join();
*self.promises.borrow_mut().get_mut(path).unwrap() = Promise::Joined;
res
Expand All @@ -239,15 +246,22 @@ impl SharedPromises {
}
}

pub fn thread_id(&self, path: &NormalizedPathBuf) -> Option<ThreadId> {
self.promises
.borrow()
.get(path)
.and_then(|promise| promise.thread_id())
}

pub fn progress(&self) -> Progress {
let mut total = 0;
let mut running = 0;
let mut finished = 0;
for promise in self.promises.borrow().values() {
match promise {
Promise::Running { .. } => running += 1,
Promise::Joining => finished += 1,
Promise::Joined => finished += 1,
Promise::Joining { .. } => finished += 1,
Promise::Joined { .. } => finished += 1,
}
total += 1;
}
Expand Down
1 change: 1 addition & 0 deletions crates/erg_linter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ repository.workspace = true
homepage.workspace = true

[features]
default = ["parallel"]
debug = ["erg_common/debug", "erg_parser/debug", "erg_compiler/debug"]
backtrace = ["erg_common/backtrace"]
japanese = [
Expand Down
3 changes: 2 additions & 1 deletion crates/erg_parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ repository.workspace = true
homepage.workspace = true

[features]
default = ["parallel"]
debug = ["erg_common/debug"]
japanese = ["erg_common/japanese"]
simplified_chinese = ["erg_common/simplified_chinese"]
traditional_chinese = ["erg_common/traditional_chinese"]
unicode = ["erg_common/unicode"]
pretty = ["erg_common/pretty"]
large_thread = ["erg_common/large_thread"]
experimental = ["erg_common/experimental", "parallel"]
experimental = ["erg_common/experimental"]
pylib = ["dep:pyo3", "erg_common/pylib"]
pylib_parser = ["pylib"]
log-level-error = ["erg_common/log-level-error"]
Expand Down
4 changes: 2 additions & 2 deletions doc/EN/dev_guide/build_features.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ Enable Python-compatible mode, which makes parts of the APIs and syntax compatib

## experimental

Enable experimental features (contains `parallel`).
Enable experimental features.

## log-level-error

Only display error logs.

## parallel

Enable compiler parallelization. Unstable feature.
Enable compiler parallelization. Default is enabled.
4 changes: 2 additions & 2 deletions doc/JA/dev_guide/build_features.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ Python互換モードを有効にする。APIや文法の一部がPythonと互

## experimental

実験的な機能を有効にする。`parallel`も有効化される。
実験的な機能を有効にする。

## log-level-error

エラーログのみ表示する。

## parallel

コンパイラの並列化を有効にする。不安定機能
コンパイラの並列化を有効にする。デフォルトは有効

0 comments on commit 15752bc

Please sign in to comment.