Semaphore lock timeout bug in 11.0.2 & window move and resize size bug in 11.0.3

Hello,

I am noticing some strange behavior with Semaphore Locks and Move and Resize Window in recent versions on KM. As a test I have two macros that will tile a window to the left or right half of the screen.

11.0.1 has the behavior that I think is most desirable. The macros can be run quickly with or without a Semaphore Lock and there is no flickering.

11.0.2 introduces the difference in behavior between the macros with and without Semaphore Locks. With Semaphore Locks, the macros can not be run repeatedly very well. Without Semaphore Locks there is no flickering.

11.0.3 with Semaphore Locks the macros behave the same as in 11.0.2. However, without Semaphore Locks, there is a new behavior where the window is resized/moved a couple of times after the hotkey trigger is pressed. This is what I am calling flickering.

Here is a link to download videos of the three different behaviors as well as the macros used:
https://rfm-my.sharepoint.com/:u:/g/personal/graceson_molecularvista_com/Ef-wP3FBCJBKpLsiP4u3PW4BgFuRLGVRTP4FpvcFfz2L2w?e=q2fQJe

This is due to a bug in macOS, and Peter had to change how Keyboard Maestro behaved when moving and sizing windows.

-rob.

3 Likes

That reminds me, I should have added my OS version. I'm running MacOS 13.7.

Which part of what I'm showing do you think is related to this window positioning issue in MacOS? Because part of what I'm showing is a change in behavior between macros that have a Semaphore lock and ones that don't. Because that is one of the variables I was changing, I was thinking that something changed with that rather than with the window resizing function alone.

There is this other "flickering" issue as well, which I think is probably something in the window moving and sizing function alone since I only see that when there are no Semaphore locks and only in version 11.0.3.

Sorry, I was referring to the flickering thing…and some of those threads (in the linked thread there are more) go back to before or at least the time of macOS 13.

I don't understand why semaphore locks would make a difference, but I'm pretty sure this is very much related to the bug and the changes Peter made (in 11.0.2 or .0.3, I forget which).

-rob.

1 Like

Yeah, it could be some weird interplay between the changes in the window sizing function and the semaphore lock, since I think the bug happens with the two together.

I just realized that my screen captures didn't show them, but in the 11.0.2 and 11.0.3 videos where the semaphore locks are present and the window is not moving properly, there are a lot of timeout notifications, even though the timeout is 0.01s and I'm probably not pressing the buttons that quickly.

If you read the thread @griffman linked you'll see that Apple's window-moving APIs are not very good (my words, not Peter's!) and KM has to make multiple calls to the API to get the move "right".

Each call takes a non-zero amount of time, which means the action takes multiple non-zero amounts of time, which means the semaphore lock is set for that long, which means you are seeing timeouts even though you aren't re-triggering the macro that quickly.

Log the MILLISECONDS() immediately before and after the "Move Window" action and (remembering that those logging actions themselves take non-zero time!) you'll see what I mean.

So your macro is behaving "as intended" wrt timeouts, that behaviour being forced by the macOS APIs, and all you can really do is turn off the notifications and wait between keystrokes.

And it's those timeouts, and the lack of them, that cause the other behaviour. With the locks you can't quickly repeat the macros -- they're locked! Without the locks you are trying to move a window that hasn't yet completed being moved -- flicker.

4 Likes

Ah, I think that makes some sense now, especially given that this delay seems to have been added in KM 11.0.2.

The first instance of the macro locks the semaphore for ~0.3s because it won't unlock until the macro is done running. If that is the only instance that tries to run, then there are no timeouts or notifications. If I try to run the macro again within that ~0.3s time, then the second semaphore gets blocked by the first and times out after 0.01s, leading to a notification.

However, it seems like the blocking I am experiencing is much longer than the ~0.3s expected.

I timed the "Move Window" action in KM 11.0.3 by writing MILLISECONDS() to a text file before and after it runs.

Image

I can then change the delay time using:

defaults write com.stairways.keyboardmaestro.engine WindowSetFrameAnimationTime -float 0.0

With a delay of 0, I get a time of about 11 milliseconds! However, if I set the delay to 0.3, which should be the default,

defaults write com.stairways.keyboardmaestro.engine WindowSetFrameAnimationTime -float 0.3

then I get a time of about 1218 milliseconds.

Just to double check that 0.3s is the default value, I repeated the timing after running

defaults delete com.stairways.keyboardmaestro.engine WindowSetFrameAnimationTime

and got a value of 1217 milliseconds.

So, this delay that has been introduced is significant—over 100×!

Also, while it is nice to have the ability to turn the delay on or off using defaults, I find that approach a bit concerning. Now, macros containing window frame manipulations are no longer transportable between different computers. I might have a macro that works best with my local configuration, but because that configuration is hidden and not attached to the macro, the macro is no longer a self-contained unit. It is a small issue, but I think it would be very nice if .kmmacros files contained information about configuration like this.

Because it isn't for 0.3s -- it's for as long as it takes for the OS to manipulate the window into the correct position and size, and that may take multiple attempts.

This is a macOS problem, and KM is doing its best to work round it.

To quote from this thread:

Sadly it's a choice between slow and works (almost always) or fast and works sometimes for some people maybe. Until Apple decide to resolve it.

Only if there's also a time element. And only if that is required to be sub 1.5s (1200ms plus some leeway). And macros that depend on quick actions (whatever they are) are less transportable anyway -- you have no idea of the speed of someone else's machine, the number of foreground and background process they are running, not even the number of KM macros running concurrent with yours. All those things (and many more) will impact on an action's speed of execution.

You have a choice -- have the "Manipulate a Window" action run slowly but (nearly always) reliably, or have it run quickly and frequently fail to achieve the correct position and/or size.

But that's your choice to make for you -- not for me. Which is why a macro shouldn't force such a configuration (you could always include it as a Comment).

Aside from a theoretical interest in the whys and wherefores of this -- is there an actual problem in real world use? Do you often want to (by manual command) move a window twice within a second? Or was that just a demo and this is more a general speed issue when tiling multiple windows?

1 Like

For me, 1.2s is definitely slow enough that I thought something was broken. If I’m moving multiple windows around or make a mistake, then the delay becomes quite noticeable.

The window generally moves much quicker than that -- it's the action completing, positioning being checked, that's slow. So you want retain the current behaviour except when you've made a mistake or are moving multiple windows in the same macro. And there are ways to do those faster -- for example, the "Cancel" action for the first and "Execute a Macro" action for the second.

But a big question is -- why the semaphore lock? The answer to that may change how the problem should be approached.

I guess I don't have a fundamental reason. How would you use "Cancel" or "Execute a Macro" instead?

I think I'm most interested in the case where I have made a mistake and want to re-run the macro on the same window. I guess in that case the currently running instance should be aborted and the new instance should run instead. Does "Execute a Macro" replace a running instance with a new instance by default?

They're not "instead"s.

And if you make a mistake you'd generally "Cancel" and try again. You could, for example, checking the names of all currently executing macro instances and canceling the instance(s) you want to stop. Or just put up with the flickering and let the new macro "override" the old.

Nope -- but it can execute a macro asynchronously so you wouldn't have to wait for window n's manipulation to complete before starting window ms. So that might be handy for moving multiple windows.

Semaphore locks are generally used to either protect "resources" (stopping two instances accessing the same global at the same time or giving conflicting instructions to an app) or serialise operations that might otherwise run concurrently, which is why I wondered why you are using them. You might if, for example, you wanted to move the window into a certain position before clicking at a set point -- you wouldn't a second macro to undo the first move until the click had happened.