diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java index fcfef02be4e..ac76ddc997e 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java @@ -5231,13 +5231,19 @@ public boolean sleep () { try { addPool(); allowTimers = runAsyncMessages = false; - NSRunLoop.currentRunLoop().runMode(OS.NSDefaultRunLoopMode, NSDate.distantFuture()); + /* + * Use a timeout-based approach to allow checking for thread interruption. + * Sleep for a maximum of 50 milliseconds at a time, similar to GTK. + */ + do { + NSRunLoop.currentRunLoop().runMode(OS.NSDefaultRunLoopMode, NSDate.dateWithTimeIntervalSinceNow(0.05)); + } while (synchronizer.isMessagesEmpty() && !thread.isInterrupted()); allowTimers = runAsyncMessages = true; } finally { removePool(); } sendPostExternalEventDispatchEvent (); - return true; + return !thread.isInterrupted(); } int sourceProc (int info) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java index 93972e94530..97a43c34aed 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java @@ -5633,11 +5633,11 @@ public boolean sleep () { OS.g_main_context_check (context, max_priority [0], fds, nfds); OS.g_main_context_release (context); } - } while (!result && synchronizer.isMessagesEmpty() && !wake); + } while (!result && synchronizer.isMessagesEmpty() && !wake && !thread.isInterrupted()); wake = false; if (!GTK.GTK4) GDK.gdk_threads_enter (); sendPostExternalEventDispatchEvent (); - return true; + return !thread.isInterrupted(); } /** diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Display.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Display.java index 01ab47eb7cc..a459d1dfce8 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Display.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Display.java @@ -1407,6 +1407,47 @@ public void run() { } } +@Test +@DisabledOnOs(value = org.junit.jupiter.api.condition.OS.WINDOWS, disabledReason = "Windows uses WaitMessage() which does not support timeout-based interruption checking") +public void test_sleep_respondToInterrupt() { + final Display display = new Display(); + Shell shell = new Shell(display); + try { + shell.open(); + Thread uiThread = Thread.currentThread(); + + // Start a thread that will interrupt the UI thread after 1 second + Thread interrupter = new Thread(() -> { + try { + Thread.sleep(1000); + uiThread.interrupt(); + } catch (InterruptedException e) { + // Ignore + } + }); + interrupter.start(); + + // Event loop with timeout to prevent hanging if sleep() never responds to interrupt + long deadline = System.currentTimeMillis() + 5000; + while (!shell.isDisposed() && System.currentTimeMillis() < deadline) { + if (!display.readAndDispatch()) { + boolean hasMoreWork = display.sleep(); + // When interrupted, sleep() returns false and interrupt flag is preserved + if (!hasMoreWork && uiThread.isInterrupted()) { + // Success! Clean up and return + Thread.interrupted(); // clear flag + return; + } + } + } + // If we get here, the test failed + fail("sleep() did not respond to thread interruption within timeout"); + } finally { + shell.dispose(); + display.dispose(); + } +} + @Test public void test_syncExecLjava_lang_Runnable() { final Display display = new Display();