diff options
author | Andrey Loskutov | 2019-04-30 15:26:56 +0000 |
---|---|---|
committer | Andrey Loskutov | 2019-05-02 13:34:25 +0000 |
commit | ead7de4749a856cec4c830d7bd331961cc5dad27 (patch) | |
tree | 6bbebf330176a611232d1ca03094cedb3b6eb0a5 | |
parent | 068e046d670c32868130280243b61cfdad2440a1 (diff) | |
download | eclipse.platform.text-ead7de4749a856cec4c830d7bd331961cc5dad27.tar.gz eclipse.platform.text-ead7de4749a856cec4c830d7bd331961cc5dad27.tar.xz eclipse.platform.text-ead7de4749a856cec4c830d7bd331961cc5dad27.zip |
Bug 546828 - Editors based on FileDocumentProvider /
TextFileDocumentProvider lock UI on attempt to edit while build is
running
The change modifies FileDocumentProvider.getValidateStateRule() and
DocumentProviderOperation created in
TextFileDocumentProvider.validateState() to
return non-null rule (referenced IFile) for IFileEditorInput's if the
IResourceRuleFactory.validateEditRule() has no rule for us.
This prevents UI freeze on attempt to modify a file while build is
running. Instead of a freeze, we get a progress dialog with a
possibility to cancel the (blocking) attempt to modify the file.
Change-Id: I23a6ddab2ca8f31b5ba134301356c34250f5087c
Signed-off-by: Andrey Loskutov <loskutov@gmx.de>
9 files changed, 406 insertions, 11 deletions
diff --git a/org.eclipse.ui.editors.tests/META-INF/MANIFEST.MF b/org.eclipse.ui.editors.tests/META-INF/MANIFEST.MF index 110f4b4b036..c24a6dc9af6 100644 --- a/org.eclipse.ui.editors.tests/META-INF/MANIFEST.MF +++ b/org.eclipse.ui.editors.tests/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-SymbolicName: org.eclipse.ui.editors.tests;singleton:=true -Bundle-Version: 3.11.300.qualifier +Bundle-Version: 3.11.400.qualifier Bundle-Vendor: %Plugin.providerName Bundle-Localization: plugin Export-Package: org.eclipse.ui.editors.tests diff --git a/org.eclipse.ui.editors.tests/pom.xml b/org.eclipse.ui.editors.tests/pom.xml index c08e4931569..81b8a7a5c5c 100644 --- a/org.eclipse.ui.editors.tests/pom.xml +++ b/org.eclipse.ui.editors.tests/pom.xml @@ -19,7 +19,7 @@ </parent> <groupId>org.eclipse.ui</groupId> <artifactId>org.eclipse.ui.editors.tests</artifactId> - <version>3.11.300-SNAPSHOT</version> + <version>3.11.400-SNAPSHOT</version> <packaging>eclipse-test-plugin</packaging> <properties> <testSuite>${project.artifactId}</testSuite> diff --git a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/EditorsTestSuite.java b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/EditorsTestSuite.java index 98f79171182..adb8c5598f7 100644 --- a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/EditorsTestSuite.java +++ b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/EditorsTestSuite.java @@ -33,6 +33,7 @@ import org.junit.runners.Suite.SuiteClasses; MarkerAnnotationOrderTest.class, ZoomTest.class, FileDocumentProviderTest.class, + TextFileDocumentProviderTest.class, StatusEditorTest.class }) public class EditorsTestSuite { diff --git a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/FileDocumentProviderTest.java b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/FileDocumentProviderTest.java index 9538e5c5578..91531f44a9e 100644 --- a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/FileDocumentProviderTest.java +++ b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/FileDocumentProviderTest.java @@ -30,6 +30,7 @@ import org.eclipse.swt.widgets.Display; import org.eclipse.core.internal.localstore.FileSystemResourceManager; import org.eclipse.core.internal.resources.File; +import org.eclipse.core.internal.resources.Workspace; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.ILog; @@ -56,9 +57,12 @@ import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor; +import org.eclipse.ui.intro.IIntroManager; +import org.eclipse.ui.intro.IIntroPart; import org.eclipse.ui.editors.text.FileDocumentProvider; @@ -74,6 +78,7 @@ public class FileDocumentProviderTest { private AtomicBoolean stoppedByTest; private AtomicBoolean stopLockingFlag; private LockJob lockJob; + private LockJob2 lockJob2; private FileDocumentProviderMock fileProvider; private FileSystemResourceManager fsManager; private IEditorPart editor; @@ -81,6 +86,7 @@ public class FileDocumentProviderTest { @Before public void setUp() throws Exception { + closeIntro(PlatformUI.getWorkbench()); IFolder folder = ResourceHelper.createFolder("FileDocumentProviderTestProject/test"); file = (File) ResourceHelper.createFile(folder, "file.txt", ""); assertTrue(file.exists()); @@ -90,6 +96,7 @@ public class FileDocumentProviderTest { stoppedByTest = new AtomicBoolean(false); fileProvider = new FileDocumentProviderMock(); lockJob = new LockJob("Locking workspace", file, stopLockingFlag, stoppedByTest); + lockJob2 = new LockJob2("Locking workspace", file, stopLockingFlag, stoppedByTest); // We need the editor only to get the default editor status line manager IWorkbench workbench = PlatformUI.getWorkbench(); @@ -126,6 +133,7 @@ public class FileDocumentProviderTest { public void tearDown() throws Exception { stopLockingFlag.set(true); lockJob.cancel(); + lockJob2.cancel(); if (editor != null) { page.closeEditor(editor, false); } @@ -199,6 +207,34 @@ public class FileDocumentProviderTest { assertTrue(stopLockingFlag.get()); } + @Test + public void testValidateStateForFileWhileWorkspaceIsLocked() throws Exception { + assertNotNull("Test must run in UI thread", Display.getCurrent()); + + // Start workspace job which will lock workspace operations on file + lockJob2.schedule(); + + Thread.sleep(100); + + // Put an UI event in the queue which will stop the workspace lock job + // after a delay + Display.getCurrent().timerExec(600, new Runnable() { + @Override + public void run() { + stopLockingFlag.set(true); + System.out.println("UI event dispatched, lock removed"); + } + }); + + // Original code will lock UI thread here because it will try to acquire + // workspace lock and no one will process UI events anymore + fileProvider.validateState(editor.getEditorInput(), editor.getSite().getShell()); + + System.out.println("Busy wait terminated, UI thread is operable again!"); + assertFalse("Test deadlocked while waiting on resource lock", stoppedByTest.get()); + assertTrue(stopLockingFlag.get()); + } + /* * Set current time stamp via java.nio to make sure * org.eclipse.core.internal.resources.File.refreshLocal(int, @@ -227,6 +263,17 @@ public class FileDocumentProviderTest { ILog log = Platform.getLog(Platform.getBundle(PLUGIN_ID)); log.log(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, message, ex)); } + + static void closeIntro(final IWorkbench wb) { + IWorkbenchWindow window = wb.getActiveWorkbenchWindow(); + if (window != null) { + IIntroManager im = wb.getIntroManager(); + IIntroPart intro = im.getIntro(); + if (intro != null) { + im.closeIntro(intro); + } + } + } } class FileDocumentProviderMock extends FileDocumentProvider { @@ -253,9 +300,9 @@ class FileDocumentProviderMock extends FileDocumentProvider { /** Emulates what SVN plugin jobs are doing */ class LockJob extends WorkspaceJob { - private final IResource resource; - private AtomicBoolean stopFlag; - private AtomicBoolean stoppedByTest; + final IResource resource; + AtomicBoolean stopFlag; + AtomicBoolean stoppedByTest; public LockJob(String name, IResource resource, AtomicBoolean stopFlag, AtomicBoolean stoppedByTest) { super(name); @@ -266,7 +313,7 @@ class LockJob extends WorkspaceJob { this.resource = resource; } - public IStatus run2(IProgressMonitor monitor) { + public IStatus run2() { long startTime = System.currentTimeMillis(); // Wait maximum 5 minutes int maxWaitTime = 5 * 60 * 1000; @@ -312,7 +359,7 @@ class LockJob extends WorkspaceJob { @Override public void run(IProgressMonitor pm) throws CoreException { try { - run2(pm); + run2(); } catch (Exception e) { // Re-throw as OperationCanceledException, which will be // caught and re-thrown as InterruptedException below. @@ -328,3 +375,75 @@ class LockJob extends WorkspaceJob { } } + +/** Emulates what AutoBuildJob is doing */ +class LockJob2 extends Job { + + final IResource resource; + AtomicBoolean stopFlag; + AtomicBoolean stoppedByTest; + + public LockJob2(String name, IResource resource, AtomicBoolean stopFlag, AtomicBoolean stoppedByTest) { + super(name); + this.stopFlag = stopFlag; + this.stoppedByTest = stoppedByTest; + setUser(true); + setSystem(true); + this.resource = resource; + setRule(ResourcesPlugin.getWorkspace().getRoot()); + } + + @Override + public boolean belongsTo(Object family) { + return super.belongsTo(family) || LockJob2.class == family; + } + + @Override + public IStatus run(IProgressMonitor monitor) { + Workspace workspace = (Workspace) ResourcesPlugin.getWorkspace(); + try { + workspace.prepareOperation(getRule(), monitor); + workspace.beginOperation(true); + run2(); + } catch (CoreException e) { + FileDocumentProviderTest.logError(e.getMessage(), e); + } finally { + try { + workspace.endOperation(getRule(), false); + } catch (CoreException e) { + FileDocumentProviderTest.logError(e.getMessage(), e); + } + } + return monitor.isCanceled() ? Status.CANCEL_STATUS : Status.OK_STATUS; + } + + public IStatus run2() { + long startTime = System.currentTimeMillis(); + // Wait maximum 5 minutes + int maxWaitTime = 5 * 60 * 1000; + long stopTime = startTime + maxWaitTime; + + System.out.println("Starting the busy wait while holding lock on: " + resource.getFullPath()); + try { + while (!stopFlag.get()) { + try { + if (System.currentTimeMillis() > stopTime) { + FileDocumentProviderTest.logError("Tiemout occured while waiting on test to finish", + new IllegalStateException()); + stoppedByTest.set(true); + return Status.CANCEL_STATUS; + } + Thread.sleep(100); + } catch (InterruptedException e) { + stoppedByTest.set(true); + FileDocumentProviderTest.logError("Lock job was interrupted while waiting on test to finish", e); + e.printStackTrace(); + return Status.CANCEL_STATUS; + } + } + } finally { + System.out.println("Lock task terminated"); + } + return Status.OK_STATUS; + } +} diff --git a/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/TextFileDocumentProviderTest.java b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/TextFileDocumentProviderTest.java new file mode 100644 index 00000000000..dde68474e1b --- /dev/null +++ b/org.eclipse.ui.editors.tests/src/org/eclipse/ui/editors/tests/TextFileDocumentProviderTest.java @@ -0,0 +1,251 @@ +/******************************************************************************* + * Copyright (c) 2016 Andrey Loskutov. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Andrey Loskutov <loskutov@gmx.de> - initial API and implementation + *******************************************************************************/ +package org.eclipse.ui.editors.tests; + +import static org.eclipse.ui.editors.tests.FileDocumentProviderTest.closeIntro; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.nio.file.Files; +import java.nio.file.attribute.FileTime; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.eclipse.swt.widgets.Display; + +import org.eclipse.core.internal.localstore.FileSystemResourceManager; +import org.eclipse.core.internal.resources.File; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IProgressMonitorWithBlocking; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.jobs.Job; + +import org.eclipse.core.resources.IFolder; + +import org.eclipse.core.filebuffers.tests.ResourceHelper; + +import org.eclipse.jface.action.IStatusLineManager; + +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.ide.IDE; +import org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor; + +import org.eclipse.ui.editors.text.TextFileDocumentProvider; + +/** + * Test checking UI deadlock on modifying the TextFileDocumentProvider's + * underlined file. + */ +public class TextFileDocumentProviderTest { + + private File file; + private AtomicBoolean stoppedByTest; + private AtomicBoolean stopLockingFlag; + private LockJob lockJob; + private LockJob2 lockJob2; + private TextFileDocumentProvider fileProvider; + private FileSystemResourceManager fsManager; + private IEditorPart editor; + private IWorkbenchPage page; + + @Before + public void setUp() throws Exception { + closeIntro(PlatformUI.getWorkbench()); + IFolder folder = ResourceHelper.createFolder("FileDocumentProviderTestProject/test"); + file = (File) ResourceHelper.createFile(folder, "file.txt", ""); + assertTrue(file.exists()); + fsManager = file.getLocalManager(); + assertTrue(fsManager.fastIsSynchronized(file)); + stopLockingFlag = new AtomicBoolean(false); + stoppedByTest = new AtomicBoolean(false); + fileProvider = new TextFileDocumentProvider(); + lockJob = new LockJob("Locking workspace", file, stopLockingFlag, stoppedByTest); + lockJob2 = new LockJob2("Locking workspace", file, stopLockingFlag, stoppedByTest); + + // We need the editor only to get the default editor status line manager + IWorkbench workbench = PlatformUI.getWorkbench(); + page = workbench.getActiveWorkbenchWindow().getActivePage(); + editor = IDE.openEditor(page, file); + TestUtil.runEventLoop(); + + IStatusLineManager statusLineManager = editor.getEditorSite().getActionBars().getStatusLineManager(); + // This is default monitor which almost all editors are using + IProgressMonitor progressMonitor = statusLineManager.getProgressMonitor(); + assertNotNull(progressMonitor); + assertFalse(progressMonitor instanceof NullProgressMonitor); + assertFalse(progressMonitor instanceof EventLoopProgressMonitor); + assertTrue(progressMonitor instanceof IProgressMonitorWithBlocking); + + // Because this monitor is not EventLoopProgressMonitor, it will not + // process UI events while waiting on workspace lock + fileProvider.setProgressMonitor(progressMonitor); + + TestUtil.waitForJobs(500, 5000); + Job[] jobs = Job.getJobManager().find(null); + String jobsList = Arrays.toString(jobs); + System.out.println("Still running jobs: " + jobsList); + if (!Job.getJobManager().isIdle()) { + jobs = Job.getJobManager().find(null); + for (Job job : jobs) { + System.out.println("Going to cancel: " + job.getName() + " / " + job); + job.cancel(); + } + } + } + + @After + public void tearDown() throws Exception { + stopLockingFlag.set(true); + lockJob.cancel(); + lockJob2.cancel(); + if (editor != null) { + page.closeEditor(editor, false); + } + ResourceHelper.deleteProject(file.getProject().getName()); + TestUtil.runEventLoop(); + TestUtil.cleanUp(); + } + + @Test + public void testSynchronizeInputWhileWorkspaceIsLocked1() throws Exception { + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=482354 + assertNotNull("Test must run in UI thread", Display.getCurrent()); + fileProvider.connect(editor.getEditorInput()); + + // Start workspace job which will lock workspace operations on file via + // rule + lockJob.schedule(); + + // touch the file of the editor + makeSureResourceIsOutOfDate(); + + // Put an UI event in the queue which will stop the workspace lock job + // after a delay so that we can verify the UI events are still + // dispatched after the call to refreshFile() below + Display.getCurrent().timerExec(500, new Runnable() { + @Override + public void run() { + stopLockingFlag.set(true); + System.out.println("UI event dispatched, lock removed"); + } + }); + + // Original code will lock UI thread here because it will try to acquire + // resource lock and no one will process UI events anymore + fileProvider.synchronize(editor.getEditorInput()); + + System.out.println("Busy wait terminated, UI thread is operable again!"); + assertFalse("Test deadlocked while waiting on resource lock", stoppedByTest.get()); + assertTrue(stopLockingFlag.get()); + } + + @Test + public void testSynchronizeInputWhileWorkspaceIsLocked2() throws Exception { + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=482354 + assertNotNull("Test must run in UI thread", Display.getCurrent()); + + fileProvider.connect(editor.getEditorInput()); + + // Start workspace job which will lock workspace operations on file via + // rule + lockJob.schedule(); + + // touch the file of the editor + makeSureResourceIsOutOfDate(); + + // Put an UI event in the queue which will stop the workspace lock job + // after a delay so that we can verify the UI events are still + // dispatched after the call to refreshFile() below + Display.getCurrent().timerExec(500, new Runnable() { + @Override + public void run() { + stopLockingFlag.set(true); + System.out.println("UI event dispatched, lock removed"); + } + }); + + // Original code will lock UI thread here because it will try to acquire + // resource lock and no one will process UI events anymore + fileProvider.synchronize(editor.getEditorInput()); + + System.out.println("Busy wait terminated, UI thread is operable again!"); + assertFalse("Test deadlocked while waiting on resource lock", stoppedByTest.get()); + assertTrue(stopLockingFlag.get()); + } + + @Test + public void testValidateStateForFileWhileWorkspaceIsLocked() throws Exception { + assertNotNull("Test must run in UI thread", Display.getCurrent()); + + fileProvider.connect(editor.getEditorInput()); + + // Start workspace job which will lock workspace operations on file + lockJob2.schedule(); + + Thread.sleep(100); + + // Put an UI event in the queue which will stop the workspace lock job + // after a delay + Display.getCurrent().timerExec(600, new Runnable() { + @Override + public void run() { + stopLockingFlag.set(true); + System.out.println("UI event dispatched, lock removed"); + } + }); + + // Original code will lock UI thread here because it will try to acquire + // workspace lock and no one will process UI events anymore + fileProvider.validateState(editor.getEditorInput(), editor.getSite().getShell()); + + System.out.println("Busy wait terminated, UI thread is operable again!"); + assertFalse("Test deadlocked while waiting on resource lock", stoppedByTest.get()); + assertTrue(stopLockingFlag.get()); + } + + /* + * Set current time stamp via java.nio to make sure + * org.eclipse.core.internal.resources.File.refreshLocal(int, + * IProgressMonitor) will call super.refreshLocal(IResource.DEPTH_ZERO, + * monitor) and so lock the UI by trying to access resource locked by the + * job + */ + private void makeSureResourceIsOutOfDate() throws Exception { + int count = 0; + Files.setLastModifiedTime(file.getLocation().toFile().toPath(), + FileTime.fromMillis(System.currentTimeMillis())); + // Give the file system a chance to have a *different* timestamp + Thread.sleep(100); + while (fsManager.fastIsSynchronized(file) && count < 1000) { + Files.setLastModifiedTime(file.getLocation().toFile().toPath(), + FileTime.fromMillis(System.currentTimeMillis())); + Thread.sleep(10); + count++; + } + System.out.println("Managed to update file after " + count + " attempts"); + assertFalse(fsManager.fastIsSynchronized(file)); + } + +} + + diff --git a/org.eclipse.ui.editors/META-INF/MANIFEST.MF b/org.eclipse.ui.editors/META-INF/MANIFEST.MF index e9aeb5d8dd9..46b6cfdc3f4 100644 --- a/org.eclipse.ui.editors/META-INF/MANIFEST.MF +++ b/org.eclipse.ui.editors/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.ui.editors; singleton:=true -Bundle-Version: 3.11.400.qualifier +Bundle-Version: 3.11.500.qualifier Bundle-Activator: org.eclipse.ui.internal.editors.text.EditorsPlugin Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName diff --git a/org.eclipse.ui.editors/pom.xml b/org.eclipse.ui.editors/pom.xml index 8adf7a15989..3179a966059 100644 --- a/org.eclipse.ui.editors/pom.xml +++ b/org.eclipse.ui.editors/pom.xml @@ -18,6 +18,6 @@ </parent> <groupId>org.eclipse.ui</groupId> <artifactId>org.eclipse.ui.editors</artifactId> - <version>3.11.400-SNAPSHOT</version> + <version>3.11.500-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/FileDocumentProvider.java b/org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/FileDocumentProvider.java index c7351d7bb38..a61d0616988 100644 --- a/org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/FileDocumentProvider.java +++ b/org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/FileDocumentProvider.java @@ -1128,7 +1128,19 @@ public class FileDocumentProvider extends StorageDocumentProvider { protected ISchedulingRule getValidateStateRule(Object element) { if (element instanceof IFileEditorInput) { IFileEditorInput input= (IFileEditorInput) element; - return fResourceRuleFactory.validateEditRule(new IResource[] { input.getFile() }); + IFile file= input.getFile(); + ISchedulingRule validateEditRule= fResourceRuleFactory.validateEditRule(new IResource[] { file }); + if (validateEditRule == null) { + // Note that factory decides to provide a null rule for modifiable files (not read-only). + // Null rule means, that org.eclipse.core.internal.resources.WorkManager.checkIn(ISchedulingRule, IProgressMonitor) + // will run jobManager.beginRule(null, monitor); which will NOT show any progress dialog + // and will *immediately* lock UI thread via lock.acquire(); while the workspace is locked + // Providing here a file we enforce the progress dialog, where this operation can be cancelled by user, + // so that an occasional "Modify" or "Save" of the editor will NOT block UI forever. + return file; + } else { + return validateEditRule; + } } return null; } diff --git a/org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/TextFileDocumentProvider.java b/org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/TextFileDocumentProvider.java index 5088a986e55..b00bcfd1748 100644 --- a/org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/TextFileDocumentProvider.java +++ b/org.eclipse.ui.editors/src/org/eclipse/ui/editors/text/TextFileDocumentProvider.java @@ -1017,7 +1017,19 @@ public class TextFileDocumentProvider implements IDocumentProvider, IDocumentPro public ISchedulingRule getSchedulingRule() { if (info.fElement instanceof IFileEditorInput) { IFileEditorInput input= (IFileEditorInput) info.fElement; - return fResourceRuleFactory.validateEditRule(new IResource[] { input.getFile() }); + IFile file= input.getFile(); + ISchedulingRule validateEditRule= fResourceRuleFactory.validateEditRule(new IResource[] { file }); + if (validateEditRule == null) { + // Note that factory decides to provide a null rule for modifiable files (not read-only). + // Null rule means, that org.eclipse.core.internal.resources.WorkManager.checkIn(ISchedulingRule, IProgressMonitor) + // will run jobManager.beginRule(null, monitor); which will NOT show any progress dialog + // and will *immediately* lock UI thread via lock.acquire(); while the workspace is locked + // Providing here a file we enforce the progress dialog, where this operation can be cancelled by user, + // so that an occasional "Modify" or "Save" of the editor will NOT block UI forever. + return file; + } else { + return validateEditRule; + } } return null; } |