Skip to content

[FS-1028] - Implement Async.StartImmediateAsTask - #2534

Merged
KevinRansom merged 2 commits into
dotnet:dev15.6from
OnurGumus:master
Dec 15, 2017
Merged

[FS-1028] - Implement Async.StartImmediateAsTask#2534
KevinRansom merged 2 commits into
dotnet:dev15.6from
OnurGumus:master

Conversation

@OnurGumus

Copy link
Copy Markdown
Contributor

Feature implemented.

@dsyme

dsyme commented Mar 7, 2017

Copy link
Copy Markdown
Contributor

The code looks ok though the tests indicate some failures?

@OnurGumus

Copy link
Copy Markdown
ContributorAuthor

Yes kinda weird. The test failing is
Conformance\InferenceProcedures\TypeInference (E_OnOverloadIDAttr01.fs) -- failed
I have no clue why. Not related to my work. I only ran the test named "test" locally and that passed. Could this be already broken even before my commit?

@dsyme

dsyme commented Mar 7, 2017

Copy link
Copy Markdown
Contributor

@OnurGumus I'll close and reopen to re-run tests. But I think more tests must be failing - usually each of part1, part2 and part3 indicate different failures

For example I expect you will need to update the API surface area tests?

@OnurGumus

Copy link
Copy Markdown
ContributorAuthor

I will check it again, but that's gonna be on the weekend again:)

@KevinRansom

Copy link
Copy Markdown
Contributor

@dotnet-bot test this please

@OnurGumus

Copy link
Copy Markdown
ContributorAuthor

Sorry for not dealing with this yet. I forgot to update surface area tests. I will check it asap.

@forki

Copy link
Copy Markdown
Contributor

ping

@OnurGumus

Copy link
Copy Markdown
ContributorAuthor

I just want to highlight a potential problem that also exists for Async.StartImmediate. For example:


[<EntryPoint>]
let main argv = async {
printfn "1- %A" (Thread.CurrentThread.ManagedThreadId)
do! Async.Sleep 1000
printfn "2- %A" (Thread.CurrentThread.ManagedThreadId)
do! Async.Sleep 1000
printfn "3- %A" (Thread.CurrentThread.ManagedThreadId)
do! Task.Delay(1000) |> Async.AwaitTask
printfn "4- %A" (Thread.CurrentThread.ManagedThreadId)
do! Async.Sleep 1000
printfn "5- %A" (Thread.CurrentThread.ManagedThreadId)
do! Task.Delay(1000) |> Async.AwaitTask
printfn "6- %A" (Thread.CurrentThread.ManagedThreadId)
return ()
} |> Async.StartImmediate |> ignore
System.Console.ReadLine()
0

If we use async everywhere then everything works perfectly. That is no thread switch occurs. However if start to use System.Threading.Tasks.Task in between as in above example, then a thread switch occurs. The async module is a little bit complicated for me with a lot of "trambolines" involved.

@dotnetdotnet deleted a comment from msftclasSep 27, 2017
@dsyme

Copy link
Copy Markdown
Contributor

If we use async everywhere then ...no thread switch occurs.

@OnurGumus I think it's just luck or which thread pool thread gets chosen. When i tried it I got

1- 1
2- 4
3- 4
4- 5
5- 4
6- 4

@OnurGumus

OnurGumus commented Sep 27, 2017

Copy link
Copy Markdown
ContributorAuthor

@dsyme Which example have you tried? If you have used above example of course a thread switch occurs because tasks are involved. But the key is if you try below:

[<EntryPoint>]
let main argv = async {
printfn "1- %A" (Thread.CurrentThread.ManagedThreadId)
do! Async.Sleep 1000
printfn "2- %A" (Thread.CurrentThread.ManagedThreadId)
do! Async.Sleep 1000
printfn "3- %A" (Thread.CurrentThread.ManagedThreadId)
do! Async.Sleep 1000
printfn "4- %A" (Thread.CurrentThread.ManagedThreadId)
do! Async.Sleep 1000
printfn "5- %A" (Thread.CurrentThread.ManagedThreadId)
do! Async.Sleep 1000
printfn "6- %A" (Thread.CurrentThread.ManagedThreadId)
return 1
} |> Async.StartImmediateAsTask |> ignore // or Async.StartImmediate with return ()
System.Console.ReadLine()
0

Then no thread switch occurs as there are no tasks involved. Hence we achieve our goal.
To clarify the confusion, my pull request introduces a brother method to StartImmediate that has the same characteristics. That is StartImmediate must return unit where as StartImmediateAsTask can return something.

@dsyme

Copy link
Copy Markdown
Contributor

Then no thread switch occurs as there are no tasks involved.

Thread switches occur in your sample - the Async.Sleep calls are ultimately implemented via by a timer callback which may get scheduled on a different thread (unless the originating code was a thread with a non-null SynchronizationContext, e.g. a UI thread).

When I compile this code

open System.Threading
open System.Threading.Tasks
[<EntryPoint>]
let main argv = async {
printfn "1- %A" (Thread.CurrentThread.ManagedThreadId)
do! Async.Sleep 1000
printfn "2- %A" (Thread.CurrentThread.ManagedThreadId)
do! Async.Sleep 1000
printfn "3- %A" (Thread.CurrentThread.ManagedThreadId)
do! Async.Sleep 1000
printfn "4- %A" (Thread.CurrentThread.ManagedThreadId)
do! Async.Sleep 1000
printfn "5- %A" (Thread.CurrentThread.ManagedThreadId)
do! Async.Sleep 1000
printfn "6- %A" (Thread.CurrentThread.ManagedThreadId)
return ()
} |> Async.StartImmediate |> ignore // or Async.StartImmediate with return ()
System.Console.ReadLine()
0

I get

C:\GitHub\dsyme\visualfsharp>a
1- 1
2- 4
3- 4
4- 5
5- 4
6- 5

@OnurGumus

Copy link
Copy Markdown
ContributorAuthor

@dsyme : Are you sure? Because that's weird. I am getting

1- 1
2- 4
3- 4
4- 4
5- 4
6- 4

repeatedly every time. As a proof see below gif file:

fsharp

I am using .NET 4.7 with F# 4.1 Release build 64 bit (though other configurations also yield the same result).

@dsyme

Copy link
Copy Markdown
Contributor

@OnurGumus That's just luck with regard to the thread pool threads (e.g. thread 4) used for the timer callbacks.

@OnurGumus

Copy link
Copy Markdown
ContributorAuthor

@dsyme , I see. Then my preliminary assumption was incorrect. Nevertheless, as I stated, this function is still valuable and complementary to StartImmediate, as it ensures, it starts a task immediately and when done, it returns the task itself. Though not sure what I should do to complete this pull request.

@dsyme

Copy link
Copy Markdown
Contributor

@OnurGumus The PR is ready - you don't have to do anything more. It's just a question of when we're willing to rev FSharp.Core (and if we rev the version number etc.)

@dsymedsyme changed the title [RFC FS-1028] - Implement Async.StartImmediateAsTask[FS-1028] - Implement Async.StartImmediateAsTaskDec 1, 2017
@KevinRansom
KevinRansom changed the base branch from master to dev15.6December 7, 2017 19:22
@KevinRansom
KevinRansom merged commit 4054701 into dotnet:dev15.6Dec 15, 2017
T-Gro pushed a commit that referenced this pull request Jun 12, 2026
Sign up for freeto 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.

6 participants

@OnurGumus@dsyme@KevinRansom@forki@msftclas@msftgits