Multiple sleep() calls from multiple flows

Post your questions and help other users.

Moderator: Martin

elektroinside
Posts: 69
Joined: 14 Feb 2019 15:04

Multiple sleep() calls from multiple flows

Post by elektroinside » 06 Mar 2019 05:54

So I've built some flows exclusively for widgets, to have a nice visual feedback about the progress of my functional flows. Basically each toggles the visibility property of an element (a dot, text) with a few hundred millis between the actions, simulating a blinking dot progress, so it's very simple.
I have multiple functional flows which trigger each other based on some conditions, so i can reuse them whenever I want. Each of these flows also have their own widgets with this progress dot described above.
When I trigger a functional flow which triggers another functional flow which triggers another and so on, the progress dots are blinking correctly at most on one widget, on the second it either stays forever on or it doesn't show at all.
I'm using the script action with the sleep command to put a pause between the visibility toggles. If i execute the progress flows manually, it doesn't matter how many times i try, it works, but all of them set the elements to visibility off or on at the same particular time, even though i executed the flows randomly, and each flow acts on a different widget.
It's like all the sleep commands between the actions start a counter at the same time, and not randomly (it should be random, as there's no way i somehow start the flows to sync the sleeps every time i try).
This would be fine, if they would act the same way when the functional flows execute the progress flows. But they don't. The functional flows also have "sleeps" and somehow i think this influences the sleep timers for the progress flows (or even other functional flows), as they either stop toggling the visual widget elements, all the same time, or resume, but not always all of the flows, just a few, and most of the time, only one, the toggling of the widget elements.

I'm seeing multiple issues, but the most important one is that the sleep function is not individually handling all the calls, it's like the sleep function has one reference point for all calls, it's not asymmetric. Plugin timeouts also seems to influence sleep().

Is this behavior by design or it's a bug?

User avatar
Desmanto
Posts: 2709
Joined: 21 Jul 2017 17:50

Re: Multiple sleep() calls from multiple flows

Post by Desmanto » 06 Mar 2019 06:23

I still don't understand what you mean, since I can't see the whole flow. Sharing your flow probably also won't help either, it is probably difficult to share also, since your flow call other flow again. Can you just create another 2 separate flows with widgets to demonstrate the problem?

There are several point to note on :
1. Script/expression has global lock. Only one script action can run at one time. This is to prevent race condition. So if there are tons of script running from all flows, all will be put into queue and executed one by one. If there is loop or somewhat lagging script from one part, it will drag the whole other flow execution. Every other script will wait too long.
2. Pay attention to your glovar. The bigger your glovar, the slower your flow run. Because each glovar will be used as the context before executing an element. I now keep my glovar below 20 KB and if possible I will keep it below 10 KB. (look at variables.bin at your automagic folder)
3. If your flows all using glovar, there might be glovar conflict when you run the flow at the same time. Maybe flow A start the counter at 1. Flow B already at the counter 5. When flow A execute, it change the glovar become 2, and flow B now read 2 also.
4. sleep() inside script has been released from global lock since AM 1.34.0 (if I am not wrong). So using sleep() seems will call separate thread and let the script from other flow to run properly.
5. Execute flows, if checked wait, will wait until the child flow to be finished first. Make sure your flow logic pay attention to this.
6. Widget element changes speed is maximum 2 fps, or the shortest time you can change an element is 500 ms. If you have sleep() less than this, or in other word change the widget more than 2 times persecond, the changes will be ignore, animation seems to be stopped.
Index of Automagic useful thread List of my other useful posts (and others')
Xiaomi Redmi Note 5 (whyred), AOSP Extended v6.7 build 20200310 Official, Android Pie 9.0, Rooted.

elektroinside
Posts: 69
Joined: 14 Feb 2019 15:04

Re: Multiple sleep() calls from multiple flows

Post by elektroinside » 06 Mar 2019 07:18

There's no need to share, i think i got a perfect answer which makes perfect sense, many thanks for this precious info.

The culprit is definitely the global lock combined with the sleep which is not part of the global lock and the queuing mechanism, which is also global, not per flow. Because of all these combined, basically there's no accuracy for the sleep timers, for a particular link between sleep and two actions inside one flow, when running multiple of them, and as you run more and more at the same time, things can get messy. Because sleep timers will expire sooner in the global queue as it normally would when running only one flow (from that one flow's point of view), and because the queue is not built (in my case) synchronously across all running flows and because i'm not controlling/conditioning it at all. Classic async multithreading scenario.
In a very short conclusion, AM is building one big script if you run multiple flows, from all those flows. I did understood correctly, right?

My variables cache file is less than 3kb, so it might not be involved.

Anyways, again, thanks for all these useful info Desmanto.

User avatar
Desmanto
Posts: 2709
Joined: 21 Jul 2017 17:50

Re: Multiple sleep() calls from multiple flows

Post by Desmanto » 06 Mar 2019 11:45

I predict that you use the same glovar name for all flow? (hence this chaos happen) If so, probably you are doing so to save the glovar space. If you need multiple flow controling multiple sleep timers, tag them with something, that you can refer back later. The easiest way is to use glovar map. So every timer will have new key value, such as, "flow A" = 300, "flow B" = 450, "flow C = 120. (in seconds). Even though all flow accessing the same glovar name, but it is splitted according to their flow name. To access the timer, it is not global_timer, but global_timer["flowA"]

But be careful that flow name variable is changed as you call other flow, but trigger persist across multiple nested call.


AFAIK, global lock simply queue each script action. It doesn't rebuild them, but simply run the first queued script (which can contain several lines) until it finish. Then if there are script from another flow is queueing, then it will execute that script until finish, and so on until no active script/expression left in the queue. This is so fast, that we usually don't notice any delay. But once so many script in queue, then we start to notice the slowdown. As I have experienced when I have glovar more than 100 KB.
Index of Automagic useful thread List of my other useful posts (and others')
Xiaomi Redmi Note 5 (whyred), AOSP Extended v6.7 build 20200310 Official, Android Pie 9.0, Rooted.

elektroinside
Posts: 69
Joined: 14 Feb 2019 15:04

Re: Multiple sleep() calls from multiple flows

Post by elektroinside » 06 Mar 2019 13:32

I'm unsure what are you calling "glovar". Can you please rephrase or explain?

Thank you :-)

User avatar
Desmanto
Posts: 2709
Joined: 21 Jul 2017 17:50

Re: Multiple sleep() calls from multiple flows

Post by Desmanto » 06 Mar 2019 16:00

Glovar = Global Variable :D Glovardt = Global Variable Date Time. Too lazy to type it full :D
Glovar map = Global variable with map type, so we can put multiple key value inside.
Index of Automagic useful thread List of my other useful posts (and others')
Xiaomi Redmi Note 5 (whyred), AOSP Extended v6.7 build 20200310 Official, Android Pie 9.0, Rooted.

elektroinside
Posts: 69
Joined: 14 Feb 2019 15:04

Re: Multiple sleep() calls from multiple flows

Post by elektroinside » 06 Mar 2019 17:30

Ah, ok :-D

No, I took the ugly path of creating a progress flow for each widget. I'm not using one flow and pass variables to functions/flows as I'm normally doing, exactly because I did not want to start multiple instances of the same flow to avoid who knows what conflicts. But at the same time, this way, I'm only using lovars (lol) :-D

So I recorded my screen, here: https://youtu.be/6xHFGYMmnhI

In the video, at the bottom of my phone, I'll click on the widget VOL 20 (those VOL widgets control the volume of my 5 chromecast audio gadgets - btw, it's a pain in the freakin' *ss to control volume on chromecast speaker groups). Pay attention at the bottom-left corner of the Vol20 "cast" icon, it blinks for a few seconds (it should continue blinking as long as the video plays.. but it doesn't).

