Giter Site home page Giter Site logo

Consuming events about hopac HOT 2 CLOSED

hopac avatar hopac commented on May 21, 2024
Consuming events

from hopac.

Comments (2)

polytypic avatar polytypic commented on May 21, 2024

Cool to see that you are trying out choice streams! It is unfortunate, but the documentation on them is not yet ready. It is also cool to see that you are working on stuff like the process runner. These will make nice practical examples on how to use Hopac.

Basically, you can think of streams as a kind of asynchronous communication channel, similar to multicast channels, that are suitable for one-to-many and many-to-many communication, where each consumer gets to see all the messages and can keep as much of the communication history as desired (usually, though, consumers throw away messages as they consume them).

Regarding the streams usage, I think that in some scenarios they could be a good model for the clients of a process runner. In more complex scenarios streams can also be a good model to use internally, but I think that in this case streams are not perhaps the simplest approach. In this case, I think that a simple approach would be to offer the following kind of interface:

[<NoComparison>]
type RunningProcess = 
    { LineOutput: Alt<Line>
      ProcessExited: Alt<ExitResult> }

As you can see, there is no mention of streams above. The idea above is simply that LineOutput essentially reads from an internal mailbox and ProcessExited reads an internal ivar. execute would then add handlers to write to the mailbox and the ivar:

let execute (p: Process) : RunningProcess =
    let lineOutput = mb ()
    let processExited = ivar ()

    p.OutputDataReceived.Add <| fun args ->
      lineOutput <<-+ args.Data |> start
    p.Exited.Add <| fun _ ->
      processExited <-= Exited p.ExitCode |> start

    if not <| p.Start () then
      failwithf "Cannot start %s." p.StartInfo.FileName

    p.BeginOutputReadLine ()

    { LineOutput = lineOutput
      ProcessExited = processExited }

Note that the above example doesn't remove the event handlers from the process object. That should not be a problem unless one tries to reuse process objects rather than just let them be GC'ed.

BTW, using start like above, to execute a single message passing operation, guarantees that the operations can be/are observed in the order in which the events are triggered. (If you would use queue the lines could be sent to the mailbox in some non-deterministic order.)

Now, a client of that interface can, if so desired, wrap the LineOutput as a stream by saying:

let lineStream: Streams<Line> =
  Streams.foreverJob rp.LineOutput
  |> Streams.takeUntil rp.ProcessExited

The stream bound to the variable lineStream can be used to read all the lines of output assuming that it is the only direct consumer of the LineOutput. The use of Streams.takeUntil also makes it so that the stream ends once the process has been terminated. In many cases it would likely not be necessary to explicitly close the stream.

What you would normally do with the lineStream stream would then be to combine that with some more complex stream processing. For just consuming the output once, streams are overkill.

It is also possible to go from a stream to an alternative:

let lineAlt: Alt<Line> =
  Streams.values lineStream

The elements from the stream can be read one by one from the alternative returned by Streams.values as they are generated. If you look at the implementation of multicast channels (also see CML book), you can see that a combination of a stream and alternative created using Streams.values is almost exactly like a multicast channel with a port. Just like with multicast channels, Streams.values can be used any number of times to create independent "ports" to read the stream. Hopac code can quite flexibly switch between and combine streams and other message passing operations.

from hopac.

vasily-kirichenko avatar vasily-kirichenko commented on May 21, 2024

Thanks! You are right, it seems that streams are overkill here, so I've ended up with RunningProcess, even your lineStream is not needed. I also added timeout helper https://github.com/Hopac/Hopac.Extras/blob/master/src/Hopac.Extras/ProcessRunner.fs#L79-L82:

let timeoutAlt timeout = Alt.delay <| fun _ ->
    match (startTime + timeout) - DateTime.UtcNow with
    | t when t > TimeSpan.Zero -> Timer.Global.timeOut t
    | _ -> Alt.always()

Client can synchronize on it (as well as on lineOutput and exited), then decide what to do with the process - kill it or something else. Oh, wait. I think I can create a special channel for this and do the same logic as here https://github.com/Hopac/Hopac.Extras/blob/master/src/Hopac.Extras/ObjectPool.fs#L31-L32 Will rewrite it.

About Streams, yes, a well documented example of their usage is very much appreciated.

from hopac.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.