F# string concatenation hangs an async computation - f#

Here is a basic async computation but I cannot explain the behaviour and it drives me crazy!
let noConcatStr = "42"
let concatStr = "4" + "2"
let asyncConcat =
async {
printfn "before"
do! Async.Sleep 1000
printfn "%s" concatStr
printfn "after"
}
let run = asyncConcat |> Async.RunSynchronously
This trivial async code never returns and remains hung! If I switch concaStr with noConcatStr, the code returns as expected after 1s sleep.
There is certainly a rationale behind that behaviour that is due to System.String.Concat but I really cannot find it.
Insights much appreciated.

Related

Using Async for a function that requires wait time

This is the part of the function that causes me trouble. I have isolated this part from my main function after some debugging:
let testfunction view =
printfn "Entry point: %s" view
let df = ETS.getTimeSeries myrun.tsds (myrun.initiate2 "0019.HK" view defaultbegintime defaultendtime) "unusedlegacystring"
printfn "I am here"
df |> ETS.printRecords
let async1 = async{testfunction("BID")}
let async2 = async{testfunction("ASK")}
let gglist = [async1;async2] //Async<unit> list
ETS.getTimeSeries returns a Deedle frame from the internet which is saved to df.
ETS.printRecords is a helper function that prints the Deedle frame nicely.
The parameters are all correct. I know it from running:
Async.Start async1
// prints out the dataframe as expected. Same goes for async2 as well.
If I run:
gglist
|> Async.Parallel
|> Async.Ignore
|> Async.RunSynchronously
The output is only
Entry point: BID
Entry point: ASK
I suspect this may have something to do with the fact that ETS.getTimeSeries takes anywhere from 3 seconds to 1 minute to pull the data to a Deedle frame and that I am missing some sort of command to wait for getTimeSeries is finished.
As always, any help would be much appreciated, thank you.
Update: this may have something to do with the Eikon API itself not being "thread-safe"(???) (ETS.getTimeSeries). Someone had a solution in C#, and I am trying very hard to re-write it to F#. I will come back with a solution if possible, thanks
https://community.developers.refinitiv.com/questions/11837/eikon-net-api-accepts-2-3-time-series-requests-and.html
For anyone that is interested, here is the F# implementation of the API (which I copied from the web and claims no credit for):
let getTimeSeries (tsds:ITimeSeriesDataService) (setRequestParameters:SetDataRequestParameters) (ric:ReutersInstrumentCode) : seq<IData> =
let mutable records = Seq.empty<IData> // IData is seq<KeyValuePair>
let mutable transformer = Seq.empty<IData>
let frame = DispatcherFrame()
let dataReceivedCallback (dataChunk:DataChunk) =
//transformer <- DataChunk.Records.ToTickRecords()
records <- Seq.append records dataChunk.Records
frame.Continue <- dataChunk.IsLast |> not
()
(tsds.SetupDataRequest(ric)
|> setRequestParameters) // apply the function to the TimeSeriesDataRequestSetup object
//.WithFields([]).From(Nullable<DateTime> DateTime.Now).WithAdjustedPrice(true).WithNumberOfPoints(10).WithView("BID")
.OnDataReceived(Action<DataChunk> dataReceivedCallback)
.CreateAndSend()
|> ignore
Dispatcher.PushFrame(frame) // "Enters an execute loop"
records
Credit: https://gist.github.com/roguetrainer/59e4f4664528c40239f1564ef7d6dcee
Update2: Here is a really stupid (but also effective? solution)
Create 3 instances of ETS.getTimeSeries. Let's call them ETS.getTimeSeries1, ETS.getTimeSeries2 and ETS.getTimeSeries3
Manually hard code task1 with getTimeSeries1 and so on
[task1; task2; task3] |> Async.Parallel |> Async.Ignore |> Async.StartImmediate
//work as expected
I have a 16core/32thread Gen1 TR CPU, so I guess I can hard code up to 32 threads to fully take advantage of this....

Why does casting a value declaration instead of a function argument result in different behavior?

