I am probably just doing it the wrong way but the idea is
that I have one (or more) buttons which when pressed/clicked
load some data and display that data in a ListBox.
When I click the button the first time I can see the first WriteLine
and then the LoadItemsAsync method is awaited.
If I don't do anything it will just await forever.
When I click the button again or move focus to the next control
the awaited method completes and the next WriteLine is executed.
Update: If I move the mouse the awaited method also completes
I have tried without MainLoop.Invoke as well.
I'm guessing I am just handling this the wrong way.
Any suggestions?
button.Clicked += async () =>
{
Application.MainLoop.Invoke (async () => {
//When the button is 'clicked' the following is executed
Debug.WriteLine($"Clicked the button");
var items = await LoadItemsAsync();
//However the following line is not executed
//until the button is clicked again or
//until the cursor is moved to the next view/control
Debug.WriteLine($"Got {items.Count} items)");
itemsList.SetSource(items);
//Without calling this the UI is not updated
this.LayoutSubviews();
});
};
System: Ubuntu 19.10
@timothyparez Can you provide the code of what you want to do? I needed your code to look for more if I could make it available, of course. But in principle, the problem is that code is not executed because it is awaitable and will only execute after the execution of the asynchronous method. When the asynchronous method is fully executed it is in a different thread than the one in which it was called. Try the following code:
button.Clicked += async () => {
//When the button is 'clicked' the following is executed
Debug.WriteLine ($"Clicked the button");
using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource ()) {
syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext ();
await Task<List<string>>.Run (() => LoadItemsAsync (cancellationTokenSource.Token).Result).ContinueWith (task => {
//However the following line is not executed
//until the button is clicked again or
//until the cursor is moved to the next view/control
var items = task.Result;
Debug.WriteLine ($"Got {items.Count} items)");
itemsList.SetSource (items);
//Without calling this the UI is not updated
this.LayoutSubviews ();
}, CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
syncContextTaskScheduler);
}
};
I'll take a look in the morning and I will try to provide a reproducing sample.
Will try your version as well, thnx!
@BDisp I created a sample here:
https://github.com/timothyparez/gui.cs-issue-306
You can run it and hit the "Load Data" button once.
It will sit there forever until you do any of the following:
@timothyparez I'm working on it. ReadConsoleInput does not go well with Task.Delay (2000). However with Thread.Sleep (2000) it already works. Thanks.
@BDisp The use of Task.Delay(2000) in this sample is just to simulate awaiting something.
In reality I am awaiting an async SQLite query: var people = await database.Table<Person>().ToListAsync();
Yes I know. I'll try to solve that.
https://github.com/timothyparez/gui.cs-issue-306/pull/1
@timothyparez although it is working I noticed that it is not running asynchronously. I remain committed to trying to resolve this issue but if anyone else can contribute it would be great. In a windows forms or wpf project it works well, so I don't know if it's a gui limitation or if the asynchronous calling code isn't ideal for this gui.
@timothyparez. After some investigation it seems that there is an explanation for the freeze in await Task.Delay (2000). In the execution of this statement, it goes back to the calling code that is also awaiting for a returning task from the called task resulting in a deadlock. When a key is pressed or the mouse is moved is like the caller code is wakeup and request for the awaiting called task to return and ends execution. If you are getting data from a database this freeze doesn’t happen because you are going awaiting from another method, rather the caller. Can you confirm if the freeze also happens when getting data from a async database task? Thanks.
Hi,
So the original code, how I found the issue, looks like this:
var people = await database.Table<Person>().ToListAsync();
https://github.com/praeclarum/sqlite-net/blob/master/src/SQLiteAsync.cs
@timothyparez, please confirm that freezing data from sqlite only happens the first time it is run. Subsequently, pressing the button normally loads the data without freezing.
@BDisp It does not. Every time I load data I have to press enter twice (or move the mouse)
@BDisp please take a look at my "binding" branch.
Added SQLite there.
It really seems that as soon as anything is awaited this issue occurs.
@ timothyparez I just sent a PR to resolve this symptom. Please check if it works well.
https://github.com/migueldeicaza/gui.cs/pull/312/commits/9dd806f697768dcc62e6f3d4407c9f2742de8275
Ok will have a look. Also is there a place to contact you I have some questions.
@timothypare I think on my github the contact appears. Check out https://github.com/bdisp/
No contact info there :D
I didn't even know. Now it is :)
In your case, you are adding a idle handler in response to the click to run on the UI thread - but you are already on the main thread.
You do not need that level of indirection.
The patch shown before that does Send + MainLoop is likely wrong, as this would invoke a method that is expected to run on the UI from the background
Thank you. I already noticed that. Even Send invokes a synchronous execution. Already managed to overcome the await delay but the problem is that does not return to the UI until you press a key or a mouse movement. I keep trying to solve this but dealing with threads is always very slippery.
@timothypare please try again with the fix submitted and give feedback if it causes anothers weirds behaviours
In your case, you are adding a idle handler in response to the click to run on the UI thread - but you are already on the main thread.
You do not need that level of indirection.
The patch shown before that does Send + MainLoop is likely wrong, as this would invoke a method that is expected to run on the UI from the background
Are you referring to Application.MainLoop.Invoke (() => { ... }
I can remove that but the issue remains.
@timothypare please try again with the fix submitted and give feedback if it causes anothers weirds behaviours
Sorry, which one?
https://github.com/migueldeicaza/gui.cs/pull/312/commits/a68334551098b3d97ffffd41771aee8c2f002375
@timothyparez is the last one. I think it must always be the last. Someone said that changes submitted to an already submitted PR can always be done on the same PR. However, I had a doubt that I would be grateful if someone with true knowledge of the subject please clarify me. Must the changes be submitted based on the last submission, as it really was, or will I have to make the changes again from a new upstream/master checkout? Thanks in advance.
In your case, you are adding a idle handler in response to the click to run on the UI thread - but you are already on the main thread.
You do not need that level of indirection.
The patch shown before that does Send + MainLoop is likely wrong, as this would invoke a method that is expected to run on the UI from the backgroundAre you referring to
Application.MainLoop.Invoke (() => { ... }
I can remove that but the issue remains.
@timothyparez that @migueldeicaza comment was for me :)
@timothyparez as you don't have a fork of this repository in your github, of course you have to check it out in my fork of this repository at https://github.com/BDisp/gui.cs. But that you may already know, just to remember :)
This is already fixed and may be closed.