Concurrent invocations of the same macro

fmcpma

Member
Hello all.

Compared to other computer languages and scripting, Macrodroid, as far as I can tell, possesses a peculiar perk inasmuch as it is possible, and it often happens (at least in my macros), that the same macro can be re-invoked while a previous invocation of it has not yet completed and is therefore still running. This can occur repeatedly and in some cases multiple invocations may be all executing simultaneously. In these situations, it is important to antecipate all possible interactions of the macro in question with itself, a problem which is compounded by the fact that all concurrent invocations will be using the same set of local variables.

Although this MD characteristic may sometimes cause some head scratching and needs to be addressed with caution, personally I find it quite useful and elegant, as it makes it possible to place several related functions in the same macro, each called by its own trigger or invocation method and thus identified and separately processed.

What are your impressions and experiences regarding this matter? How do you usually handle it? Do you think this system works well as it is or it should be altered in any way?
 
Last edited:

fmcpma

Member
Come on, now! I know the matter is of the utmost interest, but please calm down, there is room for everyone. All questions will be answered (by someone)!

Well, to get the ball rolling, let me mention the Cancel Macro Actions action. I used to use it a lot as an alternative to:

Code:
some_code
if some_error_detected
    error_message1
    clean_up
else
    more_code
    if some_other_error
        error_message2
        clean_up
    else
        yet_more_code
        …
        and_so_on_and_so_on

I rather prefered:

Code:
some_code
if some_error_detected
    error_message1
    clean_up
    Cancel Macro Actions
end if
more_code
if some_other_error
    error_message2
    clean_up
    Cancel Macro Actions
end if
yet_more_code
…
and_so_on_and_so_on

Of course, I wanted to avoid this excessive indentation caused by error handling, which is unsightly, specially in a narrow phone screen. But then I realized that Cancel Macro Actions would not only abandon that thread of execution – that invocation of the macro – but also all others that happen to be executing at the same time and should proceed. Actually, this behaviour is what the action's name implies and what the wiki says; I just wasn't paying attention. Moreover, it could be very useful in certain situations.

Still, I may have to change the way in which I handle errors and so I'm left wishing for an action that cancel only the current macro invocation/thread and not eventual others.
 

Endercraft

Moderator (& bug finder :D)
I agree in that certain situations cancelling only the current thread would be very useful.

I needed this in one of my macros recently and the only real way I found out to do this was to wait until trigger with a system setting change that shouldn't ever happen.
I guess I could have used a broken day time trigger to wait for a day that would come in lots of years.
 

Dm114

Well-known member
I agree in that certain situations cancelling only the current thread would be very useful.

I needed this in one of my macros recently and the only real way I found out to do this was to wait until trigger with a system setting change that shouldn't ever happen.
I guess I could have used a broken day time trigger to wait for a day that would come in lots of years.
...or Battery level lower than 0
 

Endercraft

Moderator (& bug finder :D)
...or Battery level lower than 0
I did think of it but at the time I didn't really want to break the JSON.

It's not like I already have a macro that check if the battery temp is more than 1000°C. It will never trigger unless some bug because at that point it would just destroy pretty much everything and itself (how do you even get a battery that heated ?) and cause a fire maybe.
 

fmcpma

Member
I agree in that certain situations cancelling only the current thread would be very useful.

I needed this in one of my macros recently and the only real way I found out to do this was to wait until trigger with a system setting change that shouldn't ever happen.
I guess I could have used a broken day time trigger to wait for a day that would come in lots of years.
Yes, I thought of something like that too, but I didn't really like the idea of the thread being still there, just hanging forever in limbo. I also tried to cause an error that would stop processing, like calling an inexistent macro or action block but execution just continues past the error. I even tried the old division by zero trick, which usually stops any program dead in its tracks, but no luck :-(
 

fmcpma

Member
Well, I dreamed up another way of stopping the current thread. It's quite stupid but it works!

First set up an action block with just 2 actions:

Wait 100 ms (your mileage may vary)​
Press Back Button​

Then, in your macro, at the point you want to stop execution, call the previously created block, unchecking the Block next actions option.

Finally, follow that action with a Confirm Next action.

This last action will display a dialogue asking if you want to proceed execution. If the Back button is pressed it takes it as a "No" and so execution stops. And, of course, pressing the Back button is what the previously called action block does right after the dialogue is displayed.

You may just see the Confirm Next dialogue appearing and disappearing rapidly but that is it. That thread stops and the others continue.
 

FrameXX

