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

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

Merged
merged 2 commits into from Dec 15, 2017

Conversation

OnurGumus
Copy link
Contributor

Feature implemented.

@dsyme
Copy link
Contributor

dsyme commented Mar 7, 2017

The code looks ok though the tests indicate some failures?

@OnurGumus
Copy link
Contributor Author

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
Copy link
Contributor

dsyme commented Mar 7, 2017

@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?

@dsyme dsyme closed this Mar 7, 2017
@dsyme dsyme reopened this Mar 7, 2017
@OnurGumus
Copy link
Contributor Author

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

@KevinRansom
Copy link
Member

@dotnet-bot test this please

@OnurGumus
Copy link
Contributor Author

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

@forki
Copy link
Contributor

forki commented Sep 26, 2017

ping

@OnurGumus
Copy link
Contributor Author

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.

@dotnet dotnet deleted a comment from msftclas Sep 27, 2017
@dsyme
Copy link
Contributor

dsyme commented Sep 27, 2017

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
Copy link
Contributor Author

OnurGumus commented Sep 27, 2017

@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
Contributor

dsyme commented Sep 27, 2017

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
Contributor Author

@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
Contributor

dsyme commented Sep 28, 2017

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

@OnurGumus
Copy link
Contributor Author

@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
Contributor

dsyme commented Sep 29, 2017

@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.)

@dsyme dsyme changed the title [RFC FS-1028] - Implement Async.StartImmediateAsTask [FS-1028] - Implement Async.StartImmediateAsTask Dec 1, 2017
@KevinRansom KevinRansom changed the base branch from master to dev15.6 December 7, 2017 19:22
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.

None yet

6 participants