question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Deadlocking interop

See original GitHub issue

Greetings. As it happens, I’m wrapping the WinRM API. Unfortunately I’ve run into a road-block.

The API has 1) Begin-/End-Invoke, 2) (ps : PowerShell).Streams.Error.{DataAdding,Completed}. I’m trying to join the EndInvoke with the Completed event of the Error stream, but for some number of runs; about half of them; everything freezes up.

I have code like this:

// suspicious function 
    let streamOf n (psdc : PSDataCollection<_>) =
      let mb = Mailbox ()
      let stop = IVar ()
      psdc.DataAdding.Add <| fun args ->
        printDataAdding n args
        mb *<<+ args |> start
      psdc.Completed.Add <| fun args ->
        printCompleted n args
        stop *<= () |> start
      Stream.indefinitely mb
      |> Stream.takeUntil stop

// a little later in the file:

      job {
        try
          let outputs = subscribeStreams ps returned

          let! res = Job.fromBeginEnd (beginInvoke ps) ps.EndInvoke

          if ps.HadErrors then
            printfn "PowerShell had errors; draining stream"
            let! errors =
              outputs.error
              |> Stream.foldFromBack (Job.result [])
                                     (fun s t -> s |> Job.map (fun s -> t :: s))
            do! rI *<= Choice.createSnd errors

          else
            printfn "PowerShell succeeded; draining stream"
            let objects =
              Seq.foldBack (fun t s -> t :: s) res []

            do! rI *<= Choice.create objects
        with e ->
          //printfn "in job, writing transport exception"
          do! rI *<=! e
        }

// tested with
node
|> WindowsNode.runCommand ("Write-Error", ["We know, we know"])
|> run

// trace output
WindowsNode: (cb) availability: Busy
PowerShell: E.DataAdding(
  PowerShellInstanceId=9c113bd1-34dc-48e3-9b0a-648deecab88f,
  ItemAdded=We know, we know
)
WindowsNode: (cb) availability: Available
PowerShell had errors; draining stream
// nothing happens here

In short, the draining of the stream depends on

psdc.Completed.Add <| fun args ->
  printCompleted n args
  stop *<= () |> start

Firing before the job scheduler blocks on the Stream fold; otherwise the stream is never terminated and cannot be folded. However, despite only having a single run, at the topmost level (albeit with desktop GC), it would seem that is never fired.

It can be circumvented by passing a p: Promise<unit> to make it Stream.takeUntil (stop <|> p) and then giving the promise a unit value after EndInvoke has returned; but then in about half of the cases, the output is never fully captured.

Is this a scheduler issue?

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:15 (11 by maintainers)

github_iconTop GitHub Comments

2reactions
ghostcommented, Jul 14, 2016

Wow, my man. Well spotted. Resolving that cycle made it work. I wonder why the error stream doesn’t close until Dispose is called; presumably the error output would be contained in time for a given invocation in PS…

0reactions
polytypiccommented, Jul 14, 2016

fold is a structure destroying operation that’s by definition eager; right? It will try to consume the whole stream if I block on it?

The foldBack and foldFromBack stream combinators are lazy and, depending on the given combiner function, they do not necessarily consume the whole stream. For example, you could write a function to extract the head of a stream like this:

let head xs =
  xs
  |> Stream.foldFromBack (Job.raises (Exception "Empty")) (fun _ x ->
       Job.result x)

The above stream combinator, head, only examines at most the first Cons of the given stream xs.

In this case,

            let! errors =
              outputs.error
              |> Stream.foldFromBack (Job.result [])
                                     (fun s t -> s |> Job.map (fun s -> t :: s))

the computation proceeds in such a way that the entire stream is first deconstructed and a sequence of Job.map operations is built and finally the sequence of operations is executed. It would likely be faster to use an ordinary fold and list reversal.

However, in retrospect, the laziness of foldFromBack could not possibly have been a cause of the problem in this case.

Read more comments on GitHub >

github_iconTop Results From Across the Web

WPF interop deadlock on DisplaySettingsChanging
1 Answer 1 ... This is a program initialization problem. Watch out for custom splash screens. If the first event subscription doesn't happen...
Read more >
Blazor Server: Deadlock when JavaScript-Interop is called ...
Running the code in this order (first StateHasChanged() then something with JS-Interop) causes Blazor Server to deadlock pretty often.
Read more >
Race Conditions, Deadlocks - Pritom Purkayasta
When two or more threads access and trying to change the data at the same time, we call it Race Condition. Because of...
Read more >
Microsoft.Office.Interop.Excel's merge method when ...
Interop.Excel.Range. ... because Office may exhibit unstable behavior and/or deadlock when Office is run in this environment.
Read more >
Details of Lock Requests and Deadlocks | Using ObjectScript
This topic provides more detailed information on how lock requests are handled in InterSystems IRIS®, as well as a detailed look at deadlock...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found