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

TreeView reveal creates a race condition with TreeDataProvider#getChildren #192055

Open
EhabY opened this issue Sep 2, 2023 · 7 comments
Open
Assignees
Labels
bug Issue identified by VS Code Team member as probable bug tree-views Extension tree view issues
Milestone

Comments

@EhabY
Copy link
Contributor

EhabY commented Sep 2, 2023

If the tree provider is loading the tree and TreeView#reveal is revealing an element that is not loaded yet (so the parents are loaded) then a race condition can cause the view to break with the message "Element with id X is already registered".

I have also tried to create a promise queue as suggested in this comment but the issue still exists. I think this issue is caused by reveal and refresh of the tree, like it's not reveal statements causing it but rather the tree is not synchronized with reveal.

See the following example repo (based on the tree view sample): https://github.com/EhabY/TreeViewRevealExample/tree/main

  1. Run the extension
  2. Run the command Test View: Reveal
2023-09-04.17-54-20.mp4

For reference this what the executed command does:

// refresh the parent
provider.refresh();

// Reveal the child which will load the parent
this.promiseQueue = this.promiseQueue.then(async () => view.reveal({ key: "aa" }, { focus: true, select: false }));

// refresh the parent
provider.refresh();
@EhabY
Copy link
Contributor Author

EhabY commented Sep 19, 2023

Pinging @alexr00 so it does not get lost 🙏

@alexr00 alexr00 added this to the October 2023 milestone Sep 19, 2023
@alexr00 alexr00 modified the milestones: October 2023, November 2023 Oct 25, 2023
@alexr00 alexr00 modified the milestones: November 2023, December 2023 Nov 24, 2023
@alexr00
Copy link
Member

alexr00 commented Dec 5, 2023

@EhabY the way for you to solve this from your end would be to keep track of if your tree is currently refreshing and then wait to do the reveal until after the refreshing has finished.

@EhabY
Copy link
Contributor Author

EhabY commented Dec 11, 2023

@alexr00 How is it possible to track if the tree is refreshing? IIRC the refresh events do not return a promise so I would need to keep track of all visible nodes and then make sure that all of those open nodes have been reloaded (making sure to handle deleted nodes). Is there a better approach for this?

But even then, this would reduce the possibility of it happening instead of eliminating it which requires internal syncing.

@alexr00
Copy link
Member

alexr00 commented Dec 12, 2023

How is it possible to track if the tree is refreshing?

I was thinking you could track if you have an unfinished getChildren method that was called.

@EhabY
Copy link
Contributor Author

EhabY commented Dec 12, 2023

Ah, I actually tried that but it didn't work because refreshing would call getChildren sequentially:

  1. getChildren(undefined)
  2. getChildren(root)
  3. getChildren(child1)
    ...

So while we can easily check if a getChildren is being executed, we cannot know for sure when it has finished executing all calls unless we manually track all visible tree items (which can be error prone..)

@joaomoreno joaomoreno added bug Issue identified by VS Code Team member as probable bug tree-views Extension tree view issues labels Dec 14, 2023
@alexr00
Copy link
Member

alexr00 commented Dec 19, 2023

So while we can easily check if a getChildren is being executed, we cannot know for sure when it has finished executing all calls unless we manually track all visible tree items (which can be error prone..)

I think this is still possible. You just need to increment a counter when getChildren is called, then decrement it before returning.

@EhabY
Copy link
Contributor Author

EhabY commented Jan 4, 2024

I just attempted a solution as you described and it still fails, I have added a counter that is incremented first thing in getChildren and a decremented last thing in getChildren and I get the following trace.

"Before" is printed first thing in getChildren, "During" is in the middle,"After" is printed last thing, "Done!" is printed when the counter is 0 and the created promise when refreshing is resolved.

Before:  0
During:  1
After:  0
Done! -- Finishes after only refreshing the root and not the rest of the tree
Before:  0
Before:  1
During:  2
After:  1
Before:  1
During:  2
After:  1
During:  1
After:  0
Done!

Notice how when I execute the command above, it refreshed the top level first (and finishes execution) before refreshing the rest of the tree - thus depending on the counter won't work as it finishes a refresh of the root before refreshing the rest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue identified by VS Code Team member as probable bug tree-views Extension tree view issues
Projects
None yet
Development

No branches or pull requests

3 participants