Why dart calls my function "aFunction" after Step2? If I execute this code this text below in console:
Step2
Step1
My code:
void main()
{
...
stream.listen(aFunction);
print("Step2");
...
}
void aFunction()
{
print("Step1");
}
Thanks for help.
One of the few promises that a Dart Stream makes is that it generates no events in response to a listen call.
The events may come at a later time, but the code calling 'listen' is allowed to continue, and complete, before the first event is fired.
We originally allowed streams to fire immediately on a listen, but when we tried to program with that, it was completely impossible to control in practice.
The same is true for listening on a future, for example with 'then'. The callback will never come immediately.
Events should generally act as if they were fired by the top-level event loop, so the event handler doesn't have to worry if other code is running - other code that might not be reentrant.
That is not always the case in practice. One event handler may trigger other events through a synchronous stream controller, effectively turning one event into anoter. That requires the event handler to know what it is doing. Synchronous controllers are intended for internal use inside, e.g., a stream transformer, and using a synchronous stream controller isn't recommended in general.
So, no, you can't have the listen call immediately trigger the callback.
You can listen to a stream synchronously if you created a StreamController with the sync option enabled. Here is an example to get what you describe:
var controller = new StreamController<String>(sync: true);
var stream = controller.stream.asBroadcastStream();
stream.listen((text) => print(text));
controller.add("Step1");
print("Step2");
Related
I understand that Dart is single-threaded and that within an isolate a function call is popped from the event loop queue and executed. There seems to be two cases, async and sync.
a) Async: An asynchronous function will run without interruption until it gets to the await keyword. At this point, it may release control of the instruction pointer or continue its routine. (i.e. async functions can be but are not required to be interrupted on await)
b) Sync: All instructions from setup -> body -> and teardown are executed without interruption. If this is the case, I would say that synchronous functions are atomic.
I have an event listener that may have multiple calls in the event loop queue. I think I have two options.
Using the Synchronized package
a) Async version:
import 'package:synchronized/synchronized.dart';
final Lock _lock = Lock();
...
() async {
await _lock.synchronized(() async {
if (_condition) {
_signal.complete(status);
_condition = !_condition;
}
});
}
b) Sync version:
() {
if (_condition) {
_signal.complete(status);
_condition = !_condition;
}
}
From my understanding of the Dart concurrency model these are equivalent. I prefer b) because it is simple. However, this requires that there cannot be a race condition between two calls to my sync event handler. I have used concurrency in languages with GIL and MT but not with the event-loop paradigm.
a) Async: An asynchronous function will run without interruption until it gets to the await keyword. At this point, it may release control of the instruction pointer or continue its routine. (i.e. async functions can be but are not required to be interrupted on await)
await always yields. It's equivalent to setting up a Future.then() callback and returning to the Dart event loop.
For your simple example, there's no reason to use _lock.synchronized(). Synchronous code cannot be interrupted, and isolates (as their name imply) don't share memory. You would want some form of locking mechanism if your callback did asynchronous work and you needed to prevent concurrent asynchronous operations from being interleaved.
I have a question about Dart streams, as follows. When I use a Stream via its listen() method, I can assign the result returned by listen to a variable and cancel the Subscription when I'm done (such as a dispose() method). How should I go about canceling a Stream listened by an await-for loop?
Just to clarify, I am not looking to change the flow of execution (as in, to cancel the Stream so that the code after the await-for runs), but to prevent memory leaks when I don't need the Stream anymore.
The one and only way to cancel the subscription created by an await for is to exit the loop.
The moment you leave the loop using a control flow operation like return, break, continue, throw or rethrow (or a yield operation in an async* function where the listener on the stream has cancelled), the cancel method on the underlying subscription is automatically cancelled.
If the loop terminates itself, then it is because the stream is already done, so there is nothing to worry about.
If you want to keep computing inside the loop for a long time, and then exit the loop, then I recommend restructuring the code to do the computation outside of the loop instead. That is:
await for (var event in stream) {
if (event.isTheOne) {
await longComputation(event);
break;
}
}
will keep the stream alive and paused until longComputation completes.
instead I'd do something like:
var theOne = null;
await for (var event in stream) {
if (event.isTheOne) {
theOne = event;
break;
}
}
if (theOne != null) await longComputation(theOne);
or something similar.
I think How should I go about canceling a Stream is a bit misspelled. In case of listen method you have properly written cancel the Subscription, because you cancel subscription, not a steam itself.
listen() is non-blocking, it creates a subscription, registers a callback and then continues to execute the next code in the current block of code. The await for construct is blocking, it does not create a subscription to the stream. The execution of code will not go beyond the await for scope until the stream is closed. It is described with examples here. So per my understanding you don't need to worry about memory leaks in case of await for.
I know I have to cancel Stream Subscriptions when I no longer want to receive any events.
Do I have to this even after I receive a 'Done' event? Or do I get memory leaks?
What happens to Streams that are passed to addStream of another Stream? Are they automatically canceled?
Same Question on the StreamSink side do I have to close them if the stream is already done?
Short-answer: no, but you should. Nothing in the contract of either StreamSubscription or StreamSink requires closing the resources, but some use cases can lead to memory leaks if you don't close them, even though in some cases, doing so might be confusing. Part of the confusion around these classes is that they are overloaded, and handle two fairly distinct use cases:
Resource streams (like file I/O, network access)
Event streams (like click handlers)
Let's tackle these subjects one at a time, first, StreamSubscription:
StreamSubscription
When you listen to a Stream, you receive a StreamSubscription. In general, when you are done listening to that Stream, for any reason, you should close the subscription. Not all streams will leak memory if choose not to, but, some will - for example, if you are reading input from a file, not closing the stream means the handle to the file may remain open.
So, while not strictly required, I'd always cancel when done accessing the stream.
StreamSink
The most common implementation of StreamSink is StreamController, which is a programmatic interface to creating a Stream. In general, when your stream is complete (i.e. all data emitted), you should close the controller.
Here is where it gets a little confusing. Let's look at those two cases:
File I/O
Imagine you were creating an API to asynchronously read a File line-by-line:
Stream<String> readLines(String path);
To implement this, you might use a StreamController:
Stream<String> readLines(String path) {
SomeFileResource someResource;
StreamController<String> controller;
controller = new StreamController<String>(
onListen: () {
someResource = new SomeFileResource(path);
// TODO: Implement adding to the controller.
},
);
return controller.stream;
}
In this case, it would make lots of sense to close the controller when the last line has been read. This gives a signal to the user (a done event) that the file has been read, and is meaningful (you can close the File resource at that time, for example).
Events
Imagine you were creating an API to listen to news articles on HackerNews:
Stream<String> readHackerNews();
Here it makes less sense to close the underlying sink/controller. Does HackerNews ever stop? Event streams like this (or click handlers in UI programs) don't traditionally "stop" without the user accessing for it (i.e cancelling the StreamSubscription).
You could close the controller when you are done, but it's not required.
Hope that makes sense and helps you out!
I found in my case that if I have code like this:
Stream<String> readHackerNews(String path) {
StreamController<String> controller = StreamController<String>();
......
return controller.stream;
}
I see a warning message "Close instance of 'dart.core.Sink'." in the Visual Studio Code.
In order to fix this warning I added
controller.close()
to the event handler for the OnCancel event, see below:
Stream<String> readHackerNews(String path) {
StreamController<String> controller = StreamController<String>();
//TODO: your code here
controller.onCancel = () {
controller.close();
};
return controller.stream;
}
Hope this helps!
I am working with Rime, more specifically with the runicast example. Once a message is received i store it in a linked list, then i post an event to a process which is in charge of extracting messages from the linked list and processing them. My code is something like this:
static void recv_runicast(struct runicast_conn *c,
const linkaddr_t *from, uint8_t seqno)
{
/*code to insert the message into the linked list*/
...
/*Post an event to the process which extracts messages from the linked list */
process_post(&extract_msg_from_linked_list, PROCESS_EVENT_CONTINUE, NULL);
}
My question is: Is it safe to use process_post within the callback function recv_runicast? or should i use process_poll?
Thanks in advance
Yes, it's safe. The network stack operations are done in process context, and Contiki processes are not preemptive. So pretty much any process-related operations are "safe".
The main differences between process_post and process_poll is that the former will put a new event in the process event buffer, while the latter will simply set a flag. So the second options is slightly more efficient. Also, in theory the event buffer can get full and events start to get lost, but that's very unlikely to be a problem.
I would use none of these functions at all, but do the processing directly in the callback to simplify the execution flow.
I would like to launch a fairly expensive operation in response to a user clicking on a canvas element.
mouseDown(MouseEvent e) {
print("entering event handler");
var future = new Future<int>(expensiveFunction);
future.then((int value) => redrawCanvas(value);
print("done event handler");
}
expensiveFunction() {
for(int i = 0; i < 1000000000; i++){
//do something insane here
}
}
redrawCanvas(int value) {
//do stuff here
print("redrawing canvas");
}
My understanding of M4 Dart, is that this future constructor should launch "expensiveFunction" asynchronously, aka on a different thread from the main one. And it does appear this way, as "done event handler" is immediately printed into my output window in the IDE, and then some time later "redrawing canvas" is printed. However, if I click on the element again nothing happens until my "expensiveFunction" is done running from the previous click.
How do I use futures to simply launch an compute intensive function on new thread such that I can have multiple of them queued up in response to multiple clicks, even if the first future is not complete yet?
Thanks.
As mentioned in a different answer, Futures are just a "placeholder for a value that is made available in the future". They don't necessarily imply concurrency.
Dart has a concept of isolates for concurrency. You can spawn an isolate to run some code in a parallel thread or process.
dart2js can compile isolates into Web Workers. A Web Worker can run in a separate thread.
Try something like this:
import 'dart:isolate';
expensiveOperation(SendPort replyTo) {
var result = doExpensiveThing(msg);
replyTo.send(result);
}
main() async {
var receive = new ReceivePort();
var isolate = await Isolate.spawn(expensiveOperation, receive.sendPort);
var result = await receive.first;
print(result);
}
(I haven't tested the above, but something like it should work.)
Event Loop & Event Queue
You should note that Futures are not threads. They do not run concurrently, and in fact, Dart is single-threaded. All Dart code runs in an event loop.
The event loop is a loop that runs as long as the current Dart isolate is alive. When you call main() to start a Dart application, the isolate is created, and it is no longer alive after the main method is completed and all items on the event queue are completed as well.
The event queue is the set of all functions that still need to finish executing. Because Dart is single threaded, all of these functions need to run one at a time. So when one item in the event queue is completed, another one begins. The exact timing and scheduling of the event queue is something that's way more complicated than I can explain myself.
Therefore, asynchronous processing is important to prevent the single thread from being blocked by some long running execution. In a UI, a long process can cause visual jankiness and hinder your app.
Futures
Futures represent a value that will be available sometime in the Future, hence the name. When a Future is created, it is returned immediately, and execution continues.
The callback associated with that Future (in your case, expensiveFunction) is "started" by being added to the event queue. When you return from the current isolate, the callback runs and as soon as it can, the code after then.
Streams
Because your Futures are by definition asynchronous, and you don't know when they return, you want to queue up your callbacks so that they remain in order.
A Stream is an object that emits events that can be subscribed to. When you write canvasElement.onClick.listen(...) you are asking for the onClick Stream of MouseEvents, which you then subscribe to with listen.
You can use Streams to queue up events and register a callback on those events to run the code you'd like.
What to Write
main() {
// Used to add events to a stream.
var controller = new StreamController<Future>();
// Pause when we get an event so that we take one value at a time.
var subscription = controller.stream.listen(
(_) => subscription.pause());
var canvas = new CanvasElement();
canvas.onClick.listen((MouseEvent e) {
print("entering event handler");
var future = new Future<int>(expensiveFunction);
// Resume subscription after our callback is called.
controller.add(future.then(redrawCanvas).then(subscription.resume()));
print("done event handler");
});
}
expensiveFunction() {
for(int i = 0; i < 1000000000; i++){
//do something insane here
}
}
redrawCanvas(int value) {
//do stuff here
print("redrawing canvas");
}
Here we are queuing up our redrawCanvas callbacks by pausing after each mouse click, and then resuming after redrawCanvas has been called.
More Information
See also this great answer to a similar question.
A great place to start reading about Dart's asynchrony is the first part of this article about the dart:io library and this article about the dart:async library.
For more information about Futures, see this article about Futures.
For Streams information, see this article about adding to Streams and this article about creating Streams.