Multiple Volumes Unmounted, Run Macro Once

Hi all,

Just curious if anyone had a good solution for this.

I have a macro that runs when any of 3 specific volumes are unmounted and then runs another macro that turns off Power to the drive enclosure only when all volumes are unmounted:

The problem is that I normally eject these 3 drives at the same time and thus the macro is running 3 times at precisely the same second and causing an error in the power off script (It's trying to authenticate 3 times at the same time etc. etc.).

I only want the macro to run once, but only if ALL drives are unmounted and ANY drive unmount should trigger it.

If I manually eject each drive individually (and not select all and eject) then it works fine.

Thanks!

Thinks This sounds like a job for Semaphore lock… :thinking:

Or at least, it’s one option.

Then just put a Semaphore Lock action at the front of the macro. Since your macro does a check to see if the three drives are not mounted, you can make the 2nd and 3rd triggered copies of the macro abort after a period of time. VERY IMPORTANT: The period of the semaphore's timeout needs to be shorter than how long it takes you enclosed macro to run, but longer than it takes you to physically disconnect all the drives. Can you accommodate that?

1 Like

I am not sure that aborting the macros would be a good idea.

Imagine that Drive 1 has just been unmounted, and that Drive 2 is unmounted just a moment afterwards, swiftly followed by Drive 3 (trigger 3). If a semaphore lock is used, an instance of the macro will run for trigger 1, and, once it has finished, a new instance will run for trigger 2 and finally a new instance will run for trigger 3. That seems fine.

However, it seems to me that it would be possible for the first instance to assess that Drive 2 and Drive 3 were connected, just a split second before they became unmounted. If the instances triggered by the unmounting of those drives were cancelled between that assessment and the conclusion of that instance of the macro, all the drives would be unmounted and the macro would not be triggered again—so the “Back Drive [Off]” macro would never be called.

I wonder if it might be better to have the macro run a shell script (within an Execute a shell script action) that returns true if <Drive 1 is unmounted> and <Drive 2 is unmounted> and <Drive 3 is unmounted>, in one action.

I have to sleep now though… :yawning_face:

If anyone thinks the above suggestion has merit, please by all means provide an implementation (perhaps based on this post on serverfault.com?).

I don't understand your concern. If the user disconnects the drives in the order "1", "2", and "3", with a tenth of a second delay between each, then the first instance will trigger but not run the macro because 2 and 3 are offline. Then the 2nd instance will trigger but will not run the macro because 3 is offline. Then the 3rd instance will trigger, and will pass the IF condition, resulting in the execution of the macro. No matter what the order is of the disconnection, the third instance will always pass and execute the macro. It is always guaranteed that the third instance will run, since the third instance always represents the final of 3 drives being disconnected.

Thank you both for the ideas.

The Semaphore lock worked. When I select all drives on the desktop and right-click eject, the macro was executing exactly at the same time (x3), so I added the lock and put in a 1/100th of a second for a timeout. I could probably make this slightly longer, I'll do some additional testing.

Did you mean 1/100th of a second? That may work some of the time, but as I said earlier the duration should be longer than the longest difference between drive disconnects, but shorter than the duration of the enclosed macro. I don't know how long your macro takes to run (and you didn't say), but it's likely longer than one second, so a timeout of one second would seem sensible.

I see that you already have a perfectly fine solution; I would just offer another possible approach because I personally hate having to perform tasks with the mouse/trackpad.

So if I were writing this macro for myself and frequently had to manually eject these same three disks, I might do something like this and have the macro eject the volumes for me with a hotkey trigger (or perhaps triggering it by name from Alfred depending on the exact frequency...or even better cron if on a schedule) and then execute the Backup Drives [Off] macro:

1 Like

That's a very logical and robust approach. Stylistically, I would have abolished the LocalDevices variable and just used a Text block in the loop. And to reduce the amount of code I would also have placed the Pause Until action inside the loop and used the Device variable in the Pause Until action. I would have also used a local variable in place of the Device variable. But most of my ideas are just style changes.

