CLI Dart: onPause, onResume, onDone not firing up as expected - events

I am experimenting Dart, and I can't explain two observations.
I wonder why the "onDone" handler assigned to a stream subscription does not fire up.
I wonder why the "onPause" and "onResume" handlers fire up only once.
The code:
import 'dart:async';
import 'dart:io';
/// This class encapsulates all the necessary data used by the "onValue" event
/// handler (the construct avoids using global variables).
class OnValueHandlerContainer {
static StreamSubscription<int> _streamSubscriber;
static setStreamSubscriber(StreamSubscription<int> stream) {
_streamSubscriber = stream;
}
// This static method is the handler executed when a event is received through
// the stream.
//
// WARNING: you have absolutely no idea when this handler will be executed.
// Do not assume that it will be executed right after the execution of the code
// that emits an event. It may be executed several lines (of codes) below the
// line that emits the event. It may well be executed after the end of the
// script.
static void onValue(int value) {
// At this point: the state of the subscription is (inevitably) "active".
print("onValue: An event has been raised. The associated value is ${value}!");
print(" Pause the subscription. Wait for 1 second. Resume the subscription");
// Note 1: once a Dart function starts executing, it continues executing until
// it exits. When managing interrupts in C, it is necessary to protect
// interrupt handlers from being interrupted. This is not the case in
// Dart : a function (and, thus, an event handler) cannot be interrupted
// by the occurrence of another event.
// => The code below has no sense, other than experimentation.
// Note 2: while paused, the subscription will not fire any events. If it receives
// events from its source, they will be buffered until the subscription
// is resumed.
_streamSubscriber.pause();
sleep(Duration(seconds: 1));
_streamSubscriber.resume();
// At this point: the state of the subscription is "active".
}
}
main() async {
// Create a controller.
// A StreamController gives you a new stream and a way to add events to the stream
// at any point, and from anywhere. The stream has all the logic necessary to handle
// listeners and pausing. You return the stream and keep the controller to yourself.
StreamController<int> sc = StreamController<int>(
onListen: () => print("Controller: the stream has been assigned a listener!"),
onCancel: () => print("Controller: the stream has been canceled!"),
// As you may notice, the event handlers are not executed every time the
// subscription gets paused or resumed.
//
// This behaviour comes from these facts:
// - Dart is single-threaded.
// - An event handler cannot be interrupted: once a Dart function starts
// executing, it continues executing until it exits. In other words, Dart
// functions can’t be interrupted by other Dart code.
// See https://webdev.dartlang.org/articles/performance/event-loop
// - A stream is a FIFO.
onPause: () => print("Controller: the stream has been paused!"),
onResume: () => print("Controller: the stream has been resumed!")
);
// Get the stream created by the stream controller.
// Right now, this stream has no assigned listener.
Stream<int> stream = sc.stream;
print("Does the stream provided by the controller have a listener ? ${sc.hasListener ? 'yes' : 'no'} - the answer should be no.");
// Push values into the stream controlled by the stream controller.
// Because no listener subscribed to the stream, these values are just stored
// into the stream.
for(int i=0; i<3; i++) {
print("Send the value ${i} into the stream.");
sc.add(i);
}
// Add a listener to the stream.
// Now the stream has an assigned listener.
StreamSubscription<int> subscriber = stream.listen(OnValueHandlerContainer.onValue);
OnValueHandlerContainer.setStreamSubscriber(subscriber);
subscriber.onDone(() => print("The subscription is done!"));
print("Does the stream provided by the controller have a listener ? ${sc.hasListener ? 'yes' : 'no'} - the answer should be yes.");
// Wait for 10 seconds.
print("Start waiting for 10 seconds");
Future.delayed(Duration(seconds: 10)).then((var v) => print("10 seconds ellapsed!"));
print("End of script");
}
The result:
Does the stream provided by the controller have a listener ? no - the answer should be no.
Send the value 0 into the stream.
Send the value 1 into the stream.
Send the value 2 into the stream.
Controller: the stream has been assigned a listener!
Does the stream provided by the controller have a listener ? yes - the answer should be yes.
Start waiting for 10 seconds
End of script
onValue: An event has been raised. The associated value is 0!
Pause the subscription. Wait for 1 second. Resume the subscription
Controller: the stream has been paused!
onValue: An event has been raised. The associated value is 1!
Pause the subscription. Wait for 1 second. Resume the subscription
onValue: An event has been raised. The associated value is 2!
Pause the subscription. Wait for 1 second. Resume the subscription
Controller: the stream has been resumed!
10 seconds ellapsed!
Basically, the code provided performs the following actions :
A stream controller is created.
3 events are injected into the stream provided by the controller.
A listener subscribes to the stream provided by the controller.
We assign an "onDone" handler to the listener subscription.
Within the stream listener (OnValueHandlerContainer::onValue) we pause and resume the subscription.
The stream listener fires up 3 times, as expected.
However:
the "onDone" handler is never executed. I expect it to be executed at the end of the script execution, while the controller is being destroyed (and, thus, the subscription gets closed).
the "onPause" and "onResume" handlers fire up only once. I expect them to be executed 3 times.
Any idea ?

The reason you don't get a "done" event is that you never close the stream subscription.
The reason you don't get more "pause" events is that the stream subscription is clever.
The first thing you do is to add a lot of events, before anyone even listens to the stream. You should never do that in real code, instead only start adding events when the onListen is called, and stop again when onPause is called, until the subscription is resumed.
Here, the stream subscription gets stuffed up with a number of events, then it delivers one event, and then the subscription is paused.
The subscription dutifully reports that back to the controller.
Then the subscription gets a resume. This is where it gets clever. Since it already has events to deliver, it does not report the resume back to the controller. It doesn't actually want more events right now, there are plenty to deliver. And so it delivers the buffered events, one at a time, until the buffer is empty. At that point, it reports the resume back to the controller.
The controller reports that work has been resumed, but since nobody adds any more events, and nobody calls close, nothing further will happen.

Related

Is there any function in reactor.util.retry.Retry that i can use when retry is successful

I need to perform some action immediately after Retry is successful for server-sent event implementation, I was unable to find any method in reactor.util.retry.Retry. Is there any other alternate to do doOnRetrySuccess(func)
I'm not aware of a built-in operator to do this, but you could utilize an AtomicBoolean to detect if the onNext/onComplete signal occurs immediately after a retry:
final AtomicBoolean retrying = new AtomicBoolean();
monoOrFlux
.retryWhen(Retry
// Configure whatever retry behavior you want here.
// For simplicity, this example uses .indefinitely()
.indefinitely()
// Set the retrying flag to indicate that a retry is being attempted.
.doBeforeRetry(signal -> retrying.set(true)))
// Check and reset the retrying flag in doOnEach.
// This example uses doOnEach, which works for both Mono and Flux.
// If the stream is a Mono, then you could simplify this to use doOnSuccess instead.
.doOnEach(signal -> {
if ((signal.isOnNext() || signal.isOnComplete())
&& retrying.compareAndSet(true, false)) {
// This is the first onNext signal emitted after a retry,
// or the onComplete signal (if no element was emitted) after a retry.
}
});

Why Observable.race not working if one of observable stop emit events?