Well-known member
It's important to note that MacroDroid action blocks are threaded, so that every call of an action block runs in its own thread. If you are eager to start a set of actions that you need to be processed in isolation, action block is your way to go.
 

fmcpma

Member
Well, I dreamed up another way of stopping the current thread. It's quite stupid but it works!

First set up an action block with just 2 actions:

Wait 100 ms (your mileage may vary)​
Press Back Button​

Then, in your macro, at the point you want to stop execution, call the previously created block, unchecking the Block next actions option.

Finally, follow that action with a Confirm Next action.

This last action will display a dialogue asking if you want to proceed execution. If the Back button is pressed it takes it as a "No" and so execution stops. And, of course, pressing the Back button is what the previously called action block does right after the dialogue is displayed.

You may just see the Confirm Next dialogue appearing and disappearing rapidly but that is it. That thread stops and the others continue.

Come to think of it, it's possible to go one better. Suppose the action block mentioned above, containing the Wait and the Press Back Button actions, is called Press_back_button_with_delay. We can now create another action block containing the two actions which before we were placing in the macro itself, namely the Action Block action, which should now call Press_back_button_with_delay (don't forget to uncheck the Block next actions option – asynchronous call), and the Confirm Next action. We can simply call this second action block "Stop".

And there you go. We now have a Stop instruction in the form of an action block that can be called whenever we want to stop the execution of a macro thread/invocation, allowing any others working in the same macro to continue. Of course, this Stop action block should be called synchronously, that is, leaving the Block next actions option of the Action Block action checked; otherwise, all we get is a mess.

Yes, there is still the slight issue of the Confirm Next action dialogue briefly appearing and disappearing – in my phone it's just a flickering of the screen; for others, adjusting the time in the Wait action might help.
 

barrelman

New member
On one of my macros I was getting a double "Power Connected Any" trigger when the charger cable was connected. This was very quick, one immediately after the other. This caused 2 log events, the 1st with the correct data, but the 2nd [redundant] without the correct data because the 1st trigger changed some variables. I needed to cancel the macro actions so I would only get 1 trigger.

T: Power Connected Any
T: Stopwatch powerDelay: 1s [1 second]
A: If Trigger Fired: Power Connected: Any
A: Stopwatch (Reset and Restart) powerDelay
A: Else If Trigger Fired: Stopwatch: powerDelay: 1s
A: Set Variable(s) ...
A: Log Event ...
A: End if

So now what is happening is the 2nd trigger will reset and restart the powerDelay Stopwatch before the 1st powerDelay Stopwatch even has a chance to trigger. And I get 1 log event.

Maybe this stopwatch method can be adapted for your situation.
 

Dm114

Well-known member
On one of my macros I was getting a double "Power Connected Any" trigger when the charger cable was connected. This was very quick, one immediately after the other. This caused 2 log events, the 1st with the correct data, but the 2nd [redundant] without the correct data because the 1st trigger changed some variables. I needed to cancel the macro actions so I would only get 1 trigger.

T: Power Connected Any
T: Stopwatch powerDelay: 1s [1 second]
A: If Trigger Fired: Power Connected: Any
A: Stopwatch (Reset and Restart) powerDelay
A: Else If Trigger Fired: Stopwatch: powerDelay: 1s
A: Set Variable(s) ...
A: Log Event ...
A: End if

So now what is happening is the 2nd trigger will reset and restart the powerDelay Stopwatch before the 1st powerDelay Stopwatch even has a chance to trigger. And I get 1 log event.

Maybe this stopwatch method can be adapted for your situation.
If you only need to prevent from firing twice, you'd better use general constraint Macro invoked/not invoked recently
 

barrelman

New member
Yes, that's the ticket! Thanks.
T: Power Connected Any
C: Macro(s) Not Invoked [This Macro]: Not Invoked for 1s
This would accept the 1st trigger and not the 2nd. This is what I will use.
OR
T: Power Connected Any
C: Macro(s) Invoked [This Macro]: Invoked for 1s
This would accept the 2nd trigger and not the 1st.

For other situations, this stopwatch method does allow the macro actions to be cancelled [when you want them to be cancelled] before the next trigger is actually invoked after the stopwatch delay. The same stopwatch delay could be re-used throughout the macro whenever you want to cancel macro actions. Just do a final Stopwatch (Reset) before the macro is finished.

T: ?????
T: Stopwatch ?????: 1s
A: If Trigger Fired: ?????
A: Stopwatch (Reset and Restart) ?????
A: Cancel Macro Actions [This Macro]
A: Else If Trigger Fired: Stopwatch: ?????: 1s
A: ?????
A: End if
 
Top