Let's have a look at some quotes from a famous web site for collecting (mostly negative) feedback about Eclipse.
When I cancel a task, it hangs and ends up taking longer than it would have taken to let it finish.
Cancelling never works. Trying to build a project. It get's stuck. I cancel it. It cancels for 10 minutes. I have to force quit it again.
Why in gods name do you have a cancel task option if it's never going to cancel the @#$% task?? Is this some kind of sick joke?There is also a blog post written back in 2006, which gives a more detailed picture of a user experience with the cancel button.
Why does Eclipse provide a cancel button that does not work?
Let's have a look how the Cancel Operation button is implemented. When you click on the red square button, the Eclipse Platform raises a "cancellation" flag for the running background operation. Then it is up to the latter to check if that flag is raised and terminate itself.
In other words, the Eclipse Platform has no power to terminate background operations, but only to send them a request for cancellation. If the operation is implemented in a proper way, i.e. frequently checks for the cancellation flag, it will promptly terminate itself. Alternatively, a poor implementation may totally miss checking the cancellation flag and finish as if the user has not pushed the cancel button.
In an even worse scenario, the background operation may check for the cancellation flag, but instead of terminating itself immediately, it may try reverting everything it has done so far. While this may be a valid approach for some use cases where keeping data consistency is critical, most of the time it is just an over-engineering. This way an operation that was canceled in the middle of the execution, may take longer than if it has not been canceled at all. This leads to even more frustrating user experience.
What is the solution?
Unfortunately, there is no direct solution for you as a user to apply to your IDE. The issue is caused by a weak code implementation in the plugins providing the background operations and it must be fixed there. There is no magic fix that can be implemented in the Eclipse Platform alone.
However, there is still something you can do:
However, there is still something you can do:
- Report the issue - yes, please open a bug if you stumble upon an operation that does not terminate promptly when you hit the cancel button. This is a good small step to make a difference.
- Keep your Eclipse up-to-date with the latest version of all plugins with the hope that this kind of issues will be resolved over time.
If you are an Eclipse plugin developer then you should design carefully your background operation, so users are able to cancel them. It is natural to focus on the happy path when implementing a new operation. But it won't be always the case where users will trigger your operation and will patiently wait for it to finish. Quite often users will realize they've trigger your operation accidentally, or it is taking longer than expected and they don't want to wait for it any longer, or they just want to do something else like saving a file or shutting down the IDE, but your operations is blocking them, or... tons of other reason that may make users want to cancel your operation.
And if users cannot cancel your operation within a few seconds, they will open the Task Manager and will kill the IDE. Which is a lose-lose situation - neither your operation will be completed, nor the user will be happy. So, give the user the chance to win :-)
And if users cannot cancel your operation within a few seconds, they will open the Task Manager and will kill the IDE. Which is a lose-lose situation - neither your operation will be completed, nor the user will be happy. So, give the user the chance to win :-)
Code tips on improving the implementation of background operations
The most fundamental thing to make your background operations responsive for cancellation is to check if the cancellation flag has been raised. You should have already been provided with an instance of IProgressMonitor in your operation's implementation, whether it is a Job, a WorkspaceJob, a WorkspaceModifyOperation, etc. Checking for the cancellation flag is simply calling the the monitor's isCanceled() method.
The below code examples check for the cancellation flag and interrupts the operation's workflow by returning the CANCEL_STATUS.
if (monitor.isCanceled()) return Status.CANCEL_STATUS;
Checking for the cancellation flag should be done as often as possible. This check is a cheap operation and there should not be any concerns about the performance. In the end, if you have a long running operation, it is more important for users to cancel it promptly than having it a few milliseconds faster.
Very often long running operations are processing lots of items in a loop. As the list of items may grow unpredictably long, there should be a check for cancellation inside the loop on every iteration. This ensures that the operation can be canceled promptly regardless of the number of items that are processed. See the example below:
Very often long running operations are processing lots of items in a loop. As the list of items may grow unpredictably long, there should be a check for cancellation inside the loop on every iteration. This ensures that the operation can be canceled promptly regardless of the number of items that are processed. See the example below:
while (hasMoreWorkToDo()) { // do some work // ... if (monitor.isCanceled()) return Status.CANCEL_STATUS; } return Status.OK_STATUS;Another common issue with unresponsive background operations is when they execute an external process or a long I/O operation and block on it waiting to finish. Waiting on external processes or I/O operations should no be done indefinitely. You should take advantage of any API that allows waiting for a limited amount of time. This way you can wait just for a short time (e.g. one second), then check if your operation is canceled, and if not, wait again for a short time. Below is an example how to wait for an external process to finish and check if your operation is canceled at the same time.
while (!process.waitFor(1, TimeUnit.SECONDS)) { // process is still running // check if the operation has been canceled if (monitor.isCanceled()) { process.destroy(); return Status.CANCEL_STATUS; } }Finally, some further readings I highly recommend to every Eclipse plugin developer:
- A comprehensive tutorial on Eclipse Jobs and Background Processing. Chapter 6 is dedicated on handling job cancellation and reveals a more efficient API coming with the new Eclipse Neon release.
- An old but gold article on concurrency in Eclipse - On the Job: The Eclipse Jobs API.