Why does casting a value declaration instead of a function argument result in different behavior?
The following operation hangs:
let duration = uint32 500
...
brick.DirectCommand.TurnMotorAtPowerForTimeAsync(motors, power, duration, breakEnabled) |> ignore
The following operation succeeds:
brick.DirectCommand.TurnMotorAtPowerForTimeAsync(motors, power, uint32 500, breakEnabled) |> ignore
What's the difference?
Code:
let volume = 100
let frequency = uint16 1000
let duration = uint32 500
let power = 100
let motors = OutputPort.B ||| OutputPort.C
let breakEnabled = false
let moveAsync = async {
let brick = Brick(UsbCommunication())
brick.ConnectAsync() |> ignore
brick.DirectCommand.TurnMotorAtPowerForTimeAsync(motors, power, duration, breakEnabled) |> ignore
}
Async.RunSynchronously moveAsync
What you're doing is rather strange, you should read up on how to use async workflow. That said, what I would expect - provided that your functions indeed return Async<'a> - is something like this:
let moveAsync =
async {
let brick = Brick(UsbCommunication())
do! brick.ConnectAsync() |> Async.Ignore
let! _ = brick.DirectCommand.TurnMotorAtPowerForTimeAsync(motors, power, duration, breakEnabled)
}
You want to use let! or do! to compose async workflows - if you have an Async<'a> and don't care about the return value, you can also use Async.Ignore to turn it into Async<unit> (rather than plain ignore).
Edit: To clarify why I gave this answer - I cannot even imagine a scenario where you would face the problem as stated.
But the code you posted clearly has problems that in my opinion make it impossible to really reason about what is going on. Like returning Tasks (that may have been started or not) inside an async workflow without waiting for them to complete. If what this code does meets your expectations, I feel it's only by coincidence.
My intuition is that once you sort out these problems, you'll see that whichever way you pass in the duration argument is fine.

Cancel a sub-block of an F# async workflow

I'm trying to make an async workflow, where there's a main async loop, which executes an async sub-block in each loop. And I want this async sub-block to be cancellable, but when it cancels then I don't want the main loop to cancel. I want it to continue, at the line after the do! subBlock.
The only method I see in Async that even has an acceptable signature (takes CancellationToken, returns something that can be converted to async) is Async.StartAsTask, but that seems to hang when canceled; in the below, it prints "cancelled" and then nothing else.
open System
open System.Threading
open System.Threading.Tasks
// runs until cancelled
let subBlock =
async {
try
while true do
printfn "doing it"
do! Async.Sleep 1000
printfn "did it"
finally
printfn "cancelled!"
}
[<EntryPoint>]
let main argv =
let ctsRef = ref <| new CancellationTokenSource()
let mainBlock =
//calls subBlock in a loop
async {
while true do
ctsRef := new CancellationTokenSource()
do! Async.StartAsTask(subBlock, TaskCreationOptions.None, (!ctsRef).Token)
|> Async.AwaitTask
printfn "restarting"
}
Async.Start mainBlock
//loop to cancel CTS at each keypress
while true do
Console.ReadLine() |> ignore
(!ctsRef).Cancel()
0
Is there any way to do this?
Whether the caller that starts and cancels the worker is an async too doesn't really affect this problem, since the worker is managed via its explicitly specified cancellation token.
Asyncs have three continutations: the normal one, which can return a value, one for exceptions, and one for cancellation. There are multiple ways to add a cancellation continuation to an async, such as Async.OnCancel, Async.TryCancelled, or the general Async.FromContinuations, which includes the exception case. Here's a program that has the desired output:
let rec doBlocks () =
async { printfn "doing it"
do! Async.Sleep 1000
printfn "did it"
do! doBlocks () }
let rec runMain () =
use cts = new CancellationTokenSource()
let worker = Async.TryCancelled(doBlocks (), fun _ -> printfn "Cancelled")
Async.Start(worker, cts.Token)
let k = Console.ReadKey(true)
cts.Cancel()
if k.Key <> ConsoleKey.Q then runMain ()
This works just as well if runMain is an async. In this simple case, you could also just have it print the "cancelled" message itself.
I hope this helps. I don't think there is a general answer to how to structure the program; that depends on the concrete use case.
What happens here is that when your child task is cancelled, the OperationCanceledException brings down your mainBlock as well. I was able to get it to work by using this:
let rec mainBlock =
async {
ctsRef := new CancellationTokenSource()
let task = Async.StartAsTask(subBlock, TaskCreationOptions.None, (!ctsRef).Token) |> Async.AwaitTask
do! Async.TryCancelled(task, fun e ->
(!ctsRef).Dispose()
printfn "restarting"
Async.Start mainBlock)
}
When the task is cancelled, mainBlock is explicitly restarted in the cancelation handler. You need to add #nowarn "40" for it since mainBlock is used inside its definition. Also note the dispose on token source.
You can find more information on this problem (and perhaps a nicer solution in the form of StartCatchCancellation) in these two threads.

F# async ; Run asynch expression in same thread, and yet be able to wait on async operations (e.g. do!)

