I have been looking here in the forum, and in the wiki, for a Semaphore Lock resembling approach that keeps only the latest instance of macro running. I.e. if a macro is triggered again while running, it should cancel any already running instances of the macro, but keeps the newest triggering instance running. Always giving priority to the newest instance, is another way of putting it.
I was not sure if I should post this in Questions & Suggestions, Tips & Tutorials or here in the Macro Library. As I've ended up with a seemingly well functioning group of macros I choose the latter, but even though I share my approach here as kind of a neatly packed up macro group, I am by no means confident that this is the best way of achieving the desired behaviour, so I'd also love any kind of feedback or further ideas!
By the way sorry for my extremely longwinded post here, I am not good with technical explanations like this, but as I find the approach I share here to be both useful, and kind of cool, I wanted to try to explain/document it as best as I could.
ă…¤
I might be mistaken, but I at least have not been able to find any way of simply achieving the behaviour explained in the first paragraph above. So instead I've worked out an approach that in use resembles the Semaphore Lock a great deal:
ă…¤
This "Semaphore Action Group" lets the user set a unique Semaphore Name in the Execute Subroutine Action. In this group an instance variable is also set to the currently executing instance UUID.
This Subroutine creates a global "Semaphore Lock Variable" from the Semaphore Name chosen by the user (this variable is prefixed with "SemaphoreLockVariable_" to prevent clashing with any already existing global variables). This Semaphore Lock Variable will hold both the current and last executing instance UUID – if triggered more than one time) – this Subroutine will cancel the last executing instance, but keeping the latest executing instance running. The Subroutine also executes an asynchronously run Submacro that waits until the latest executing instance of the caller have finished running; before it clears the Semaphore Lock Variable (setting it to the empty string).
ă…¤
I am sure something like this could be achieved simpler. And I know for a fact that it could have been achieved without running three levels deep. But I choose to set it up in this way so that it would make the user experience more closely resembling that of an actual Semaphore Lock action. I choose to use a Subroutine for the second level, over a Submacro, for this very reason (as it have less clutter on the caller end, where the semaphore lock name must be entered). And since Subroutines cannot be ran asynchronously I then in turn had to also call a Submacro, that asynchronously waits for the caller to finish before clearing the global variable.
ă…¤
There is no problem using a copy of the same "Semaphore Action Group" (calling the same Subroutine and Submacro) on different macros, as long as the Semaphore Name is set uniquely in each of the macros you want to be able to run simultaneously. Setting different macros with the same semaphore name is also possible. But if another macro with the same semaphore name is triggered it will cancel the already running macro midway. This is of course the very nature of this approach, but I just thought I'd mention it, as a macro left in a half-finished state can possibly lead to some unwanted behaviour.
ă…¤
Here is the macro group containing the Level 1 "Semaphore action group", the Level 2 Subroutine, and the Level 3 Submacro:
Semaphore- Keep Only Latest Instance Running Macros.kmmacros (166 KB)
(v11.0.2)
ă…¤
DEMONSTRATION:
The macro group also includes two macros demonstrating this alternative variant of a semaphore lock.
Each of these macros are set up to run for 10 second, counting from when it was last triggered. By triggering these demonstration macros numerous times, less than 10 second apart, you will be able to observe how only the latest instance is kept running (especially if you check the KM menu bar menu to see which macros are currently running (the asynchronously ran Submacro(s), waiting to clear the Semaphore Lock Variable(s), will of course also show up in this list)).
Each of the demonstration macros are set up with Semaphore Names unique from each other, demonstrating that they can both run simultaneously. If you set them both to the same name you can observe that only the last triggered macro is kept running. But beware that the demonstration macros I've set up each rely on a global counter variable only cleared at the very end of the macros, so if set up with the same semaphore name, one of the macros will not run to it's end, and this global counting macro must then be cleared manually (e.g. by running the last canceled macro once more to completion).
ă…¤
PS: As a tip, if you like this shared approach, I recommend saving the action group from the L1 macro as a "favourite action" (right click, Add to Favourites), making it easy to implement it like you would any other semaphore action.
Edit: Fixed a small mistake in one of the demonstration macros