The Core Technologies Blog

Professional Software for Windows Services / 24×7 Operation


Q&A: How do I Enforce a Single Instance of my Python Script with AlwaysUp?

How do I enforce a single instance of my Python app with AlwaysUp?
  Hi,

I am trying the 30 day demo to see if AlwaysUp works for my application. I am running a couple of python scripts and so far your application works perfectly. I do however have an issue. It is critical that only one instance of my application runs when I run it from the command line my mutex works as it should.

My code looks like this:

Python code to manage a mutex

I am able to run a second instance from a command line. This will cause some serious misreporting if two applications are running. Is there anything I can do?

Thanks.

— Carl

Hi Carl, thanks for trying AlwaysUp. Thanks also for sending your code, which enabled us to identify the problem quickly. It has to do with how mutex locks work.

Your mutex isn’t visible across sessions

By default, mutexes have “session scope”. That is, they exist only in the login session where they were created. And that has important consequences.

For example, let’s say that Alice logs in to a computer. Obviously, she can start a copy of your Python script just fine. But when Alice attempts to launch a second copy, it fails — exactly as you’ve designed.

Now let’s say that Bill logs into his account on the same computer. Like Alice, he will be able to start only a single copy of your script.

But there will now be two instances of your Python script running on the PC — one for Alice and one for Bill. That’s probably not what you want, right?

In summary, your current single-instance enforcement mechanism will actually allow multiple copies because your application creates a different mutex in each login session.

Now, let’s review why that’s important when running your Python script as a Windows Service.

AlwaysUp runs your application in a different session

To start your Python script at boot, AlwaysUp runs it in the background in the isolated Session 0.

And when you log into your computer, Windows creates a new session for you — Session 1, 2, or 3, etc.

You’re able to “run a second instance from a command line” because the mutex doesn’t do its job across the two sessions.

Use a Global mutex to restrict all instances of your app

Fortunately the fix is simple. Give our mutex a “global scope” so that it applies across all login sessions.

Indeed, all you have to do is prefix the name of your mutex with “Global\” (no quotes). It’s a one-line update to your code, like this:

self.mutexname = app_name + “Global\_mutex_{eaf8379e-5231-412c-92da-a0328eacea9e}”

For additional context, check out Microsoft’s description of the situation in their technical documentation on mutex objects:

 On a server that is running Terminal Services, a named system mutex can have two levels of visibility. If its name begins with the prefix Global\, the mutex is visible in all terminal server sessions. If its name begins with the prefix Local\, the mutex is visible only in the terminal server session where it was created. In that case, a separate mutex with the same name can exist in each of the other terminal server sessions on the server. If you do not specify a prefix when you create a named mutex, it takes the prefix Local\. Within a terminal server session, two mutexes whose names differ only by their prefixes are separate mutexes, and both are visible to all processes in the terminal server session. That is, the prefix names Global\ and Local\ describe the scope of the mutex name relative to terminal server sessions, not relative to processes.

Hopefully that all makes sense now.

Please get in touch if you have any other questions running your Python scripts as Windows Services.

Posted in AlwaysUp | Tagged , , , , , | Leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *