Semaphore Lock for Dummies

No problem! I really appreciate your constructive and detailed feedback.

Agreed. Rewritten removing "primary purpose".

Well, this is a complex sentence, and another user remarked about confusion of the name. To me, having the "(using the same Semaphore Name)" immediately following " Place a Semaphore Lock Action" removes all ambiguity.
So, I'll have to disagree on this one, and keep it as is.

Agreed. Change to use your text.

Seems like this is made clear by:

The lock will remain in place, preventing the other macros from executing, ==until the first macro completes== or until the semaphore is unlocked with a Semaphore Unlock action.

So I don't see the need for any change there.

OK, is this clearer?

You can also cause all additional macro instances to be immediately cancelled by setting a very short timeout (1 hundredth of a second) for the ==Semaphore Lock Action==. ==When the Semaphore Local Action times out, it cancels the pending execution of that macro instance.==

"Macro instances" can be confusing. Basically, every time a Macro executes, it is an "instance".
So in this context, "macro instances" refers to both additional instances of the same macro and instances of other macros that use the same named Semaphore Lock Action.

I will add a footnote defining "macro instance"

Actually that statement needs to be above the bullet list, which is where I have put it.

Thanks again for your helpful feedback.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To All:

Based on and inspired by the feedback from @Shoshanna, I have made another major revision.
Rather than posting a quote of it here, please read the entire article so that this section is understood in context.

All constructive comments welcome!

Semaphore Actions -- KM Wiki

Maybe I ate some paint off the wall in the 70s ( "Lead paint" Get it? :sweat_smile:) but I still don't understand "You can also cause all additional macro instances to be immediately cancelled by setting a very short timeout (1 hundredth of a second) for the Semaphore Lock Action. When the Semaphore Local Action times out, it cancels the pending execution of that macro instance."

Can you perhaps give an example of why would it be desired to cancel all additional instances? Sorry but I've read it about five times and still don't get why would this be necessary/desired.

IME, this has most often occurred when a Macro is triggered automatically by something other than a direct user trigger, like a hot key.

For example, I have a Macro that moves the mouse to the current window whenever a new window is activated, OR the current window is moved. So, if I am dragging a window from one screen to another, I don't want the Macro to actually execute with every minute movement of the window. So I have a Semaphore that cancels new instances. I don't want to process any new instances until the current one is completed, AND I don't want to queue up instances.

I hope that makes sense.

I think I understand now but I'll fully understand it once I actually make use of this technique. By the way, interesting macro about moving the pointer to a newly activated window. What's the benefit of this if I may ask? Is it to save some wrist by having to move the mouse less (which over time I can see that making a significant difference).

I have two large 27-monitors, and I can easily switch to another app in the other screen, and having the mouse automatically follow this is very, very useful and efficient. Not only does this macro move the mouse to the new active window, but it moves it to a preset, relative position for that specific app and window.

1 Like

Basically, any time you have a macro that should only be running one instance, but that takes some time, and that could be triggered while it is already running.

For example, say you have a backup macro that runs every hour, but occasionally it has to backup so much stuff that it takes more than an hour. You would not want it to start a second backup process while the first one is still operating. So you add a Semaphore Lock to that start of the macro.

Now you have a choice of what you want it to do in this instance. If you want it to just skip this backup and wait until the next hour, then you set the timeout to be very short. So as soon as the next hour ticks over, the macro will start and immediately timeout.

Alternatively, say you want the backup to happen when the first backup finishes. In that case, you leave the timeout long - the macros will stack up and then run sequentially. Presuming the second (and further) backups are quicker, when the first one finishes, the second one will run, then the third one, and when all have run the next hour tick will start the next backup.

I trust that is clear?

2 Likes

Yes. Your examples do help understand the functionality of a semaphore.

I appreciate you taking the time to explain.

I want to throw in my own two cents. Any time a macro deals with hardware, you should use a semaphore to prevent any other macro (including the same macro being run twice at the same time) from accessing that same hardware. That's because simultaneous hardware access by two running macros is usually a bad thing. So here are some common semaphore actions that I use:

image
and also:

image

