FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
Plotly.NET/src/Plotly.NET.ImageExport/AsyncHelper.fs at dev · plotly/Plotly.NET · GitHub
Uh oh!
There was an error while loading.
Please reload this page
.
plotly
/
Plotly.NET
Public
Notifications
You must be signed in to change notification settings
Fork
99
Star
859
Code
Issues
42
Pull requests
3
Discussions
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Discussions
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
Plotly.NET
/
src
/
Plotly.NET.ImageExport
/
AsyncHelper.fs
Copy path
More file actions
More file actions
Latest commit
History
History
History
78 lines (58 loc) · 2.71 KB
Breadcrumbs
Plotly.NET
/
src
/
Plotly.NET.ImageExport
/
AsyncHelper.fs
Copy path
File metadata and controls
78 lines (58 loc) · 2.71 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
module
Plotly.NET.ImageExport.AsyncHelper
open
System.
Threading
open
System.
Threading
.
Tasks
(*
This is a workaround to avoid deadlocks
https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d
TL;DR in many cases, for example, GUI apps, SynchronizationContext
is overriden to *post* the executing code on the initial (UI) thread. For example,
consider this code
public async Task OnClick1()
{
var chart = ...;
var base64 = ImageExport.toBase64PNGStringAsync()(chart).Result;
myButton.Text = base64;
}
Here we have an async method. Normally you should use await and not use .Result, but
assume for some reason the sync version is used. What happens under the hood is,
public async Task OnClick1()
{
var chart = ...;
var task = ImageExport.toBase64PNGStringAsync()(chart);
task.ContinueWith(() =>
UIThread.Schedule(() =>
myButton.Text = Result;
)
);
task.Wait();
}
(this is pseudo-code)
So basically, we set the task to wait until it finishes. However, part of it being
finished is to actually execute the code with button.Text = .... The waiting happens
on the UI thread, exactly on the same thread as where we're waiting for it to do
another job!
That's not the only place we potentially deadlock by using fake synchronous functions.
The reason why it happens, is because frameworks (or actually anyone) override
SynchronizationContext. In GUI and game development it's very useful to keep UI logic
on one thread. But our rendering does not ever callback to it, we're independent of
where the logic actually happens.
That's why what we do is we set the synchronization context to null, do the job, and
then restore it. It is a workaround, because it doesn't have to work everywhere and
independently. But it will work for most cases.
When will it also break? For example, if we decide to take in some callback as a para-
meter, which potentially accesses the UI thread (or whatever). In Unity, for instance,
you can only access Unity API from the main thread. So our fake synchronous function
will crash in the end, because due to the overriden (by us) sync context, the callback
will be executed in some random thread (as opposed to being posted back to the UI one).
However, our solution should work in most cases.
Credit to [@DaZombieKiller](https://github.com/DaZombieKiller) for helping.
*)
let
runSync
job input
=
let
current
=
SynchronizationContext.Current
SynchronizationContext.SetSynchronizationContext
null
try
job input
finally
SynchronizationContext.SetSynchronizationContext current
let
taskSync
(
task
:
Task
<
'a
>)
=
task
|>
runSync
(
fun
t
->
t.Result
)
let
taskSyncUnit
(
task
:
Task
)
=
task
|>
runSync
(
fun
t
->
t.Wait
())
Back
|
FazBrowse Home
|
New Git URL