mirror of
https://github.com/1Password/arboard.git
synced 2025-08-04 19:08:32 +00:00
Add debug helper for too-early Linux clipboard dropping
This commit is contained in:
parent
0c6d9195c8
commit
557d3199bc
1 changed files with 52 additions and 3 deletions
|
@ -178,8 +178,9 @@ impl XContext {
|
|||
#[derive(Default)]
|
||||
struct Selection {
|
||||
data: RwLock<Option<Vec<ClipboardData>>>,
|
||||
/// Mutex around nothing to use with the below condvar.
|
||||
mutex: Mutex<()>,
|
||||
/// Mutex around when this selection was last changed by us
|
||||
/// for both use with the below condvar and logging.
|
||||
mutex: Mutex<Option<Instant>>,
|
||||
/// A condvar that is notified when the contents of this clipboard are changed.
|
||||
///
|
||||
/// This is associated with `Self::mutex`.
|
||||
|
@ -266,6 +267,8 @@ impl Inner {
|
|||
// dropping the `data_guard` and calling `wait[_for]` and that we don't we wake other
|
||||
// threads in that position.
|
||||
let mut guard = selection.mutex.lock();
|
||||
// Record the time we modify the selection.
|
||||
*guard = Some(Instant::now());
|
||||
|
||||
// Notify any existing waiting threads that we have changed the data in the selection.
|
||||
// It is important that the mutex is locked to prevent this notification getting lost.
|
||||
|
@ -1086,7 +1089,10 @@ impl Drop for Clipboard {
|
|||
return;
|
||||
}
|
||||
if let Some(global_cb) = global_cb {
|
||||
if let Err(e) = global_cb.server_handle.join() {
|
||||
let GlobalClipboard { inner, server_handle } = global_cb;
|
||||
drop(inner);
|
||||
|
||||
if let Err(e) = server_handle.join() {
|
||||
// Let's try extracting the error message
|
||||
let message;
|
||||
if let Some(msg) = e.downcast_ref::<&'static str>() {
|
||||
|
@ -1105,6 +1111,49 @@ impl Drop for Clipboard {
|
|||
error!("The clipboard server thread panicked.");
|
||||
}
|
||||
}
|
||||
|
||||
// By this point we've dropped the Global's reference to `Inner` and the background
|
||||
// thread has exited which means it also dropped its reference. Therefore `self.inner` should
|
||||
// be the last strong count.
|
||||
//
|
||||
// Note: The following is all best effort and is only for logging. Nothing is guaranteed to execute
|
||||
// or log.
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(inner) = Arc::get_mut(&mut self.inner) {
|
||||
use std::io::IsTerminal;
|
||||
|
||||
let mut change_timestamps = Vec::with_capacity(2);
|
||||
let mut collect_changed = |sel: &mut Mutex<Option<Instant>>| {
|
||||
if let Some(changed) = sel.get_mut() {
|
||||
change_timestamps.push(*changed);
|
||||
}
|
||||
};
|
||||
|
||||
collect_changed(&mut inner.clipboard.mutex);
|
||||
collect_changed(&mut inner.primary.mutex);
|
||||
collect_changed(&mut inner.secondary.mutex);
|
||||
|
||||
change_timestamps.sort();
|
||||
if let Some(last) = change_timestamps.last() {
|
||||
let elapsed = last.elapsed().as_millis();
|
||||
// This number has no meaning, its just a guess for how long
|
||||
// might be reasonable to give a clipboard manager a chance to
|
||||
// save contents based ~roughly on the handoff timeout.
|
||||
if elapsed > 100 {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the app isn't running in a terminal don't print, use log instead.
|
||||
// Printing has a higher chance of being seen though, so its our default.
|
||||
// Its also close enough to a `debug_assert!` that it shouldn't come across strange.
|
||||
let msg = format!("Clipboard was dropped very quickly after writing ({elapsed}ms); clipboard managers may not have seen the contents\nConsider keeping `Clipboard` in more persistent state somewhere or keeping the contents alive longer using `SetLinuxExt` and/or threads.");
|
||||
if std::io::stderr().is_terminal() {
|
||||
eprintln!("{msg}");
|
||||
} else {
|
||||
log::warn!("{msg}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue