Comments (12)
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.
This is similar to
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.
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.
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.
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 Timer
s from (so TimerManager
owns Timer
s 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.
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 Unique
s in the MomentIO
instead of newEvent
s 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.
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.
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.
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.
I see you closed this @luke-clifton - are you happy you understand what's going on now?
from reactive-banana.
Yes, it was that the network hadn't been actuated that was my issue. When actuated, the Handler
s 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.
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)
- `reactive-banana` can't be compiled on GHC 9.2 HOT 2
- I created a Matrix channel HOT 8
- An interesting `Applicative`-ish way of combining `Event`s HOT 4
- Add `MonadMoment` instances for all monad transformers in `transformers` HOT 1
- Release 1.3.0.0 HOT 3
- Join Stackage HOT 4
- `Behavior` constantly re-evaluating when caching is expected HOT 4
- Unexpected memory growth HOT 6
- `Behavior`s can hold onto more memory than they need to HOT 2
- Memory leak with dynamic behavior switching HOT 16
- Gloss GUI events not responding HOT 5
- Idea: add a combinator to track when a behavior is a different value HOT 14
- Call `unregister` for garbage collected events made by `AddHandler`. HOT 9
- Discrepancy in model and implementation HOT 15
- Optimize representation of Latch HOT 1
- Not compatible with these-1.2 (disallowed) HOT 2
- Simultaneous events can have undefined behavior within execute HOT 2
- source-code formatter? HOT 2
- Accept deepseq-1.5 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from reactive-banana.