From eb02e2674217d3646155d65cb5353f983adb67ac Mon Sep 17 00:00:00 2001 From: Eirik Bakke Date: Mon, 12 Aug 2024 16:49:42 -0400 Subject: [PATCH 1/2] Get rid of some dead code in NbClipboard. The fields lastWindowActivated, lastWindowDeactivated, and lastWindowDeactivatedSource, and the related conditions were no longer in actual use; this can be seen by reading through the code. It seems the condition that once used lastWindowActivated was at some point replaced by a call to Task.waitFinished. So there wasn't a mistake; just some unused code left around. See the relevant commit from 2012: https://github.com/eirikbakke/netbeans-releases/commit/6de619c#diff-69d67e2e5b062ae995069c62294958e6e24a42c599cc6684c47f89b086c20278 --- .../src/org/netbeans/NbClipboard.java | 17 +++-------------- .../src/org/netbeans/NbClipboardNativeTest.java | 4 ++-- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/platform/o.n.bootstrap/src/org/netbeans/NbClipboard.java b/platform/o.n.bootstrap/src/org/netbeans/NbClipboard.java index 7d44bc5d88c1..1f9025e133f9 100644 --- a/platform/o.n.bootstrap/src/org/netbeans/NbClipboard.java +++ b/platform/o.n.bootstrap/src/org/netbeans/NbClipboard.java @@ -60,9 +60,6 @@ public final class NbClipboard extends ExClipboard private Lookup.Result result; final boolean slowSystemClipboard; private Transferable last; - private long lastWindowActivated; - private long lastWindowDeactivated; - private Reference lastWindowDeactivatedSource = new WeakReference<>(null); private volatile Task setContentsTask = Task.EMPTY; private volatile Task getContentsTask = Task.EMPTY; private boolean anyWindowIsActivated = true; @@ -279,14 +276,12 @@ final void waitFinished () { getContentsTask.waitFinished (); } - final void activateWindowHack (boolean reschedule) { + /** Used by tests only. */ + final void activateWindowHack () { // if WINDOW_DEACTIVATED is followed immediatelly with // WINDOW_ACTIVATED then it is JDK bug described in // issue 41098. - lastWindowActivated = System.currentTimeMillis(); - if (reschedule) { - scheduleGetFromSystemClipboard(true); - } + scheduleGetFromSystemClipboard(true); } private void logFlavors (Transferable trans, Level level, boolean content) { @@ -340,8 +335,6 @@ public void eventDispatched(AWTEvent ev) { return; if (ev.getID() == WindowEvent.WINDOW_DEACTIVATED) { - lastWindowDeactivated = System.currentTimeMillis(); - lastWindowDeactivatedSource = new WeakReference<>(ev.getSource()); anyWindowIsActivated = false; if( Utilities.isWindows() ) { //#247585 - even listening to clipboard changes when the window isn't active @@ -356,10 +349,6 @@ public void eventDispatched(AWTEvent ev) { fireChange(); } anyWindowIsActivated = true; - if (System.currentTimeMillis() - lastWindowDeactivated < 100 && - ev.getSource() == lastWindowDeactivatedSource.get()) { - activateWindowHack (false); - } if (log.isLoggable (Level.FINE)) { log.log (Level.FINE, "window activated scheduling update"); // NOI18N } diff --git a/platform/o.n.bootstrap/test/unit/src/org/netbeans/NbClipboardNativeTest.java b/platform/o.n.bootstrap/test/unit/src/org/netbeans/NbClipboardNativeTest.java index c7121e0679b3..1649d5ada250 100644 --- a/platform/o.n.bootstrap/test/unit/src/org/netbeans/NbClipboardNativeTest.java +++ b/platform/o.n.bootstrap/test/unit/src/org/netbeans/NbClipboardNativeTest.java @@ -146,7 +146,7 @@ public void testWhenCallingGetContentsItChecksSystemClipboardFirstTimeAfterActiv // just simulate initial switch to NetBeans main window - ec.activateWindowHack (true); + ec.activateWindowHack (); waitFinished (ec); if (listenerCalls == 0) { @@ -179,7 +179,7 @@ public void testWhenCallingGetContentsItChecksSystemClipboardFirstTimeAfterActiv if (slowClipboardHack ()) { assertEquals ("The getContents rechecked the clipboard just for the first time, not now, so the content is the same", "data2", s); - ec.activateWindowHack (true); + ec.activateWindowHack (); Thread.sleep (200); t = this.ec.getContents(this); From 1910c514e3af17eb34175079df6e4de954082e0a Mon Sep 17 00:00:00 2001 From: Eirik Bakke Date: Sun, 7 Jan 2024 00:28:48 +0100 Subject: [PATCH 2/2] Adjust the clipboard retry logic to use exponential backoff. It was creating a lot of logging messages, and clipboard access is a heavy operation that can sometimes interfere with other applications. --- .../o.n.bootstrap/src/org/netbeans/NbClipboard.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/platform/o.n.bootstrap/src/org/netbeans/NbClipboard.java b/platform/o.n.bootstrap/src/org/netbeans/NbClipboard.java index 1f9025e133f9..49d8c728fdb0 100644 --- a/platform/o.n.bootstrap/src/org/netbeans/NbClipboard.java +++ b/platform/o.n.bootstrap/src/org/netbeans/NbClipboard.java @@ -424,20 +424,25 @@ public void run() { // that is used because accessing the clipboard can block // indefinitely. Running the access loop here is deemed similar // in nature. - final int MAX_TRIES = 50; + + /* The loop will actually stop before getting to 10 iterations, per the delay + formula and conditional throw. But keep the MAX_TRIES just as a fail-safe. */ + final int MAX_TRIES = 10; final long start = System.currentTimeMillis(); + int delay = 20; for (int i = 0; i < MAX_TRIES; i++) { try { transferable = systemClipboard.getContents(this); break; } catch (IllegalStateException ex) { // Throw exception if retries failed - if (i == (MAX_TRIES - 1) || (System.currentTimeMillis() - start) > 980L) { + if (i == (MAX_TRIES - 1) || (System.currentTimeMillis() + delay - start) > 1000L) { throw ex; } else { - log.log(Level.INFO, "systemClipboard#getContents threw IllegalStateException (try: {0})", i + 1); // NOI18N + log.log(Level.INFO, "systemClipboard#getContents ISE, attempt {0}", i + 1); // NOI18N } - Thread.sleep(20); // Give system time to settle + Thread.sleep(delay); // Give system time to settle + delay *= 2; } } superSetContents(transferable, null);