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: The synchronization context method Send is now blocking #1854

Merged
merged 15 commits into from
Aug 12, 2022
Merged

Fix: The synchronization context method Send is now blocking #1854

merged 15 commits into from
Aug 12, 2022

Conversation

saskathex
Copy link
Contributor

The send method of the synchronization context has to block the caller thread in case it is not the main thread.

The send method of the synchronization context has to block the caller thread in case it is not the main thread.
@tig
Copy link
Collaborator

tig commented Jul 19, 2022

Hi. Thanks for this.

Can you please explain what issue this fixes and how it fixes it?

@saskathex
Copy link
Contributor Author

Sure. The current "send" method implementation of the synchronization context is in fact equal to the "post" method. It does enqueue/dispatch the action in the idle handler of the main loop.
The microsoft documentation states that the send invokes the action synchronuously. https://docs.microsoft.com/en-us/dotnet/api/system.threading.synchronizationcontext.send?view=net-6.0

The changes: The send method waits until the action is executed in the main thread/loop and blocks the caller until done. There is a differentation between a call from the main thread and a "non"-main thread. To differentiate the main thread the main thread id is being stored during the .Init call.

My use/case is the following:

  • Use a task to do some long running work. During the runtime of the task user input is required. So I popup dialog is displayed. The current implementation does not block the caller task until user input is done.
  • I used the the same concept in other ui frameworks like gtksharp and winforms and it works correctly.

@tznind
Copy link
Collaborator

tznind commented Jul 19, 2022

The current implementation does not block the caller task until user input is done.

Is it worth making the blocking optional? I know winforms has Invoke and InvokeAsync where the first blocks and the second does not. If our current implementation is non blocking then changing it to blocking could be a breaking change? So having both as options would be good so that users who don't want blocking are still able to get it?

@BDisp
Copy link
Collaborator

BDisp commented Jul 19, 2022

I never hit the Send method on a breakpoint in the GUI.cs. The Post method yes I hit in a breakpoint on async/await.
On a web app the synchronization context is not working. @saskathex do you know if there are similar functionality for a web app?

@tig
Copy link
Collaborator

tig commented Jul 22, 2022

The current implementation does not block the caller task until user input is done.

Is it worth making the blocking optional? I know winforms has Invoke and InvokeAsync where the first blocks and the second does not. If our current implementation is non blocking then changing it to blocking could be a breaking change? So having both as options would be good so that users who don't want blocking are still able to get it?

I'm confused. @saskathex pointed out in his response that right now Send works like Post (non-blocking). This PR (I think) fixes Send so that it is blocking, but does not change the Post behavior. Thus, with this PR

  • Send is blocking
  • Post is non-blocking

...and therefore the blocking/non-blocking behavior IS optional.

Right?

Regardless, there's a merge error now AND a real need for unit tests.

@tig tig changed the title Fix: The sychronization context method send Fix: The synchronization context method Send is now blocking Jul 22, 2022
@tig
Copy link
Collaborator

tig commented Jul 26, 2022

The current implementation does not block the caller task until user input is done.

Is it worth making the blocking optional? I know winforms has Invoke and InvokeAsync where the first blocks and the second does not. If our current implementation is non blocking then changing it to blocking could be a breaking change? So having both as options would be good so that users who don't want blocking are still able to get it?

I'm confused. @saskathex pointed out in his response that right now Send works like Post (non-blocking). This PR (I think) fixes Send so that it is blocking, but does not change the Post behavior. Thus, with this PR

  • Send is blocking
  • Post is non-blocking

...and therefore the blocking/non-blocking behavior IS optional.

Right?

Regardless, there's a merge error now AND a real need for unit tests.

@saskathex - can you please chime in here?

@saskathex
Copy link
Contributor Author

Imo making the blocking optional is not way to go. The synchronization context Post/Send methods are generic over all ui frameworks and should be used the correct way. The current implementation is incorrect and should be fixed.

@BDisp
Copy link
Collaborator

BDisp commented Jul 26, 2022

Imo making the blocking optional is not way to go. The synchronization context Post/Send methods are generic over all ui frameworks and should be used the correct way. The current implementation is incorrect and should be fixed.

Thanks for your feedback. I already tried to get the Send method to be called in the Terminal.Gui but without success. Can you provide a working unit test or sample application where the Send method is invoked, please? Thanks.

By the way you have to resolve the conflicts for this been merged.

@tznind
Copy link
Collaborator

tznind commented Jul 27, 2022

Imo making the blocking optional is not way to go. The synchronization context Post/Send methods are generic over all ui frameworks and should be used the correct way.

Sorry for confusing things, I think I was confusing Post/Send with MainLoop.Invoke since that's what I've had the most experience with.

@saskathex
Copy link
Contributor Author

I will try to add some unit tests.

saskathex added 2 commits July 27, 2022 11:30
Send has to invoke the action instantly instead of enqueuing in case of main thread call.
@BDisp
Copy link
Collaborator

BDisp commented Jul 27, 2022

I will try to add some unit tests.

Thank you for the unit tests, great. Can you also provide a unit test for the CreateCopy method, please?
I confirm this never blocks the code execution. In practice the d (state) method will always executed normally by the else statement because the Thread.CurrentThread.ManagedThreadId rarely will be equal to _mainThreadId and the wasExecuted field will be true after the Mainloop adds the idle. If Thread.CurrentThread.ManagedThreadId == _mainThreadId runs the function and the called method, normally a task, decide what to do next.
Don't forget to press Ctrl+K+D to reformat the text if you are using VS2022 or the equivalent command if you are using VSCode.
Thanks again for this.

@BDisp
Copy link
Collaborator

BDisp commented Jul 27, 2022

Thanks @saskathex.

Copy link
Collaborator

@tig tig left a comment

Choose a reason for hiding this comment

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

Really great work. Thank you!

Terminal.Gui/Core/Application.cs Show resolved Hide resolved
@tig
Copy link
Collaborator

tig commented Aug 4, 2022

Is this PR good to go now? Can I merge it?

@BDisp
Copy link
Collaborator

BDisp commented Aug 4, 2022

Is this PR good to go now? Can I merge it?

For me yes.

@tig tig merged commit dc99fb3 into gui-cs:develop Aug 12, 2022
@saskathex saskathex deleted the saskathex-patch-send branch August 16, 2022 20:05
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.

4 participants