From 7b1651a2c14cffb0de93265ebaad7f995f8e7741 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Wed, 20 Nov 2019 19:19:28 +0300 Subject: [PATCH] Dynamically change thread sleeping time to increase throughput during high load Previously we were always blocking for 50ms no matter what, but now we'll be dynamically adjust our block ratio depending on the current clipboard load. In the worst case scenario when user used the clipboard only once will go with the next sequence: 0 1(16 times) 1 2 4 8 16 32 50 50 50 and so on, until the next user request. --- CHANGELOG.md | 1 + src/threaded.rs | 41 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7de2710..a2e892b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased - Perform loaded data normalization for text/plain;charset=utf-8 mime type +- Fix clipboard throttling ## 0.3.5 -- 2019-09-3 diff --git a/src/threaded.rs b/src/threaded.rs index ea5d3c3..cf17306 100644 --- a/src/threaded.rs +++ b/src/threaded.rs @@ -4,7 +4,7 @@ use std::ops::Deref; use std::os::unix::io::FromRawFd; use std::sync::mpsc; use std::sync::{Arc, Mutex}; -use std::thread::sleep; +use std::thread; use std::time::Duration; use nix::fcntl::OFlag; @@ -275,9 +275,18 @@ fn clipboard_thread( }); event_queue.sync_roundtrip().unwrap(); + // We should provide lower sleep amounts in a moments of spaming our clipboard + let mut sleep_amount = 50; + // Provide our clipboard a warm start, so 16 initial cycles will be at 1ms and other will go + // like 1 2 4 8 16 32 50 50 and so on + let mut warm_start_amount = 0; + // Thread loop to handle requests and dispatch the event queue loop { if let Ok(request) = request_recv.try_recv() { + // Lower sleep amount to zero, so the next recv will be instant + sleep_amount = 0; + match request { // Load text from clipboard ThreadRequest::Load(seat_name) => { @@ -480,9 +489,33 @@ fn clipboard_thread( ThreadRequest::Kill => break, } } - // Dispatch the event queue and block for 50 milliseconds - event_queue.dispatch_pending().unwrap(); - sleep(Duration::from_millis(50)); + // Dispatch the event queue and block for `sleep_amount` + let pending_events = event_queue.dispatch_pending().unwrap(); + let num_seats = seat_map.lock().unwrap().len(); + + // If some app is trying to spam us when there no seats, it's likely that someone is + // trying to paste from us + if num_seats == 0 && pending_events != 0 { + sleep_amount = 0; + } else if sleep_amount > 0 { + thread::sleep(Duration::from_millis(sleep_amount)); + + if warm_start_amount < 16 { + warm_start_amount += 1; + if warm_start_amount == 16 { + sleep_amount = 1; + } + } else if sleep_amount < 50 { + // The aim of this different sleep times is to provide a good performance under + // high load and not waste system resources too much when idle + sleep_amount = std::cmp::min(2 * sleep_amount, 50); + } + } else if sleep_amount == 0 { + // Reset sleep amount from zero back to one, so sleep sequence could reach 50 + sleep_amount = 1; + // Reset warm start to accelerate the initial clipboard requests + warm_start_amount = 0; + } } }