mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-03 16:41:26 +01:00
fix(fuse): a stop wakes every drain worker (B180)
This commit is contained in:
1 parent
4e0fd3c0b1
commit
903452a15b
4 files changed
+68
-13
No files matched your search
@@ -703,12 +703,11 @@ impl Core {
|
||||
/// Block until there is plausibly something to do: a new op, a reconnect, or
|
||||
/// the shortest outstanding backoff elapsing.
|
||||
pub(crate) fn wait_for_drain_work(&self) {
|
||||
let (lock, cv) = &*self.drain_wake;
|
||||
let mut woken = lock.lock();
|
||||
if !*woken {
|
||||
cv.wait_for(&mut woken, DRAIN_IDLE_POLL);
|
||||
}
|
||||
*woken = false;
|
||||
sleep_for_drain_work(
|
||||
&self.drain_wake,
|
||||
|| self.shutdown.is_stopping(),
|
||||
DRAIN_IDLE_POLL,
|
||||
);
|
||||
}
|
||||
|
||||
/// Like [`wait_for_drain_work`](Self::wait_for_drain_work), but first
|
||||
@@ -722,12 +721,7 @@ impl Core {
|
||||
}
|
||||
_ => DRAIN_IDLE_POLL,
|
||||
};
|
||||
let (lock, cv) = &*self.drain_wake;
|
||||
let mut woken = lock.lock();
|
||||
if !*woken {
|
||||
cv.wait_for(&mut woken, timeout);
|
||||
}
|
||||
*woken = false;
|
||||
sleep_for_drain_work(&self.drain_wake, || self.shutdown.is_stopping(), timeout);
|
||||
}
|
||||
|
||||
/// Perform one queued op and retire it.
|
||||
@@ -2697,6 +2691,27 @@ fn keeps_landed_upload(pinned: bool, len: u64, budget: u64) -> bool {
|
||||
}
|
||||
|
||||
/// Where `st` has `uid`: its name and parent.
|
||||
/// Sleep on the drain's `wake` for up to `timeout`, unless a wake-up came
|
||||
/// since a worker last took one, or the mount is `stopping`.
|
||||
///
|
||||
/// One wake-up wakes every worker, and the first to wake takes it. A worker
|
||||
/// that came to sleep after that slept out its timeout, the stop's included,
|
||||
/// and the stop gave up on it after 10 s (`docs/BUGS.md` B180). The stop sets
|
||||
/// its flag before it takes the lock, so a worker that finds it unset under
|
||||
/// the lock is asleep when the wake-up comes.
|
||||
fn sleep_for_drain_work(
|
||||
wake: &(Mutex<bool>, parking_lot::Condvar),
|
||||
stopping: impl Fn() -> bool,
|
||||
timeout: Duration,
|
||||
) {
|
||||
let (lock, cv) = wake;
|
||||
let mut woken = lock.lock();
|
||||
if !*woken && !stopping() {
|
||||
cv.wait_for(&mut woken, timeout);
|
||||
}
|
||||
*woken = false;
|
||||
}
|
||||
|
||||
fn placed_in(st: &crate::state::State, uid: &NodeUid) -> Option<(String, Option<NodeUid>)> {
|
||||
let entry = st.by_uid.get(uid).and_then(|ino| st.entries.get(ino))?;
|
||||
Some((entry.node.name.clone(), entry.node.parent_uid.clone()))
|
||||
@@ -2712,6 +2727,16 @@ mod tests {
|
||||
use proton_drive_rs::proton_sdk::ids::{LinkId, VolumeId};
|
||||
use std::cell::Cell;
|
||||
|
||||
#[test]
|
||||
fn a_drain_worker_that_missed_the_stop_wake_up_does_not_sleep() {
|
||||
// Another worker took the stop's wake-up, and this one slept out its
|
||||
// timeout, past the stop's deadline (B180).
|
||||
let wake = (Mutex::new(false), parking_lot::Condvar::new());
|
||||
let started = Instant::now();
|
||||
sleep_for_drain_work(&wake, || true, Duration::from_secs(5));
|
||||
assert!(started.elapsed() < Duration::from_secs(1));
|
||||
}
|
||||
|
||||
fn pending(kind: &str) -> PendingOp {
|
||||
PendingOp {
|
||||
id: 0,
|
||||
|
||||
@@ -12,6 +12,32 @@ Conventions:
|
||||
|
||||
---
|
||||
|
||||
## B180 — A stop waits 10 s for a drain worker that slept through it
|
||||
|
||||
**Status:** Fixed.
|
||||
**Found:** 2026-10-04, by the simulation suite on 0f3c06f: `a_stop_ends_a_mirror_pass_after_the_uploads_it_is_sending`
|
||||
found "background workers did not finish in time" in the log, left there by two earlier tests
|
||||
whose stops had given up on a drain worker. On its own,
|
||||
`a_folder_made_again_while_the_old_one_is_trashed_is_a_new_folder` took 11 s instead of 2 s in
|
||||
about one run in three.
|
||||
|
||||
**Where:** `Core::wait_for_drain_work` and `Core::wait_for_drain_work_or_due` in
|
||||
`crates/pdfs-fuse/src/drain.rs`.
|
||||
|
||||
**Cause.** The drain workers sleep on one flag and one condvar. A stop sets its flag, then
|
||||
raises the wake-up and wakes them all, and the first worker to wake takes the wake-up. A worker
|
||||
that was between its stop check and its sleep at that moment found no wake-up and slept its
|
||||
timeout, up to 30 s. The stop gave up on it after 10 s and shut down around it. With 16 workers
|
||||
since step 7 this was common.
|
||||
|
||||
**Fix.** A worker checks the stop under the wake-up's lock before it sleeps. The stop sets its
|
||||
flag before it takes that lock, so a worker that finds it unset is asleep when the wake-up comes.
|
||||
|
||||
**Test:** `a_drain_worker_that_missed_the_stop_wake_up_does_not_sleep` in
|
||||
`crates/pdfs-fuse/src/drain.rs`.
|
||||
|
||||
---
|
||||
|
||||
## B179 — A file read just as its create lands reads empty
|
||||
|
||||
**Status:** Fixed (unverified).
|
||||
|
||||
@@ -84,6 +84,8 @@ by its local id, in the queue, the stored tree and pins alike.
|
||||
- **Deleting a folder while sync is paused no longer leaves a queued change behind** for a
|
||||
file in it that was never uploaded (B178).
|
||||
- **A file read just as it reaches Drive no longer reads empty** (B179).
|
||||
- **Stopping the daemon no longer takes 10 seconds more than it needs** when an idle upload
|
||||
worker missed the stop (B180).
|
||||
- **A file renamed just as its upload finished keeps its new name.** The mount no longer showed
|
||||
the old name, and a second rename no longer failed with "No such file or directory" (B152,
|
||||
B172).
|
||||
|
||||
@@ -9,7 +9,7 @@ Tracks `docs/MILESTONE-3.0.0.md`. Update it with every commit that moves a phase
|
||||
- **Phase 2**: local ids, inode = local id, one tree for every mount (2.10.0).
|
||||
- **Phase 3, first part**: mkdir, create, rename, unlink and rmdir on the mount are recorded
|
||||
locally and sent from the queue; `"local_first": false` brings back the old path (5e0f30c).
|
||||
- Fixes from the simulation and account runs since then: B127 and B141 to B179.
|
||||
- Fixes from the simulation and account runs since then: B127 and B141 to B180.
|
||||
- **Sync issues, first cut** (§6.2): refusals sorted into issues, shown at once in the app,
|
||||
tray and CLI; Export button and `pdfs sync export`; `pdfs sync issues`.
|
||||
|
||||
@@ -125,6 +125,8 @@ Tracks `docs/MILESTONE-3.0.0.md`. Update it with every commit that moves a phase
|
||||
its stand-in and answered `EIO` (B177, fixed). A folder trashed while sync was
|
||||
paused kept a create below it as a trash: a paused drain worker had claimed it
|
||||
(B178, fixed). A file read as its create landed read empty (B179, fixed).
|
||||
The suite on 0f3c06f: a stop gave up on a drain worker that slept through its
|
||||
wake-up (B180, fixed).
|
||||
5. **Release**: 2.10.0 is tagged; the 2.9.0 tag still waits for the user's go-ahead.
|
||||
|
||||
## After 3.0.0
|
||||
|
||||
Reference in new issue
Block a user