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
Scrolling list bound to data programmatically causes nil pointer dereference #2549
Comments
Please provide a fully working code example instead of a short code snippet. We prefer to have minimal code examples that provide nothing more than a base of reproducing the issues but can be built and tested without having to guess how pieces should be put together. |
Updated code section with full example |
Thanks for updating the code example. It would however be good if you could remove all the unnecessary details and just trim it down the most minor code that reproduces the issue (i.e. just fyne code and the parts that make the issue happen, no extra features or external dependencies). I know that it can be a bit tedious but it saves us immense amount of time for every issue because there is less unnecessary code to try and debug. |
What is unnecessary? I built this as the minimal example that reproduces the problem correctly I have removed the word generator if that was causing you problems. |
Thanks. I’m just saying that there is a lot of mutexes, goroutines and other stuff that makes it hard to debug (and a potential race condition could very well be the source of the issue in the first place). |
Ahh I see, I had this setup w/o the mutexes but thought it better to include them since they dealt with the subset of shared variables. This happens with/without the mutexes. There should only be 1 goroutine used for the scroll (how I found the error above) active at any time, otherwise there can't be programmatic keyboard scrolling as it wouldn't stop. This error also happens when scrolling slowly through the list up/down, but originally I saw it using the keyboard to scroll! |
What happens if you move the wrapping code line into the template function? |
@andydotxyz I was able to replicate the issue with the wrapping line moved to the label creation as well: list = widget.NewListWithData(data,
func() fyne.CanvasObject {
tmp := widget.NewLabel("template")
tmp.Wrapping = fyne.TextTruncate
return tmp
},
func(i binding.DataItem, o fyne.CanvasObject) {
//o.(*widget.Label).Wrapping = fyne.TextTruncate
o.(*widget.Label).Bind(i.(binding.String))
}) |
I think I have an idea about the bug leading to this issue. It's a concurrency bug. There is kind of a data race between Label.Bind() and Label.Unbind(). Since Bind() calls Unbind() to clear the previous binding, this is also a race between two concurrent Bind() calls. Some crash investigationThe crash happens in the data listener of a widget.Label. It executes This is not necessarily a bug since However, in this situation the user is blameless. We can check this by modifying the "update list item" function. Though you'll also need to edit fyne source code to export the struct list = widget.NewListWithData(data,
func() fyne.CanvasObject {
tmp := widget.NewLabel("template")
tmp.Wrapping = fyne.TextTruncate
return tmp
},
func(i binding.DataItem, o fyne.CanvasObject) {
stringBinding := i.(binding.String)
// CHECK THE VALIDITY OF THE BIND ARGUMENT
if s, ok := stringBinding.(*binding.BoundExternalStringListItem); ok {
if s == nil {
log.Printf("Nil pointer dressed as a non-nil pointer!\n")
}
}
o.(*widget.Label).Bind(i.(binding.String))
}) With this modification I can still replicate the issue, though it takes much longer for crash to happen (I increased the list to 1000 elements and scrolled with up/down arrows for maybe 15-20 seconds). I think the difference just arises from the updateFunction becoming slower due to type assertions. Notably, the situation we test for never happens, we never pass an invalid pointer. So if we trust the code above, we know that (1) we never provide an invalid value to One possible explanationThis can theoretically happen due to the non-atomicity of interface assignments. The field
Under the hood an interface value is just a pair of pointers. There are no atomicity guarantees about assignments of interfaces. (You can also disassemble the compiled code to see that the compiler implements Assume we are concurrently performing
The result is: I wasn't able to verify directly that this is what happens during the crash because I don't know how. Some intrusive attempts to log the internal structure of interface values cause go to generate more complicated code with various memory barriers and stuff, and then I cannot replicate the issue. Possible fixMaking the access to Note: there is very clearly a data race happenning with several concurrent Bind()s, so if we want to get rid of data races this should be fixed even if it's not related to the bug described by the original reporter. Any good fix for data races would also protect against the non-atomicity, I think. CounterargumentsThe possible bug I described does not explain why the issue does not happen if I just scroll the list with my touchpad, even though I can do that much quicker than what happens when I hold the down arrow key. Notably, my description does not depend on List.Select being called at all. I don't know, maybe it's not the selection that is the problem, but somehow scrollTo function invoked by Select causes Or maybe the only difference is that scrolling manually happens "on the main thread", while in the example code here scrolling happens on a separate goroutine. I don't know. |
I am able to reproduce with scrolling @nullst, I set up everything the same except without the keydown/up code. Scrolling the list slowly up and down is what throws that error for me. Thank you for all the great input! |
@thenick775 can you please test with the PR #2566 that @nullst opened |
@andydotxyz I was also unable to reproduce testing against develop after that pull request was merged, looks good here thank you! |
Great, thanks for confirming |
Describe the bug:
When scrolling a list bound to data using the keyboard programmatically, this results in a nil pointer dereference. This is after the list has been bound to an existing slice of string.
Sometimes this works, but more often than not I run into this issue, since keyboard scrolling using the arrow keys did not seem to be supported.
I have seen this behavior in the middle of list navigation in this manner, as well as at the end and near the beginning. I also see this when using the mouse wheel to scroll.
To Reproduce:
Steps to reproduce the behaviour:
declare a list and populate it with data (50-100 elements)
use a function like the one included below to map the keydowns to a scroll, where the keyup function will just disable the bool used to keep the scroll going, this function is called from a goroutine.
Example code:
Error Report
Device (please complete the following information):
Any help on debugging this problem is appreciated!
The text was updated successfully, but these errors were encountered: