Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does IDisposable work with use and return?

Tags:

In F# async workflows, we can define a resource that should be cleaned up with the use keyword.

But how does use interact with return?

For example, given this code:

let createResource = async {
  use r = Resource ()

  do! operationThatMightThrow r

  return r
}

async {
  use! r = createResource

  printfn "%O" r
}
|> Async.RunSynchronously

Where will the calls to Resource.Dispose happen?

How can I design this so that the r is always cleaned up (even if operationThatMightThrow throws)?

like image 832
sdgfsdh Avatar asked May 24 '19 19:05

sdgfsdh


2 Answers

I usually have two solutions.

The first solution is actively capturing the exception, manually disposing the disposable object, and re-throwing the exception:

let createResource = async {
    let r = new Resource ()
    try do! operationThatMightThrow r
    with e -> (r :> IDisposable).Dispose(); raise e
    return r
}

The second solution is to use a continuation function that will have access to the disposable object before the async returns:

let createResource cont = async {
    use r = new Resource ()
    do! operationThatMightThrow r
    return cont r
}

async {
    let! x = createResource (fun r -> printfn "in cont: %O" r)
    ...
}
like image 180
Nghia Bui Avatar answered Sep 18 '22 23:09

Nghia Bui


They will occur before the value is returned from the computation expression, semantically they will happen in a finally block. If you want to look at the source of the generated using statement, you can find it here. It effectively generates an access-controlled dispose function that calls Dispose() on the resource you pass in, then makes an asynchronous try-finally block with that function in the finally clause.

like image 33
Chester Husk Avatar answered Sep 17 '22 23:09

Chester Husk