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 581 - Async.RunSynchronously tries to reuse current thread #678
Conversation
Hi @radekm, I'm your friendly neighborhood Microsoft Pull Request Bot (You can call me MSBOT). Thanks for your contribution! The agreement was validated by Microsoft and real humans are currently evaluating your PR. TTYL, MSBOT; |
// Thread.IsThreadPoolThread isn't available on all profiles so | ||
// we approximate it by testing synchronization context for null. | ||
match SynchronizationContext.Current with | ||
| null when Option.isNone timeout -> RunSynchronouslyInCurrentThread (token, computation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not match timeout
as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I'll fix that.
688b9ee
to
5a27ae9
Compare
This enhancement looks correct to me. Would welcome additional reviewers. |
[| for i in 1 .. 1000 -> action |] | ||
|> Async.Parallel | ||
Async.RunSynchronously(computation, timeout = 1000) | ||
|> ignore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this test doesn't assert anything, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, there is no assert. The purpose of this test is to ensure that the computation completes quickly (originally it took more than 1 minute with 100 actions - now there are 1000 actions and it takes less than 1 second) - otherwise TimeoutException
would be raised.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok so the assertion is "no timeout" that's pretty nice, but maybe a small
comment for clarification would help.
On Dec 2, 2015 22:13, "radekm" notifications@github.com wrote:
In
src/fsharp/FSharp.Core.Unittests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs
#678 (comment):@@ -80,6 +80,15 @@ type AsyncType() =
()[<Test>]
- member this.AsyncRunSynchronouslyReusesThreadPoolThread() =
let action = async { async { () } |> Async.RunSynchronously }
let computation =
[| for i in 1 .. 1000 -> action |]
|> Async.Parallel
Async.RunSynchronously(computation, timeout = 1000)
|> ignore
Yes, there is no assert. The purpose of this test is to ensure that the
computation completes quickly (originally it took more than 1 minute with
100 actions - now there are 1000 actions and it takes less than 1 second) -
otherwise TimeoutException would be raised.—
Reply to this email directly or view it on GitHub
https://github.com/Microsoft/visualfsharp/pull/678/files#r46476056.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I'll add it.
5a27ae9
to
c8b3c56
Compare
I have improved the test |
// Thread.IsThreadPoolThread isn't available on all profiles so | ||
// we approximate it by testing synchronization context for null. | ||
match SynchronizationContext.Current, timeout with | ||
| null, None -> RunSynchronouslyInCurrentThread (token, computation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In console applications SynchronizationContext.Current
will be null
for the main application thread. How will that affect this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those applications will now run the computation on the main thread (instead of blocking that thread and running the application on a background thread). That is, I believe, intended.
This looks great. |
Fix 581 - Async.RunSynchronously tries to reuse current thread
Fixes #581 -
Async.RunSynchronously
tries to reuse the currentThreadPool
thread for running the computation (originally the computation was always performed in anotherThreadPool
thread).Currently there are two problems:
The code doesn't use
Thread.IsThreadPoolThread
since it isn't available in all profiles (eg. in profile 47). Instead value ofThread.IsThreadPoolThread
is approximated bySynchronizationContext.Current = null
.Current
ThreadPool
thread is reused only if no timeout was given toAsync.RunSynchronously
. The reason is that some thread is needed to cancel the computation when the time runs out and the current thread seems as a good candidate for this task. In some profiles I could useSystem.Threading.Timer
orCancellationTokenSource.CancelAfter
(internally usesTimer
) but they don't ensure that cancellation is performed whenThreadPool
is busy. For example following coderequests cancellation after 2 seconds but cancellation actually happens after 193 seconds on my machine and if the
ThreadPool
reaches its maximal capacity (eg. byThreadPool.SetMaxThreads(4, 4)
) then the cancellation never happens.The third option is to create a single special thread which will be dedicated for cancellation (ie. for calling
CancellationTokenSource.Cancel
). This special thread could be used by all calls toAsync.RunSynchronously
.