I'd like to implement websocket reconnect in webapp if internet connection is lost. In order to detect that internet is lost I use ping-pong approach, which means that I send from client ping-message and server returns me pong-message.
When webapp loaded I send init ping message and start to listen a reply on socket some kind of this:
this.websocket.onmessage = (evt) => {
try {
const websocketPayload: any = JSON.parse(evt.data);
if (websocketPayload.pong !== undefined && websocketPayload.pong == 1) {
this.pingPong$.next('pong');
}
It means that internet connection looks ok and we can continue. Also I have the follow code:
Observable.race(
Observable.of('timeout').delay(5000).repeat(),
this.pingPong$
).subscribe((data) => {
console.log("[ping-pong]:", data);
if (data == 'pong') {
Observable.interval(5000).take(1).subscribe(() => {
console.log("[ping-pong]:sending ping")
this.send({ping:1})
});
} else if (data == 'timeout'){
// show reconnect screen and start reconnect
console.error("It looks like websocket connection lost");
}
});
But!
When this.pingPong$ subject stops to emit events - .next() doesn't happen because of we can't get response when I break connection manually - I considered that in Observable.race this observable will be emitted
Observable.of('timeout').delay(5000).repeat()
But my subscribe never happens if this.pingPong$ stop emitting.
Why ?
Thank you
race picks and keeps subscribed to the first Observable that emits.
So if your this.pingPong$ starts emitting and then stops it makes no difference because race keeps subscribed to this.pingPong$. The other Observables don't matter any more. You might want emit one value from this.pingPong$ and the repeat the whole process. For example like the following:
Observable.race(
Observable.of('timeout').delay(5000).repeat(),
this.pingPong$
)
.pipe(
take(1), // complete the chain immediately
repeat() // resubscribe after take(1) completes the chain
)
.subscribe(...);
Obviously it mostly depends on what you want to do but I hope you get the point.

Send message only once instead of periodically

I have developed a scenario where at first the vehicles send a self messsage and upon reception of the self message vehicles send a message to RSU.
The self message code is written in the initialize() method. But during simulation the vehicles send the message to RSU every second.
I want the message to be sent only once. What should I do?
I have attached the handleSelfmessage method of my TraCIDemo11p.cc class.
if(msg->isSelfMessage()==true)
{
cModule *tmpMobility = getParentModule()->getSubmodule("veinsmobility");
mobility = dynamic_cast<Veins::TraCIMobility*>(tmpMobility);
ASSERT(mobility);
t_channel channel = dataOnSch ? type_SCH : type_CCH;
WaveShortMessage* wsm = prepareWSM("data", dataLengthBits, channel, dataPriority, -1,2);
wsm->setSenderAddress(myAddress);
wsm->setRecipientAddress(1001);
sendMessage(wsm->getWsmData());
}
Your approach seems right, but obviously you have some problem in your implementation.
Alternatively you can create a new message and send it to yourself
myOneTimeMsg = new cMessage("OneTimeMsg");
scheduleAt(simTime()+1.0, myOneTimeMsg); // this will send the message at t=currentTime+1.0 seconds
Then you can handle that message as follows:
if(msg->isSelfMessage()==true){
if (msg == myOneTimeMsg) {
// do what you need next...
Amending the answer of #user4786271:
The handleSelfMsg method of TraCIDemo11p.cc obviously is executed for every self-message which this module receives - possibly also non WSMs. So if you just added the given code there, it will send a WSM for every of those self-messages. Thus, only checking for self-message type is not enough. You need to create a new message type and check for that type as shown by #user4786271.

How do I determine that all actors have received a broadcast message

I have a single ActorA that reads from an input stream and sends messages to a group of ActorB's. When ActorA reaches the end of the input stream it cleans up its resources, broadcasts a Done message to the ActorB's, and shuts itself down.
I have approx 12 ActorB's that send messages to a group of ActorC's. When an ActorB receives a Done message from ActorA then it cleans up its resources and shuts itself down, with the exception of the last surviving ActorB which broadcasts a Done message to the ActorC's before it shuts itself down.
I have approx 24 ActorC's that send messages to a single ActorD. Similar to the ActorB's, when each ActorC gets a Done message it cleans up its resources and shuts itself down, with the exception of the last surviving ActorC which sends a Done message to ActorD.
When ActorD gets a Done message it cleans up its resources and shuts itself down.
Initially I had the ActorB's and ActorC's immediately propagate the Done message when they received it, but this might cause the ActorC's to shut down before all of the ActorB's have finished processing their queues; likewise the ActorD might shut down before the ActorC's have finished processing their queues.
My solution is to use an AtomicInteger that is shared among the ActorB's
class ActorB(private val actorCRouter: ActorRef,
private val actorCount: AtomicInteger) extends Actor {
private val init = {
actorCount.incrementAndGet()
()
}
def receive = {
case Done => {
if(actorCount.decrementAndGet() == 0) {
actorCRouter ! Broadcast(Done)
}
// clean up resources
context.stop(self)
}
}
}
ActorC uses similar code, with each ActorC sharing an AtomicInteger.
At present all actors are initialized in a web service method, with the downstream ActorRef's passed in the upstream actors' constructors.
Is there a preferred way to do this, e.g. using calls to Akka methods instead of an AtomicInteger?
Edit: I'm considering the following as a possible alternative: when an actor receives a Done message it sets the receive timeout to 5 seconds (the program will take over an hour to run, so delaying cleanup/shutdown by a few seconds won't impact the performance); when the actor gets a ReceiveTimeout it broadcasts Done to the downstream actors, cleans up, and shuts down. (The routers for ActorB and ActorC are using a SmallestMailboxRouter)
class ActorB(private val actorCRouter: ActorRef) extends Actor {
def receive = {
case Done => {
context.setReceiveTimeout(Duration.create(5, SECONDS))
}
case ReceiveTimeout => {
actorCRouter ! Broadcast(Done)
// clean up resources
context.stop(self)
}
}
}
Sharing actorCount among related actors is not good thing to do. Actor should only be using its own state to handle messages.
How about having ActorBCompletionHanlder actor for actor of type ActorB. All ActorB will have reference to ActorBCompletionHanlder actor. Every time ActorB receives Done message it can do necessay cleanup and simply pass done message to ActorBCompletionHanlder. ActorBCompletionHanlder will maintain state variale for maintaining counts. Everytime it receives done message it can simply update counter. As this is solely state variable for this actor no need to have it atomic and that way no need for any explicit locking. ActorBCompletionHanlder will send done message to ActorC once it receives last done message.
This way sharing of activeCount is not among actors but only managed by ActorBCompletionHanlder. Same thing can be repeated for other types.
A-> B's -> BCompletionHanlder -> C's -> CCompletionHandler -> D
Other approach could be to have one monitoring actor for evey related group of actors. And using watch api and child terminated event on monitor you can chose to decide what to do once you receive last done message.
val child = context.actorOf(Props[ChildActor])
context.watch(child)
case Terminated(child) => {
log.info(child + " Child actor terminated")
}

Pebble JS app_message_outbox_send() in a for loop

I am using sdk2 for pebble, with the js appmessage features:
I am trying to send consecutive messages to pebble js on the phone, for each one of my menu items. A variable movie_count = 5 exists, and I use this for looping, It gets logged out as 5 as the code shows below, so it should be getting to all 5 , logging errors at least, but it just doesn't log anything after the first time:
static void up_click_handler(ClickRecognizerRef recognizer, void *context) {
int i;
APP_LOG(APP_LOG_LEVEL_DEBUG, "movie_count int %u", movie_count);
for(i = 0;i<movie_count;i++){
Tuplet build_menu_tuple = TupletInteger(BUILD_MENU_KEY, 1); // just a flag for the appmessage js code
Tuplet menu_id_tuple = TupletInteger(MENU_ID_KEY, i);
DictionaryIterator *iter;
app_message_outbox_begin(&iter);
if (iter == NULL) {
return;
}
dict_write_tuplet(iter, &build_menu_tuple);
dict_write_tuplet(iter, &menu_id_tuple);
dict_write_end(iter);
app_message_outbox_send();
}
}
in JS appmessage
this code in the js is executed, although only once, I have logged the output in my in_receiver() callback in my watchapp, and my first item gets logged, but the logger just quits after that... Is this because the watchapp cannnot send blutooth messages in a loop like this? Is there a way to make sure the message is sent, or pause the execution so it sends at a slower pace? (the movies_json exists above the code below, i left it out for brevity, but it is there, a json object with an inner array of movies)
if(e.payload.build_menu){
var menu_id = e.payload.menu_id;
console.log("menu_id" + menu_id);
Pebble.sendAppMessage({"title":movies_json.movies[menu_id].title,
"stars":movies_json.movies[menu_id].stars,
"menu_id":menu_id
});
console.log("movie title:" + movies_json.movies[i].title);
}
in_recived_handler callback code to handle messages from the js
this code is in the callback that takes in messsages from the phones js... it only gets to the first item, logs just the first item's menu_id and title, and then logging just stops.
if(menu_id_tuple){
int menu_id;
menu_id = menu_id_tuple->value->int32;
char movie_title[30];
strncpy(movie_title, movie_title_tuple->value->cstring, 30);
APP_LOG(APP_LOG_LEVEL_DEBUG, "In received handler movie_title: %s" , movie_title);
APP_LOG(APP_LOG_LEVEL_DEBUG, "In received handler menu_id: %u" , menu_id);
}
You need to wait until the first message is sent to send the next one.
The proper way to do this is to register a callback for the outbox_sent event and to queue the next message in this event.
Explanation
There is only one bluetooth buffer on pebble and it can only hold one message at a time. If you send messages in a for loop, this buffer gets filled with the first message and all the other messages are rejected.
You would see the error messages if you checked the return value of app_message_outbox_send(). You should also implement a AppMessageOutboxFailed handler.

Resources