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

Fix external document loading #4952

Merged
merged 5 commits into from Oct 31, 2021
Merged

Conversation

realthunder
Copy link
Collaborator

Forum thread https://forum.freecadweb.org/viewtopic.php?p=495078#p495078

The problem happens when partial loading is enabled. If document A contains a link to some object in document B, it will load B as partial document with only that object and its necessary dependencies. But if document A contains another link to some object in document C which also has a link to some object in document B, the link in document C may not be restored, because document B is partially loaded without the linked object. This patch will check for this case and reload document B for more objects.

@gbroques
Copy link
Contributor

gbroques commented Aug 2, 2021

Thank you @realthunder!!

I'll see if I can find some time to review and verfiy this fix.

@luzpaz luzpaz added the Core Issue or PR touches core sections (App, Gui, Base) of FreeCAD label Aug 2, 2021
src/App/Application.h Outdated Show resolved Hide resolved
@luzpaz luzpaz added the Missing: confirmation Missing confirmation from other testers label Aug 6, 2021
Copy link
Member

@chennes chennes left a comment

Choose a reason for hiding this comment

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

I've added a few comments on the code (I have not tested the PR yet, so this is just code inspection stuff).

objNames.swap(it->second);
}
int pass = 0;
do {
Copy link
Member

Choose a reason for hiding this comment

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

Why use a do... here? Given the exit condition !_pendingDocs.empty(), you actively don't want this to guarantee the first iteration given your access to _pendingDocs.front(). In addition, the Core Guidelines advise against its use (see http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es75-avoid-do-statements). I suggest switching to a while...:

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Well, I think this would be a 'genuine examples where a do-statement is a clear statement of a solution'. _pendingDocs may get pushed with new entry at the very end of this very long loop statement. And there is a big block of comments explaining the reason, about 10 lines above the exiting while(). Although this is not the only place where _pendingDocs is refilled, but I just want emphasize that push which is the solution to the problem that this PR is trying to solve.

Copy link
Member

Choose a reason for hiding this comment

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

My concern here is that when someone reads this do...while loop, it calls their attention to the fact that you are trying to guarantee at least one execution, regardless of loop end condition. That is contrary to the code itself, however: if _pendingDocs were indeed empty upon entering this loop, you very much do NOT want to execute it once, the statement _pendingDocs.front() is almost sure to crash.

I argue that it's much more comfortable for a programmer reading this code to keep the loop end condition in mind as they read, despite the length of the loop. As written the only way to read it is to first scroll to the end, note the end condition, then scroll back to the top. Then you have to think "OK, why do we want to guarantee one execution of this loop, even if the end condition is satisfied?" And then you see line 704... it's very confusing. I don't think you accomplish your stated goal of emphasizing the final push: your large comment block emphasizes that without trouble: using this very non-idiomatic loop construction only muddies the waters.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

My concern here is that when someone reads this do...while loop, it calls their attention to the fact that you are trying to guarantee at least one execution, regardless of loop end condition.

I think that's where our difference is. When I see a do {} while loop, I think of it as not worth it to check for loop entry condition because it is already guaranteed. And it is, as you can see with the few lines before the loop. Maybe it would be more obvious if I move the code to populate _pendingDocs closer together?

I see do {} while loop more for the emphasis on exit condition rather than entry, and the second half of this loop (which is newly added by this PR) is dedicated for checking the exit condition, so it would be logical to put the final check at the end.

Copy link
Member

Choose a reason for hiding this comment

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

We can agree to disagree on this point: every developer is a little different, after all! I don't think this is a deal-breaker for the merge, it's not worth either of us spending more time considering it, IMO.

src/App/Application.cpp Outdated Show resolved Hide resolved
src/App/Application.h Outdated Show resolved Hide resolved
src/App/Document.h Outdated Show resolved Hide resolved
src/Gui/ViewProviderDocumentObject.cpp Outdated Show resolved Hide resolved
@@ -472,6 +473,12 @@ void ViewProviderPartExt::onChanged(const App::Property* prop)
// if the object was invisible and has been changed, recreate the visual
if (prop == &Visibility && (isUpdateForced() || Visibility.getValue()) && VisualTouched) {
updateVisual();
// updateVisual() may not be triggered by any change (e.g.
// triggered by an external object through forceUpdate()). And
// since DiffuseColor is not changed here either, do not falsly set
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// since DiffuseColor is not changed here either, do not falsly set
// since DiffuseColor is not changed here either, do not falsely set

@luzpaz
Copy link
Contributor

luzpaz commented Sep 2, 2021

@realthunder when you have a free moment, can you respond to the reviewers? thx!

@realthunder
Copy link
Collaborator Author

Yeah, I've already conversed with @chennes about it. I'd like to make some modifications according to the comments (this and a few other PR), but it takes quite some time to checkout a branch and compile and test. I am busy with a pending release at the moment. Will get back to it soon.

@chennes
Copy link
Member

chennes commented Sep 2, 2021

No rush on my end, obviously, I'm just slowly working my way through these old PRs in the hopes that we can get them resurrected and merged.

@berndhahnebach
Copy link
Contributor

Following a link to the branch on the CI-repository:

https://gitlab.com/freecad/FreeCAD-CI/-/commits/PR_4952

The CI-status is available on the latest commit of the branch.
If there is no status available the PR should be rebased on latest master.
Check pipeline branches to see if your PR has been run by the CI.

https://gitlab.com/freecad/FreeCAD-CI/-/pipelines?scope=branches

@berndhahnebach
Copy link
Contributor

a rebase on master would be great to see if CI runs fine ...

The problem happens when partial loading is enabled. If document A
contains a link to some object in document B, it will load B as partial
document with only that object and its necessary dependencies. But if
document A contains another link to some object in document C which also
has a link to some object in document B, the link in document C may not
be restored, because document B is partially loaded without the linked
object. This patch will check for this case and reload document B for
more objects.

See an example reported in
https://forum.freecadweb.org/viewtopic.php?p=495078#p495078
@chennes chennes self-assigned this Oct 31, 2021
@chennes
Copy link
Member

chennes commented Oct 31, 2021

I notice that when I load the original example file from that forums discussion (which now works fine), if I then immediately close the file, it asks if I want to save my changes, even though I haven't knowingly made any. Is that just a consequence of the file structure? Is it expected?

@realthunder
Copy link
Collaborator Author

The author probably didn't save the external files in the correct order, that is, save the external documents first before the depending document, which FreeCAD will automatically do when saving the main document. As mentioned in the 'big block of comments' I mentioned earlier, the Link will mark itself as 'touched' if it detects the linked document has changed its LastModifiedDate property. If you do a recompute, save the file, close all, and then re-open it, it won't prompt for save again when you close it.

@chennes chennes merged commit ce56d65 into FreeCAD:master Oct 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Core Issue or PR touches core sections (App, Gui, Base) of FreeCAD Missing: confirmation Missing confirmation from other testers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants