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

Revert IFile.create(...REPLACE) - fixes #1433 #1434

Merged
merged 4 commits into from
Jun 19, 2024

Conversation

jukzi
Copy link
Contributor

@jukzi jukzi commented Jun 18, 2024

Allows to use modifyRule for IFile.write() if the file already exits, so that every IFILE.setContents() can be replaced with IFile.create().

#1433

@jukzi jukzi linked an issue Jun 18, 2024 that may be closed by this pull request
Allows to use modifyRule for IFile.write() if the file already exits, so
that every IFile.setContents() can be replaced with IFile.create().

eclipse-platform#1433
Copy link
Contributor

github-actions bot commented Jun 18, 2024

Test Results

 1 734 files  ±0   1 734 suites  ±0   1h 30m 35s ⏱️ + 5m 31s
 3 971 tests +1   3 949 ✅ +1   22 💤 ±0  0 ❌ ±0 
12 510 runs  +3  12 349 ✅ +3  161 💤 ±0  0 ❌ ±0 

Results for commit 6825d24. ± Comparison against base commit 8969f83.

♻️ This comment has been updated with latest results.

Comment on lines 433 to 435
setDerived(derived, null);
}
setContents(content, force, keepHistory, monitor);
Copy link
Contributor

Choose a reason for hiding this comment

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

Usually, methods in resource are supposed to be atomic (ie grouped in a single operation), here those 2 methods would lead to 2 operations side by side but not grouped.
What you can try to remediate that would be something like

if (derived == isDerived()) {
	setContents(...);
} else {
	SubMonitor progress = SubMonitor.convert(monitor, 100).checkCanceled();
	// need to group operation
	final ISchedulingRule groupRule = new MultiRule[] { workspace.getRuleFactory().derivedRule(this), workspace.getRuleFactory().modifyRule(this) };
	try {
		workspace.prepareOperation(rule, progress.split(1)); // locks
		workspace.beginOperation(true);
		setDerived(derived, progress.split(1));
		setContents(content, force, keepHistory, progress.split(98))
	} catch (OperationCanceledException e) {
		workspace.getWorkManager().operationCanceled();
		throw e;
	} finally {
		progress.done();
		workspace.endOperation(rule, true); // this is extremely important of workspace is locked
	}

Copy link
Contributor

Choose a reason for hiding this comment

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

API-wise it's totally fine. See nested comment about the workspace operation should be atomic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion! I have tried your implementation and it would work but can not be done in default method of IFile due to references to internal methods. but based on the idea i think i have found a easier solution with less overhead. please review again. Junit also tests for atomic operations now.

Copy link
Contributor

Choose a reason for hiding this comment

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

in default method of IFile due to references to internal methods.

There is nothing mandating to create a default method here. IFile is annotated as "noextends/noimplements", so we can add new interface methods in here and have the implementation in File.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So far the therory. In practice there are other implementations even within the IDE (PDE's ContributionDataFile) :-(

Copy link
Contributor

Choose a reason for hiding this comment

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

While it's an annoyance, I think keeping the resource code as maintainable as posible is more important than keeping e4 tools alive. IMO, it's best to go for a regular interface method and impl in File, and to adapt PDE then.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure, that would be good. But i have no idea why it is like that in PDE and how it could be solved. So as long as we can implement a default its much easier to proceed.

@@ -414,7 +414,7 @@ public default void create(byte[] content, int createFlags, IProgressMonitor mon
* in sync with the local file system
* @param derived Specifying this flag is equivalent to atomically calling
* {@link IResource#setDerived(boolean)} immediately after
* creating the resource or non-atomically setting the
* creating the resource or atomically setting the
Copy link
Contributor

Choose a reason for hiding this comment

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

Given the pain introduced by "atomically setting the derived flag before setting the content of an already" which as far as I understand is not a very interesting case (derived usually matters at creation), maybe we should just restrict it and make it "createAsDerived" and ignore it when resource already exists.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The single most important usage of this API still is org.eclipse.jdt.internal.core.builder.AbstractImageBuilder.writeClassFile(ClassFile, SourceFile, boolean) where the derived flag is updated also for existing files. Thats ancient code and i do not know why. But its legit.
As a compromise we could only change (i.e. set) derived flag if derived==true is given as argument. In that case we would also not need the REPLACE. I pushed a implemementation for that: "A value of false will not update the derived flag of an existing file."

Comment on lines 433 to 435
setDerived(derived, null);
}
setContents(content, force, keepHistory, monitor);
Copy link
Contributor

Choose a reason for hiding this comment

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

in default method of IFile due to references to internal methods.

There is nothing mandating to create a default method here. IFile is annotated as "noextends/noimplements", so we can add new interface methods in here and have the implementation in File.

@jukzi jukzi requested a review from mickaelistria June 18, 2024 11:49
@jukzi
Copy link
Contributor Author

jukzi commented Jun 19, 2024

@mickaelistria can you please review again - i would like to merge today

Copy link
Contributor

@mickaelistria mickaelistria left a comment

Choose a reason for hiding this comment

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

My previous comments still stand, and I even support it stronger: it's better to just adopt the usual pattern of decl in interface + impl in File to not be bothered with REPLACE in lower-level API, even if it requires changing PDE also. This will ultimately lead to better result.
The over-usage of the REPLACE field in the lower level FileSystemResourceManager is worrying me, it's not the right grain where to hook this complexity. This is IMO a kind of design leak from a higer-level utility method into its low-level backend, and we shouldn't allow that unneeded change in the backend just for introduction of a convenience method downstream.

@jukzi
Copy link
Contributor Author

jukzi commented Jun 19, 2024

The over-usage of the REPLACE field in the lower level FileSystemResourceManager is worrying me

@mickaelistria i already solved that as you recommended. Please read the code again.
It feels wrong to reject a fix only because PDE did wrong.

@mickaelistria
Copy link
Contributor

i already solved that as you recommended. Please read the code again.

To be clear, I think that the introduction of write should not change FileSystemResourceManager at all, and I even think that everything about REPLACE should also remained the same old. This patch still modifies it.
I also don't see File implementing write directly as we discussed ealier.

It feels wrong to reject a fix only because PDE did wrong.

The thing is that I barely see your proposal here as a fix but more as some higher level concepts leaking into lower-level API, thus placing complexity at the wrong level which will utlimately hurt.
I suggest you just fully ignore PDE at the moment, and just make the default method an empty shell doing nothing (until PDE is fixed) and then allow yourself to implement a write in File that is consistent with all other operations; without need to modify FileSystemResourceManager.

@jukzi
Copy link
Contributor Author

jukzi commented Jun 19, 2024

To be clear, I think that the introduction of write should not change FileSystemResourceManager at all,

ok, i moved the code from FileSystemResourceManager to File - it can be done with the default method to not produce error in PDE.

Copy link
Contributor

@mickaelistria mickaelistria left a comment

Choose a reason for hiding this comment

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

Thanks, I think the current proposal is a clear improvement.

@jukzi jukzi merged commit e639885 into eclipse-platform:master Jun 19, 2024
16 checks passed
@jukzi jukzi deleted the REPLACE_rule branch June 19, 2024 07:42
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.

IFile.write: IllegalArgumentException
3 participants