Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ProfileChangeOperation: fix progressmonitor usage #335 #336

Merged
merged 2 commits into from
Nov 5, 2023

Conversation

jukzi
Copy link
Contributor

@jukzi jukzi commented Sep 26, 2023

passing a monitor to two callees requires it to be splitted.

#335

Also fixed forgotten monitor.done() in performProvisioningPlan and improved tests.

@github-actions
Copy link

github-actions bot commented Sep 26, 2023

Test Results

       9 files         9 suites   33m 50s ⏱️
2 181 tests 2 177 ✔️   4 💤 0
6 633 runs  6 622 ✔️ 11 💤 0

Results for commit a24950d.

♻️ This comment has been updated with latest results.

return;
}
IStatus status = transfer(targetDescriptor, sourceDescriptor, monitor);
SubMonitor subMon = SubMonitor.convert(monitor,NLS.bind(Messages.downloading, getArtifactKey().getId()),1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converting the monitor into a SubMonitor with work one does not look necessary here and it can just be passed directly.
Furthermore I think it is not necessary to add the try-catch-block to ensure done is called in any ways.
Just calling done below is sufficient and if the method throws the method returns any way and a caller that catches an exception either calls done on the passed monitor or (should) call split which also cleans up children.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Furthermore I think it is not necessary to add the try-catch-block to ensure done is called in any ways

Correct, IProgressmonitor states:

CALLER/CALLEE RESPONSIBILITIES:

Methods that receive an IProgressMonitor ("callees") must either obey the following conventions or include JavaDoc explaining how it differs from these rules. The called method:

Will call beginTask on the argument 0 or 1 times, at its option.
Will not promise to invoke done() on the monitor.
Will not call setCanceled on the monitor.
May rely on the monitor not being null.
May rely on the monitor ignoring the string passed to beginTask.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Furthermore I think it is not necessary to add the try-catch-block to ensure done is called in any ways

Correct, IProgressmonitor states:

CALLER/CALLEE RESPONSIBILITIES:

Methods that receive an IProgressMonitor ("callees") must either obey the following conventions or include JavaDoc explaining how it differs from these rules. The called method:

Will call beginTask on the argument 0 or 1 times, at its option.
Will not promise to invoke done() on the monitor.
Will not call setCanceled on the monitor.
May rely on the monitor not being null.
May rely on the monitor ignoring the string passed to beginTask.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

furthermore I think it is not necessary to add the try-catch-block to ensure done is called in any ways.

Yep, see javadoc of IProgressMonitor ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By passing a submonitor with progress one and unconditional calling done() the progress is unconditional increased which ensures to use up all the reserved progress. That is technically not necessary but ensures a user readable progress from 0 to 100% . This commit fixes the error and proofs that with junit test. This pattern is easy to understand and not error prone. I am not interested in further "optimizations" that "may" work under special conditions.

Copy link
Member

@laeubi laeubi Oct 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just wanted to note that when one splits a monitor previous aquired "splits" will be completed automatically, so if the toplevel is calling done() and the other code is always using split() progress si still reported correctly in every case.

This is to ease fulfill the contract responsibility of IProgressMonitor for the called method:

  1. Will call beginTask on the argument 0 or 1 times, at its option.
  2. Will not promise to invoke done() on the monitor.
  3. Will not call setCanceled on the monitor.
  4. May rely on the monitor not being null.
  5. May rely on the monitor ignoring the string passed to beginTask.

so calling done is of course allowed, but not required by a method receiving a monitor.

if (steps.isEmpty()) {
LogHelper.log(new Status(IStatus.WARNING, Activator.ID,
NLS.bind(Messages.noDigestAlgorithmToVerifyDownload, artifactDescriptor.getArtifactKey())));
monitor = IProgressMonitor.nullSafe(monitor);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SubMonitor.convert can handle null monitors and can't we just call done on the sub-monitor?
Again I think the try-catch is not necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all my concerns are expressed by the junit result.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But asserting in AbstractProvisioningUITest that for example ProvisioningSession.performProvisioningPlan() consumes all work of the monitor is not right.

From the Java-doc of IProgressMonitor:
https://github.com/eclipse-equinox/equinox/blob/8beb9ce0b1dea1176b06ad7300f2663ff6db4eff/bundles/org.eclipse.equinox.common/src/org/eclipse/core/runtime/IProgressMonitor.java#L57

CALLER/CALLEE RESPONSIBILITIES: 

Methods that receive an {@link IProgressMonitor} ("callees") must either obey the following conventions or include  these rules. The called method:
[...]
- Will not promise to invoke {@link #done()} on the monitor.

[...]
The caller:
[...]
Will not rely on the callee to invoke {@link #done()} on the monitor. It must either select an implementation of {@link IProgressMonitor} that does not require {@link #done()} to be called, for example {@link SubMonitor}, or  it must invoke {@link #done()} itself after the method returns.

And the only real caller of ProvisioningSession.performProvisioningPlan() in ProfileModificationJob.runModal() even explicitly calls done, although even that would actually not be necessary since the caller of that method respectively the creator of a progress-monitor is responsible to call done.

So asserting that in the tests is too strict.

Copy link
Member

@HannesWell HannesWell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Jörg for this PR.
In general in most places SubMonitor.convert() can be used directly and calling IProgressMonitor.nullSafe() seems not necessary.

Furthermore as already written in the detailed comments, a try-catch-block is often not necessary because in case of an exception the method terminates immediatly and progress-reporting usually stops anyways.

Besides the already covered locations it looks like MirrorRequest.transfer() also seem not to handle its monitor correctly, just like SimpleArtifactRepository.getArtifact() and getRawArtifact().

@jukzi
Copy link
Contributor Author

jukzi commented Oct 25, 2023

Mind to submit as-is if nobody works on further improvements?

@HannesWell
Copy link
Member

Mind to submit as-is if nobody works on further improvements?

I can take care of this later/in the next days.

@HannesWell HannesWell force-pushed the monitor_done()_ branch 2 times, most recently from 4962ea4 to d97e3cd Compare October 29, 2023 14:28
@HannesWell
Copy link
Member

HannesWell commented Oct 29, 2023

@jukzi as discussed I now corrected/simplified the usage of the SubMonitor.
Please have a look if your main points are still addressed.

In some places I used newChild() instead of split() since split throws OperationCancledException, which is actually more convenient, but the calling code seems to expect a canceled status instead.

@jukzi
Copy link
Contributor Author

jukzi commented Nov 2, 2023

@jukzi as discussed I now corrected/simplified the usage of the SubMonitor.

@HannesWell the tests fail now - which shows the problem

@HannesWell
Copy link
Member

@jukzi as discussed I now corrected/simplified the usage of the SubMonitor.

@HannesWell the tests fail now - which shows the problem

As said above it is not a problem. A callee does need not call done on the received monitor as the doc states.
It is perfectly fine if a method has e.g. ten pieces of work to do if it returns and the monitor only has 9/10 pieces of work consumed.
It is the responsibility of the creator of of a Monitor to call done on it.
Therefore I think the new assumption in the tests is not right.

Did you encounter any creator of a Monitor that did not call done? If yes that should be fixed instead of requiring the calles to call done. Of course they should consume some amounts of work, but the last bit can be left out. Therefore the SubMonitor.split/newChild() pattern was introduced, which actually makes it relatively simple to use monitors right.

@laeubi
Copy link
Member

laeubi commented Nov 3, 2023

Of course they should consume some amounts of work

A receiver of the monitor might or might not call any method on the monitor... The good thing about submonitor is that one can reuse unused progress if desired.

@HannesWell HannesWell force-pushed the monitor_done()_ branch 2 times, most recently from 25832ef to 58d2455 Compare November 5, 2023 09:33
passing a monitor to two callees requires it to be splitted.

eclipse-equinox#335

Also fixed forgotten monitor.done() in performProvisioningPlan and
improved tests.
@HannesWell
Copy link
Member

I know changed the two methods that the test complains about (although I still have the opinion that they are too strict) to call done at the end of the method, but not in a finally block. This way all the callers that make false assumptions should be happy too.

This way we can have at least the more important changes to avoid multiple monitor usages.

@HannesWell HannesWell merged commit e31c371 into eclipse-equinox:master Nov 5, 2023
8 of 9 checks passed
@jukzi jukzi deleted the monitor_done()_ branch November 6, 2023 07:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants