I need to do something with wizards in Eclipse so I checked JDT how they have implemented wizards and found this weird code I don't understand.
It ignores the wizard scheduling rule (returned from getSchedulingRule) in case the code is called from already executing Job (it uses the scheduling rule of that Job). So if wizard needs the scheduling rule of entire workspace but the current thread is already executing any job, than the scheduling rule of this job is used instead, which can cause problems when the new runnable is executed in workspace. I added some comments to code so it is more clear.
Could any Eclipse expert explain why the try block is implemented as is (not just using getSchedulingRule)?
/**
* Returns the scheduling rule for creating the element.
* @return returns the scheduling rule
*/
protected ISchedulingRule getSchedulingRule() {
return ResourcesPlugin.getWorkspace().getRoot(); // look all by default
}
/*
* @see Wizard#performFinish
*/
@Override
public boolean performFinish() {
IWorkspaceRunnable op= new IWorkspaceRunnable() {
@Override
public void run(IProgressMonitor monitor) throws CoreException, OperationCanceledException {
try {
finishPage(monitor);
} catch (InterruptedException e) {
throw new OperationCanceledException(e.getMessage());
}
}
};
try {
//TODO: i need explanation of this block. Wizard should be used
// from UI thread, so the code Job.getJobManager().currentJob()
// means that there is possible Job currently executed by UI thread.
// Ok now if there is a job, use its scheduling rule ignoring getSchedulingRule.
// This could be maybe to force that this new runnable isn't executed until this thread finishes
// its current Job. Okb but if the current Job rule isn't so powerfull as this wizard needs, what than?
// It will cause error when executing op, because the runnable will not have enough access
// cause ignoring getSchedulingRule...
ISchedulingRule rule= null;
Job job= Job.getJobManager().currentJob();
if (job != null)
rule= job.getRule();
IRunnableWithProgress runnable= null;
if (rule != null)
runnable= new WorkbenchRunnableAdapter(op, rule, true);
else
runnable= new WorkbenchRunnableAdapter(op, getSchedulingRule());
getContainer().run(canRunForked(), true, runnable);
} catch (InvocationTargetException e) {
handleFinishException(getShell(), e);
return false;
} catch (InterruptedException e) {
return false;
}
return true;
}
I'm not sure I can explain all of this but a key thing to note is that
Job.getJobManager().currentJob();
only returns the current job in the current thread. Since performFinish
is normally run in the UI thread this would not be an ordinary background job. UIJob
jobs run in the UI thread. It looks to me like this code is trying to pick up the rule from some UI job that the wizard or associated code has already started.
The true
arguments on the call:
new WorkbenchRunnableAdapter(op, rule, true)
will cause WorkbenchRunnableAdapter
to call
Job.getJobManager().transferRule(fRule, thread);
if the thread changes. I think this means the code is trying to keep the same rule in use throughout the execution of the runnable and whatever job was previously running.