Experimenting some with F# async taught me that I can StartImmediate on the current running thread. This allows me, it seems, to run an async expression which can still pass out control, whenever getting inside of it to some async operation (e.g. do!), to the code outside of the async expression. Please see the simple example below:
open System.Threading
let workThenWait() = async {
printfn "async start"
do! Async.Sleep(1000)
printfn "async end"
}
let demo() =
workThenWait() |> Async.StartImmediate
printfn "main started"
// here I want to wait to the async expression in case it has passed control
printfn "main end"
demo()
The result we get is:
async start
main started
main end
async end
On the other hand, if I execute the same async expression (in this case workThenWait) using StartAsTask (inside demo), I can potentially wait on it at the end.
MY QUESTION is:
using the previous example using StartImmediate, can I run on the same thread, but ALSO wait at the end for the async expression in case some async operation (such as do!) is called and passes control forward?
I think You need Async.RunSynchronously (http://msdn.microsoft.com/en-us/library/ee370262.aspx)
update:
Ok, now I understand better what You want, and I was able to achieve this with Async.StartWithContinuations method.
Here's the code:
open System.Threading
let f() =
printfn "main thread: %A" Thread.CurrentThread.ManagedThreadId
let c1 =
async {
printfn "c1 async thread: %A" Thread.CurrentThread.ManagedThreadId
do! Async.Sleep(1000)
return "some result"
}
let continuation s =
printfn "continuation thread: %A" Thread.CurrentThread.ManagedThreadId
printfn "now the code You want after waiting and the result %s" s
Async.StartWithContinuations(
c1,
continuation,
(fun _ -> ()),
(fun _ -> ())
)
printfn "Code that runs during async computation"
Now this is definitely not very readable as the flow of the code is not obvious. I couldn't find any better solution.
You can do this with Hopac libary:
let workThenWait() = job {
printfn "async start"
do! Hopac.Timer.Global.sleep (TimeSpan.FromMilliseconds 1000.)
printfn "async end"
}
let demo() =
let promise = workThenWait() |> Promise.start |> run
printfn "main started"
// here I want to wait to the async expression in case it has passed control
let result = run promise
printfn "main end"
demo()
Hopac is both more performant and functional than async and is little known compared to how good it is. I highly recommend it.

How do I use an async workflow in a Seq.pick in F#

I am new to functional programming in general and started learning F# recently. I wanted to use an async workflow returning Async<'U option> to pick an item in a Sequence. I find a nice Seq.pick function, but I am not sure how I could use that with an async workflow.
If that is not possible, is there another alternative to using an imperative style program to pick the item from the list. The following is a modified variation of my program. Any feedback is highly appreciated.
let run = async {
while not stopped do
use! resource = acquireResourceLockAsync
let! items = fetchItemsAsync 5
let! item = Seq.pick returnIfLocked items
let! status = performTaskAsync item
do! updateStatusAsync status
do! Async.Sleep 1000
}
Thanks in anticipation.
EDIT: Updated my question based on the answer by jpalmer. I noticed both Seq.filter and Seq.pick earlier and decided that Seq.pick will meet my need better, as I need the first item that I am able to lock. However, I forgot to change the return value of my function - instead of returning true, it should return Some(item). Now with that update, is there an elegant way to approach this without 1) blocking a thread to convert Async<'U option> to 'U and 2) resorting to an imperative style looping?
I am unclear exactly what you are trying to do. If you want to convert from Async<'T> to 'T non-blocking, then you want to use let! in an async workflow. So the seq-like logic probably needs to be written as its own loop, as suggested below. If that doesn't help, then perhaps share more code, especially the intended types of items/item/returnIfLocked, as I'm unclear what's async in your example.
let asyncPick f (s:seq<_>) =
async {
use e = s.GetEnumerator()
let r = ref None
while Option.isNone(!r) && e.MoveNext() do
let! x = f e.Current
r := x
match !r with
| Some z -> return z
| None -> return failwith "no matching item found"
}
let chooser ax =
async {
let! x = ax
if x%3 = 0 then
return Some x
else
return None
}
let s = seq { for i in 1..10 do yield async { return i } }
let main() =
async {
let! firstChosen = s |> asyncPick chooser
return firstChosen
}
|> Async.RunSynchronously
|> printfn "%d"
main()
It is important to look at the signature of the function you are using,
Seq.pick expects a function which returns option<'t>, you want to use Seq.Filter which takes a function which returns a bool.
You will still have another problem though in that you have Async<bool> - you will need to convert that to a normal bool, but you could do this inside your 'Seq.Filter' function

Resources