Giter Site home page Giter Site logo

Comments (12)

ocharles avatar ocharles commented on June 2, 2024 1

Good stuff, I'm glad we got that cleared up! If you have any other questions, don't hesitate to open an issue and we can help clarify. I'm glad to hear more people are playing around with reactive-banana 😄

from reactive-banana.

luke-clifton avatar luke-clifton commented on June 2, 2024

This is similar to

https://hackage.haskell.org/package/threepenny-gui-0.8.3.0/docs/src/Graphics.UI.Threepenny.Timer.html#timer

I notice that the timer there starts off disabled. Which works around the problem somewhat.

I think I want some sort of runIoAfterNetworkUpdate function, though I think queuing the event up might be more intuitive.

from reactive-banana.

ocharles avatar ocharles commented on June 2, 2024

This isn't really supported by reactive-banana. A liftIOLater call happens while the network is locked, meaning any other entries into the network will block. In your case (without forkIO) this re-entry happens from the same thread that holds the lock and will thus deadlock.

As this isn't supported by RB it may be a good time to talk more concretely about what it is you want to do, as there may be other options. IME firing events from inside MomentIO has always backfired, and I usually settle on something else. Maybe this will apply here!

from reactive-banana.

luke-clifton avatar luke-clifton commented on June 2, 2024

So, my use-case is that I want to trigger something in my framework, which will then go off and do some stuff, and then report back when finished by firing the event. The "timer" as in the threepenny-gui link above is pretty typical of what I'm trying to achieve and seems to be basically the same thing as I posted here.

It seems though, if the framework responds too quickly, the event might be missed.

The alternative seems to be to set up the network, and then trigger the framework into doing it's thing by using a different Event in combination with execute to actually start the frameworks thing. But that seems really awkward as sometimes I really do just want it to start immediately, and don't have a handy Event lying around to use for that purpose.

from reactive-banana.

ocharles avatar ocharles commented on June 2, 2024

I'm afraid I'm still not quite understanding what you want to do. Taking the Timer example, what I would do is something more like:

data Timer
withTimer :: MonadUnliftIO m => (Timer -> m a) -> m a
watchTimer :: Timer -> MomentIO (Event ())

So outside my network definition I would use withTimer to create the timer, and then I would pass a Timer into my network definition and use watchTimer to get an Event (). watchTimer would likely be fromAddHandler and withTimer would essentially be withAsync, creating a thread that ticks every n ms.

This API works for a statically defined number of timers. If you wanted that to be dynamic things get a bit trickier, but I would probably have a TimerManager object that I can request Timers from (so TimerManager owns Timers and manages their threads)

It seems though, if the framework responds too quickly, the event might be missed.

I'm not sure I understand this. "Too quickly" seems odd, because there's not really any racing or anything in reactive-banana, rather an event either takes an exclusive lock of the network and progresses it, or blocks on the lock until other threads are finished.

But that seems really awkward as sometimes I really do just want it to start immediately, and don't have a handy Event lying around to use for that purpose.

I also don't understand this. Specifically, what do you mean by "I don't have an Event lying around"? I could only really see that happening at the moment you create your initial network (or maybe when you use execute?)

from reactive-banana.

luke-clifton avatar luke-clifton commented on June 2, 2024

Sorry, that was all a bit unclear.

Yes, in the static case, it's easy enough, but in the dynamic case (i.e. creating new timers in an execute) it gets hard.

Basically, the problem I have is that I can get my hands on the Handler before I'm allowed to use it.

I have to wait until the network has finished being updated before I'm allowed to trigger the Handler. (The original code demonstrates that problem, but to make it clearer, use fire () >> threadDelay 1000 >> fire () and notice that the first fire is simply dropped because the network is not yet ready to receive it).

In the dynamic case I can't (easily) know when the network is ready (I can in the static case because I manually call compile and actuate). The best I can do in the dynamic case is have an event that happens after the event that triggered the execute trigger the start off the timer.

So, I have one Event (MomentIO ()) which I feed to execute, somewhere in that MomentIO () I have called newEvent and gotten a Handler, but it's not safe for me to use it yet. I can't ship it off to some other thread at this point, or some TimerManager, because that also won't know when it's safe. The only thing I can do is have another event trigger some other IO action (which was registered in the new network) which hands of the handler. Because this IO action occurred in the new network, I know the network is ready, and it is safe to use the Handler. But that is really convoluted.

So, I guess, in general, how do you know when it is safe to use a Handler if it was created in an execute?


For the timer problem I could just have a single Event Unique that was used by all timers, and just generate new Uniques in the MomentIO instead of newEvents and have a single Handler for all timers and filter on the Unique. But I'm looking for a more general solution of how to handle newEvent calls in an execute.

from reactive-banana.

luke-clifton avatar luke-clifton commented on June 2, 2024

I'm not sure I understand this. "Too quickly" seems odd, because there's not really any racing or anything in reactive-banana, rather an event either takes an exclusive lock of the network and progresses it, or blocks on the lock until other threads are finished.

That isn't what I'm witnessing though. The race seems to be that I'm able to fire the event before the network is ready to receive it. It's not blocking, it's just dropping the event.

module Main where

import Reactive.Banana
import Reactive.Banana.Frameworks
import Control.Concurrent
import Control.Monad

main :: IO ()
main = do
    let
        networkDescription :: MomentIO ()
        networkDescription = do
            (e, fire) <- newEvent
            liftIOLater (void $ forkIO $ fire "a" >> threadDelay 1000 >> fire "b")
            reactimate (print <$> e)
    net <- compile networkDescription
    actuate net
    threadDelay 1000000
    pause net

For the sake of argument, pretend that this all happened in an execute so I can't pull it out of the MomentIO. How am I supposed to use the fire :: Handler without dropping that first event.

This program only prints out

"b"

But I expect it to print

"a"
"b"

from reactive-banana.

ocharles avatar ocharles commented on June 2, 2024

Ah, there is a caveat with the blocking - this only happens if the network is actuated. However, when you're in a call to compile this isn't true. In this state, events will be entirely discarded. I would guess what's happening here is the forkIO happens before the actuate call, causing that first event to drop. To work around this, the best I can think of is a separate MVar acting like a semaphore that you would use to signal that you know actuate has been called successfully.

I'll try and get back to your previous comment tomorrow

from reactive-banana.

luke-clifton avatar luke-clifton commented on June 2, 2024

Ah, great. That is exactly the problem. It's a pity that I'll need to add that guard when it only applies to the top-level case (and actually, the executes work just fine without it!).

from reactive-banana.

ocharles avatar ocharles commented on June 2, 2024

I see you closed this @luke-clifton - are you happy you understand what's going on now?

from reactive-banana.

luke-clifton avatar luke-clifton commented on June 2, 2024

Yes, it was that the network hadn't been actuated that was my issue. When actuated, the Handlers behave as I expected. I probably do need to audit my Handler use more carefully if I ever pause the network, as some events probably shouldn't be lost..

from reactive-banana.

Abastro avatar Abastro commented on June 2, 2024

I want to know the best practice to make the handler block until the network is actuated - I have many instances where events should never be dropped (that is not a timer).

from reactive-banana.

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.