And since macOS also has things that can be properly handled only once at a time I also use these semaphores: (if you are only reading the item, you might be able to get away with omitting the semaphore, but I like to be safe so I use them even if I'm not sure)

image
and:

image

and: (this next one helps to prevent two macros from trying to send two SMS messages at the same time. Why would I need that? Because I use Send SMS to send myself error messages such as when a macro decides to abort. It's entirely possible that two macros could decide to abort at the same time resulting in two concurrent calls to Send SMS action)

image

And since a macOS application cannot be accessed concurrently by two running macros I also use semaphores to lock an application that I may be controlling with KM either through menus or keys or whatever:

image

and:

image

However there is a problem. The problem is that the KM GUI doesn't allow you to search for semaphore strings exclusively of KM actions. In other words, the editor will find both occurrences when I'm looking for one or the other. As a result, I don't actually call my semaphores by the same name as the action. Eg, my "Mouse" Lock semaphore is actually called "Cat". My keyboard semaphore could be "Piano". And "Safari" could be "Expedition". But I didn't show that to you in these examples because that would be confusing to most readers.

In most cases there's no need to unlock the semaphore because it automatically unlocks when the macro ends. But sometimes you want to lock something only for a single action, in which case it might be appropriate to unlock the action in the same macro. Eg:

image

In the above example no other macro (including a copy of this one) would be allowed to quit and relaunch safari until this one is finished. It might not be the best example but it should get you thinking.

In general using semaphores to lock KM macros from accessing a limited macOS resource or a piece of hardware is good, safe programming. If I recall correctly, I was nagging the author of KM to implement semaphores, and maybe it's presumptuous to assume that he did it because of my requests, but I like to imagine that's the case.

2 Likes

Ok I must be even more of a dummy since I can't figure this out.

Does the Semaphore go at the top of the macro even if there are several steps / groups. Or does it go in between each action.

Putting it in context. I have a macro that SSH's into my 3 Raspberry Pi's so I don't want to be able to use a macro to launch any other apps or anything while its singing it. I have attached some of the macro for context.

Rasberry Pi Macro.kmmacros (58.8 KB)

Just came across this as I had a macro launch 50 times triggered by window name change, and even though there was a semaphore, everything was canceled by the KM engine with this in the log.
More than 50 simultaneous macros - aborting everything

So now, thanks to your succinct and patient explanation, my semaphore has a timeout as well.
And thanks to all others who contributed here.

Have a wonderful day.

1 Like

I think the semaphore acts like a gatekeeper and does not allow the process to proceed. So it can be anywhere in the macro. And a timeout can cause the whole macro to abort if the gate cannot be opened in the timeout interval.

The experts here will know more, and I might well be wrong. But the above is my understanding.

You are welcome.

Semaphores, when misused, can actually cause the KM Engine to terminate all macros. But that's actually better than having two conflicting macros run, resulting in who-knows-what kinds of errors. So when the KM engine aborts 50 macros, that's actually a positive feature of using semaphores, not a bug.

When the KM engine stops, that may seem like a bad thing to some people, but it's like having an ASSERT action that tries to validate a condition, and that condition isn't met, so the program stops. When an ASSERT action causes a program to stop, that's not an accident, that's an intentional halt, just like when the KM engine stops 50 simultaneous macros caused by semaphores.

That's my opinion.

1 Like

I'm wondering if the Semaphore lock could be used in this case...?

I have a macro that (long story short) can end up triggering itself. In order to avoid this, I included the following at its start:

image

The Break Profile Loop submacro provides a 1 second window within which the macro will not run again:

image

I've read through the wiki, and I don't think the Semaphore Lock can help me here, but as my current method feels slightly inelegant, I thought I'd check.

The way you've written it, there will be a one second period of time when the parent macro will not be execute. You didn't indicate what the trigger is/are for the first macro, but that doesn't really matter. I see no indication of why you want a "one second period of disabling" the parent macro, but I guess the reason doesn't matter either. What I sense that you might not be realizing is that you can use semaphores to get a very similar effect.

For example the parent macro can contain this action: (notice the options... and notice the greyed-out semaphore action under the options...)

And in your sub macro you can do this:

Notice that I used the semaphore unlock action above.

I believe this should give you the same overall result, without needing your IF action.

Bear in mind I'm not really sure why you are doing this. It doesn't quite add up. Why are you trying to disable the parent macro when the sub-macro executes some code? I can't see any reason why you would want to do this. So I'm not sure if this is the best solution for you.

Thankyou for the reply. My brevity was an attempt not to derail this thread with a detailed description of the specific macro, but as you asked, I'll explain.

It's a Stream Deck profile switching macro that I developed for use with Logic Pro plugins. I've been trying to make it easier for other people to implement, as I've received quite a few messages about it. One of the complications is the loop-breaking submacro.

  • When a whitelisted plugin window is at the front, the macro triggers.
  • It opens a blank app, which in turn triggers a Stream Deck profile for that plugin.
  • Window focus momentarily switches to this blank app and then back to the plugin window in Logic Pro.
  • The macro sees the plugin window again and re-triggers. This loops over and over without the Break Profile Loop submacro.

I tried the Semaphore Lock as you kindly described, but it doesn't prevent looping. That's ok because, despite its inelegance, my method does work reliably.

I don't know what you mean by "looping." I don't see any loops. All I intended to show was how to use semaphores to implement the same thing as your example code. But my code can't take into account any code that you haven't included in your screenshots.

I understand that you want to show only the minimum amount of code that's needed. That's fine, but I'm not sure if it was enough, if my code isn't equal to yours.

For clarity, I explained the looping here:

"The macro sees the plugin window again and re-triggers. This loops over and over without the Break Profile Loop submacro."

I think I'm getting a better idea of what the Semaphore is and isn't used for, so thanks again.

That's true, and I apologize, but I did write my code before you wrote that. Also, I still don't know what a plugin window is, or what a blank app is. So I'm not qualified to solve this.

1 Like

How to do this, I still don't understand

Read this page to find out about locks. The important part for you at the moment is

  • Place a Semaphore Lock Action (using the same Semaphore Name) as the first action of each Macro that you wish to prevent executing simultaneously.