I'll call a functional flow a flow which does something in the background, occasionally displaying a notification (toast message) on the screen, and a progress flow which only does one thing, blinks a dot on the widget.

Each of these widgets on the screen have their own separate "progress" flows, which will blink a dot or some other text char, like at the beginning of the video (or end) on the Vol 20 widget. The Vol 20 functional flow will first trigger (and wait for) Vol 0 functional flow, which also has its own progress flow (which also blinks a dot, in the same place as Vol 20, only on Vol 0 widget of course). You won't see any blinking dots on Vol 0 in the video, because it simply does not show, although Vol 0 functional flow calls its progress flow. So we have 4 running flows so far (2 functional and 2 progress flows). Vol 0 functional calls yet another flow at the end, which queries and displays the current status of the chromecast devices (only volume levels). The "Status" widget also has it's own progress flow and Vol 0 waits for it to end. So in this moment, we have 6 flows running at the same time. Again, you won't see any blinking dots on the "Status" widget, because it doesn't show.
Continuing, after the "status" flow ends, Vol 0 ends, and then Vol 20 will continue setting the volumes to 20% on the chromecast devices. At this point, the frozen blinking dot on Vol 20 widget resumes.

Running just one of these at the same time (basically running one functional flow and one progress flow at the same time) will display the blinking dot (almost) correctly on their corresponding widget. Running 2 of them, will freeze the one which worked, and never display the blinking dot on the second one. Not to mention the third... These volume flows use a plugin (Autocast) to control the volumes of the chromecasts. Plugin have their own timeouts, which somehow influences sleep().

So basically this is what i was I'm talking about in the previous post :)

I'll attach a picture of a progress flow shortly from my phone (I'm typing from my PC now). They are almost the same for each widget, I only replaced the widget name and element to toggle (the visibility).

elektroinside
Posts: 69
Joined: 14 Feb 2019 15:04

Re: Multiple sleep() calls from multiple flows

Post by elektroinside » 06 Mar 2019 17:44

Here's a progress flow:

http://automagic4android.com/flow.php?i ... 34d5cb898a

I accidentally uploaded one for another widget, but it's the same for each, except for the widget name. I know it's a mess and ugly, but it's not meant to be shared. I'm try catching each action, almost (simulating with exceptions), because for some reason (and occasionally) these progress flows crash, throwing some Java exceptions. I'm yet to see why.

elektroinside
Posts: 69
Joined: 14 Feb 2019 15:04

Re: Multiple sleep() calls from multiple flows

Post by elektroinside » 06 Mar 2019 18:28

Here's an exception (and a screenshot with almost two simultaneous exceptions):

06.03.2019 19:50:51.530 [Volume20 Progress] Action 'Script: sleep(500);'
java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:152)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1285)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:458)
at ch.gridvision.ppam.androidautomagic.simplelang.a.j.a(SourceFile:56)
at ch.gridvision.ppam.androidautomagic.model.a.du$1.a(SourceFile:132)
at ch.gridvision.ppam.androidautomagic.model.a.du$1.c(SourceFile:118)
at ch.gridvision.ppam.androidautomagiclib.util.ci$1.run(SourceFile:40)
at java.lang.Thread.run(Thread.java:764)
Attachments
IMG_20190306_202702.jpg
IMG_20190306_202702.jpg (847.04 KiB) Viewed 17725 times

elektroinside
Posts: 69
Joined: 14 Feb 2019 15:04

Re: Multiple sleep() calls from multiple flows

Post by elektroinside » 06 Mar 2019 22:45

If i manually execute all progress flows, this is how it looks like:

https://youtu.be/zzgvFwvBB6s

But the logs.. oh boy.. a concert of errors :-)

Maybe Martin can suggest something? Reading about this error on the net, I'm unsure I can do something about, other than not to execute multiple flows with sleep() scripts.
Attachments
IMG_20190307_004448.jpg
IMG_20190307_004448.jpg (846.59 KiB) Viewed 17697 times

Post Reply