Dart/Flutter: How to best process hardware events in an infinite loop - flutter

I have an infinite loop that runs in a an async function in a Flutter application (although the code is pure Dart, no Flutter API is used on it).
Basically, it processes some math stuff, but I want to be able to update the data to be processed with an event coming from hardware (in this specific case from the microphone).
In order to give the fast while(true) the opportunity to get events from the outside, I added a delay with a duration of zero.
doStuff() async {
while (!stopFlag) {
processMathStuff(dataModifiedByHardwareCallback);
// Without this, the loop can't be interrupted.
await Future.delayed(Duration.zero);
}
}
And although that seems to work in most platforms, calling Future.delayed like that makes this loop much slower than I would like. The question is if is it there a better (faster) way of doing this. This even looks kind of a hack to me. I tried calling Future.delayed only every a certain amount of iterations, but doing so even as often as every 10 iterations, the system loses events.
By the way, processMathStuff does not contain loops internally, so it is really O(1).
NOTE: If I move the code to an Isolate, I get a similar problem with the Isolate not listening to the sendPort stuff if the while(true) lacks that kind of "yield".

Related

Flutter: send message to isolate without `await`

The title is one way of doing what I ultimately want; others may be acceptable.
So I have a class that already exists. It controls some other stuff, and has a start and stop method. We are now making the list of "other stuff" depend on some settings saved in shared preferences, but shared preferences requires an asynchronous SharedPreferences.getInstance() call. I don't want to have to propagate asynchronicity all the way up the call chain. How do I avoid that? (Alternative 1: is there a library that you can persist data with, without messing directly with asynchronicity?)
Part of the problem is that the start and stop methods can be called at any point after the constructor (and indeed, when I just did SharedPreferences.getInstance().then(INITIALIZE LATE FINAL FIELDS), without await, start got called before the initialization happened and the code threw an exception.
(Alternative 2: is it acceptable to save the result of a then, and later call then on it? Like, do this._future = SharedPreferences.getInstance().then((prefs) {return LIST_OF_STUFF;}), and in start do this._future.then((listOfStuff) {START LIST OF STUFF; return listOfStuff;}), and similarly in stop? Seems...suspicious, but possibly functional.) I'd be ok with making the start and stop methods asynchronous, I guess, as long as I don't have to actually handle that in the code that calls them, but that doesn't really solve the problem that I need the SharedPreferences.getInstance() to have completed so I can have the list of stuff, first.
My default mechanism for handling concurrency is Communicating Sequential Processes, so I took a leaf from that book and thought, "What if I started an isolate that accepts start/stop messages? It can await the SharedPreferences, then loop over incoming messages and start/stop the Stuff as requested. I wrote an isolate to do that...and apparently you can't pass a ReceivePort in to an isolate (at least not by declaring it in the outer scope and using the reference in the isolate - it gives me an error Invalid argument(s): Illegal argument in isolate message: (object is aReceivePort)). You only get to directly hand the isolate a SendPort, so IT can send THE CALLER messages. The idiom I see in most places is to have the isolate send back a new SendPort, whose ReceivePort the isolate keeps. However, this returns me to my original problem - creating the isolate (and receiving its SendPort) is asynchronous, so it may not be done by the time start or stop get called. Since you pretty clearly CAN send a ReceivePort between...wait, I forgot it's technically not sending a ReceivePort, just another SendPort. Can you really only send SendPorts, specifically? That's...really obnoxious, if so. It seems pointlessly pedantic and obstructive. ...Ok, yeah, ReceivePort is explicitly restricted from being sent. Argh. I even thought I had a workaround to the "can't pass via scope" problem: make a new ReceivePort in the calling code, use its SendPort to send start and stop messages, and jerryrig some kind of handover of the ReceivePort to the isolate once that comes online. But if ReceivePort is entirely forbidden from being sent to the isolate, I guess that's out. So...how do I do what I want to do??? How do I pull a setting from disk, potentially queuing up start and stop messages while I wait for the persistence library to become available, without exposing to the calling code that we're making asynchronous calls under the hood?? Am I gonna have to make some kind of awful kludge of dangerous asynchronously-modified flags? I'll be rather disappointed in Flutter if that's what it comes down to.

I am having a problem with Flutter/Dart Async code execution, as in how does it work

In Flutter we use async await and Future, can someone explain that if we don't use another thread (which we can't in dart) and run the job on main UIThread only won't the app become choppy because even if we are waiting for the job to execute it will ultimately execute on UIThread only.
I somewhere read about isolates also. But cannot paint the exact picture. If someone can explain in detail.
I think you missed something when it comes to asynchronous methods in Dart.
Dart is a single-threaded programming language, Java or C# are multi-threaded programming languages, forget async as a new thread, it doesn't happen in Dart.
Dart is a single-threaded programming language. This means is that Dart can only run one instruction at a time, while Java could run multiple instructions concurrently.
As a rule, everything you do in Dart will start in UI-Thread. Whatever method you call in Dart, whether using sync, async, then, they will be running on UI-Thread, since Dart is a single thread.
In single-threaded languages ​​like Javascript and Dart, an async method is NOT executed in parallel but following the regular sequence of events, handled by the Event Loop. There are some problems (I would give some approaches, as we will see below) if you run the following code in a multithreaded language where fetch will take some time to execute:
String user = new Database.fetch(David);
String personalData = new Database.fetch(user);
You will receive David's data in user, and after that, you will receive your data.
This will lock your UI, unlike languages ​​like Java which have Threads, where you can perform this task in the background, on another thread, and the UI-Thread will run smoothly.
If you do this at Dart
String user = new Database.fetch(David);
String personalData = new Database.fetch(user);
user will be null in personalData, because the fetch event is a Future.
How to solve this in Dart?
String user = await Database.fetch(David);
String personalData = await Database.fetch(user);
For those who like a more functional paradigm (I don't like it) you can use then.
Database.fetch(David).then((user){
Database.fetch(user).then((personal){
String personalData = personal;
});
});
However, imagine that you have billions of data in that database, this heavy task will probably cause the animations on your screen to freeze, and you will see a jank in the user's UI, for that purpose isolates were invented.
Dart Isolates give you a way to perform real multi-threading in Dart. They have their own separate heaps(memory), and run the code in the background, just like the Threads of multi-threaded languages. I could explain how isolates work, but it would make this response very long, and the goal is just to differentiate asynchronous from multi-threaded methods.
A simple way to solve the problem above using isolates would be using compute.
Compute was created to facilitate the creation of isolates, you just pass the function and the data that this function will execute, and that's it!
Important to remember that compute is a Future, so you have to use await or then to get its result.
In our example, we could create a new thread and get its result when we finish by just calling compute like this:
String user = await compute(Database.fetch,David);
String personalData = await compute(Database.fetch,user);
Very simple, isn't it?
In summary:
Everything that waits some time to be completed, in Dart is called a "Future".
To wait for the result of a future to be assigned to a variable, use await or then.
The asynchronous methods (await and then) can be used to obtain a result from a Future, and are executed ON THE MAIN THREAD because Dart is single-thread.
If you want to run any function on a new thread, you can create an isolate. Dart offers an easy-to-use isolate wrapper called compute, where you only need to pass one method that will be processed and the data that will be processed, and it will return its result in the future.
NOTE: if you are going to use compute make sure you are using a static or top-level method (see that in the example I used Database.fetch it was no accident if you need to call Database().fetch or need to create an instance of it, means it is not a static method and will not work with isolates).
English is not my first language and I didn't want to write so much because of that, but I hope I helped differentiate between multi-threaded asynchronous programming from single-threaded asynchronous programming.

Understanding Chrome Dev Tools timeline

I'm trying to understand why I have several Long Frames reported by Chrome Dev Tools.
The first row (top of the call stack) in the flame chart are mostly Timer Fired events, triggered by jQuery.Deferred()s executing a bunch of $(function(){ }); ready funcs.
If I dig into the jQuery source and replace their use of setTimeout with requestAnimationFrame the flame chart doesn't change much, I still get many of the rAFs firing within a single frame (as reported by dev tools) making long frames. I'd have expected doing the below pseudocode:
window.requestAnimationFrame(function() {
// do stuff
});
window.requestAnimationFrame(function() {
// do more stuff
});
to be executed on two difference animation frames. Is this not the case?
All of the JS that is executing is necessary, but what should I do to execute it as "micro tasks" (as hinted at, but not explained here https://developers.google.com/web/fundamentals/performance/rendering/optimize-javascript-execution) when setTimeout and rAF don't seem to achieve this.
Update
Here's a zoomed in shot of one of the long frames that doesn't seem to have any reflows (forced or otherwise) in it. Why are all the rAF callbacks here being executed in one frame?
Long frames are usually caused by forced synchronous layouts, which is when you (unintentionally) force a layout operation to happen early.
When you write to the DOM, the layout needs to be reflowed because it has been invalidated by the write operation. This usually happens at the next frame. However, if you try to read from the DOM, the layout happens early, in the current frame, in order to make sure that the correct value gets returned. When forced layout occurs, it causes long frames, leading to jank.
To prevent this from happening, you should only perform the write operations inside your requestAnimationFrame function. The read operations should be done outside of this, so as to avoid the browser doing an early layout.
Diagnose Forced Synchronous Layouts is a nicely explained article, and has a simple example demo for detecting forced reflow in DevTools, and how to resolve it.
It might also be worth checking out FastDom, which is a library for batching your read and write. It is basically a queuing system, and is more scalable.
Additional Source:
What forces layout / reflow, by Paul Irish, contains a comprehensive list of properties and methods that will force layout/reflow.
Update: As for the assumption that multiple requestAnimationFrame calls will execute callbacks on separate frames, this is not the case. When you have consecutive calls, the browser adds the callbacks to a document list of animation callbacks. When the browser goes to run the next frame, it traverses the document list and executes each of the callbacks, in the order they were added.
See Animation Frames from the HTML spec for more of the implementation details.
This means that you should avoid using the consecutive calls, especially where the callback function execution times combined exceed your frame budget. I think this would explain the long frames that aren't caused by reflow.

How a runloop actually works

Earlier this month I asked this question 'What is a runloop?' After reading the answers and did some tries I got it to work, but still I do not understand it completely. If a runloop is just an loop that is associated with an thread and it don't spawn another thread behind the scenes how can any of the other code in my thread(mainthread to keep it simple) execute without getting "blocked"/not run because it somewhere make an infinite loop?
That was question number one. Then over to my second.
If I got something right about this after having worked with this, but not completely understood it a runloop is a loop where you attach 'flags' that notify the runloop that when it comes to the point where the flag is, it "stops" and execute whatever handler that is attached at that point? Then afterwards it keep running to the next in que.
So in this case no events is placed in que in connections, but when it comes to events it take whatever action associated with tap 1 and execute it before it runs to connections again and so on. Or am I as far as I can be from understanding the concept?
"Sort of."
Have you read this particular documentation?
It goes into considerable depth -- quite thorough depth -- into the architecture and operation of run loops.
A run loop will get blocked if it dispatches a method that takes too long or that loops forever.
That's the reason why an iPhone app will want to do everything which won't fit into 1 "tick" of the UI run loop (say at some animation frame rate or UI response rate), and with room to spare for any other event handlers that need to be done in that same "tick", either broken up asynchronously, on dispatched to another thread for execution.
Otherwise stuff will get blocked until control is returned to the run loop.

How to handle class methods being called again before they are finished?

What is the best way to handle this situation on an iPhone device: My program ramps the pitch of a sound between two values. A button pressed calls a method that has a while loop that does the ramping in small increments. It will take some time to finish. In the meantime the user has pressed another button calling the same method. Now I want the loop in the first call to stop and the second to start from the current state. Here is the something like what the method should look like:
-(void)changePitchSample: (float) newPitch{
float oldPitch=channel.pitch;
if (oldPitch>newPitch) {
while (channel.pitch>newPitch) {
channel.pitch = channel.pitch-0.001;
}
}
else if (oldPitch<newPitch) {
while (channel.pitch<newPitch) {
channel.pitch = channel.pitch+0.001;
}
}
}
Now how to best handle the situation where the method is called again? Do I need some kind of mulitthreading? I do not need two processes going at the same time, so it seems there must be some easier solution that I cannot find (being new to this language).
Any help greatly appreciated!
You cannot do this like that. While your loop is running no events will be processed. So if the user pushes the button again nothing will happen before your loop is finished. Also like this you can’t control the speed of your ramp. I’d suggest using a NSTimer. In your changePitchSample: method you store the new pitch somewhere (don’t overwrite the old one) and start a timer that fires once. When the timer fires you increment your pitch and if it is less than the new pitch you restart the timer.
Have a look at NSOperation and the Concurrency Programming Guide. You can first start you operation the increase the pitch and also store the operation object. On the second call you can call [operation cancel] to stop the last operation. Start a second operation to i.e. decrease the pitch and also store the new object.
Btw: What you are doing right now is very bad since you "block the main thread". Calculations that take some time should not be directly executed. You should probably also have a look at NSTimer to make your code independent of the processor speed.
Don't use a while loop; it blocks everything else. Use a timer and a state machine. The timer can call the state machine at the rate at which you want things to change. The state machine can look at the last ramp value and the time of the last button hit (or even an array of UI event times) and decide whether and how much to ramp the volume during the next time step (logic is often just a pile of if and select/case statements if the control algorithm isn't amenable to a nice table). Then the state machine can call the object or routine that handles the actual sound level.