1 Like

Thanks for the input. I actually have a similar macro that runs before the macro in question - that handles all the ejects:

I could add a short pause after the detach script so that each drive is disconnected at a slightly different time, thus not needing the semaphore lock, but that would only be helpful when I run this macro and not for manual ejects, i.e. If I select all the drives and right-click + eject.

The main macro with the semaphore lock allows me to offset the actual unmounting event for all cases, as it sees the unmount events and runs a script to turn off the enclosure power after all drives are unmounted.

Why do we care if this macro runs multiple instances concurrently?

Personally I'd put an insta-kill lock on the "Backup Drives [Off]" macro that this macro calls -- that's the one we don't want to run more than once.

Putting the semaphore in "Backup Drives [Off]" also makes it self-contained -- should we want to call it from a different macro we won't have to remember to semaphore that one too.

Fun fact -- you can eject all the volumes with a single command via diskutil:

diskutil eject /dev/$(diskutil list external | grep 'Drive 1 ' | grep -Eo 'disk\d+')

(Note the space after "Drive 1", in case you have "Drive 11" etc as well.)

Ah, I see — fair enough! For myself, I would probably still make a separate macro to eject drives 1-3 by hotkey just to save myself the bother of having to:

But that would be my preference for aforementioned reasons. So long as it's working for you and your workflow, that's all that matters. I only wanted to offer another possible solution in case it was helpful to you or anyone else.

All ideas I'd like to think would've occurred to me had I not thrown this rough draft together in the middle of the night because I agree with all your points and prefer to keep my code neat and tidy.

1 Like

I think I misinterpreted what you meant by “you can make the 2nd and 3rd triggered copies of the macro abort after a period of time” but I understand now that you were referring to semaphore timeouts, which is a point that you had of course gone on to address:

:heavy_check_mark:

So we are in fact on the same page there... except… since it is indeed VERY IMPORTANT that the timing of any semaphore timeout be within tight parameters, I don’t see the point of adding semaphore timeouts. What significant benefit would they bring?

Edit: rather than saying “adding semaphore timeouts” I should say “adjusting semaphore timeouts from the default”.

In theory, all three triggers could occur at the same time, resulting in all three instances passing the IF test, resulting in three copies of the "Executer Macro Backup Drives" to run sequentially. But we don't want that macro to run three times, just once. Therefore, the timeout prevents any simultaneously qualifying instances from getting to that point.

Thanks for your reply! By this point I am confused about what I was confused about. :crazy_face: Before thinking further, I shall have to give my brain a rest. :brain: ← actual size.

Then why not semaphore the problematic macro rather than this triggering one? We never want multiple instances of the "Backup Drives [Off]" sub-macro, but we might want multiple concurrent instances of this triggering macro.

Good question.

The "Backup Drives [Off]" sub-macro has a sister macro "RAID [Off]" that both trigger the final Outlet Control script. The Outlet Control Script previously had a "Pause Until Macro (the actual Script Sub-macro) is Not Executing", which I have since changed to a Semaphore lock (I thought the same thing you did about making this self-contained). Both the [Off] macros might try to trigger at the same time, but now the Semaphore Lock will queue the additional triggers and then eventually run them. I can't set a timeout for the Outlet Control Script Semaphore Lock, as I want BOTH [Off[ macros to run eventually.

I also added a Semaphore lock to the macro in question (Backup Drives [Off] [Auto]) with a time out, because I don't want 3 of these to execute and then in turn execute 3 of the Outlet Control Script macros. Does it really matter if the outlet is turned off and then the script runs again an additional 2 times? Not really, but it's also not necessary.

I really appreciate all the thought you all continue to add to this, it's very helpful.

You had a bunch of amazing posts today. You always impress me.

In this case, I don't think I agree with you. My solution of putting semaphores in the parent macro prevents three sequential calls to the enclosed macro. If you put the semaphore in the child macro, it doesn't prevent that macro from being called three times. This is what I said in post 16 above. But I hate to disagree with you, because I'm usually wrong.