Single program instance

Posted on July 28th, 2009

You can follow any responses to this entry through the RSS 2.0 feed.

I don’t know why, but this is often overlooked by many people so I’ll put it here.
When you create a program on Windows, you use mutexs to ensure your application is running as a single instance.

When you create a script or program on Linux and similar systems, you use file locks. When the process dies, the lock get lost. So you don’t need to make a pid file and check the date and so on, like i’ve seen too many times. It’s unreliable, slow, etc.

The one thing you want to make sure with all these methods, is that your lock file has a unique, reserved name, only used by you. Same for the name of the mutexes!

In plain C:

We use the fnctl “file descriptor manipulation” functionality that let us lock files.

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
 
#define LOCK "/var/lock/myprogram.lock"
 
int main(void) {
    int fd = 0;
    for(; i > 0; i--) {
        if((fd = open(LOCK, O_RDWR|O_CREAT|O_EXCL, 0444)) == -1) {
            fprintf(stderr, "Program already running\n");
            return 1;
         }
    }
 
   //Program code...
 
    close(fd);
    unlink(LOCK);
    return 0;
}

In a bash shell:

There is unfortunately, no fcntl support in bash. So there’s a small program, called flock.

It uses the flock (as the name implies) function instead. It is usually part of the util-linux package and it’s usually installed by default everywhere. Note that, the lock file won’t be deleted. One difference of flock, is that it will not work for example, on NFS.

A small work-around could be to use the above fnctl-based locking program with an exec command as “your code”, and call it from bash.

Due to the nature of file locking, the flock program must wrap around your script, this is done either by letting it start your script:

flock -xn /var/lock/myprogram.lock myprogram

Either by wrapping it inside your script (if you have no control over what calls your script):

(
    flock -ns 200
    # your script...
) 200 > /var/lock/myprogram.lock

In python:

Python is being nice and supports fcntl. Put it at the top of your script’s code or __main__ function.
Fast & easy.

#Check if we are already running an instance
import fcntl, sys, os
#You can use a /var/lock/myprogram.lock and do not store the PID of course
#but since we can do it, its sometimes handful to have it
pid_file = '/var/run/myprogram.pid'
fp = open(pid_file, 'w')
try:
        fcntl.lockf(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
        # another instance is running
        sys.exit(1)
fp.write(str(os.getpid()))
 
# Your code
fp.close()

os.unlink(pid_file)
In C# / .NET:

This method is similar as on Win32, since, well, .NET is originally made by Microsoft right? It’s not so bad anyway.

bool instance = true;
using (Mutex mutex = new Mutex(true, "myProgram", out instance))
{
if (instance)
{
//Your code, just putting visual studio default with WinForms here...
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}

4 Responses to “Single program instance”

  1. Francesco Pretto says:

    Hello. Nice reference, but there are a couple of problems.

    1) flock from util-linux is not a wrapper around fcntl() but flock(), a different and less flexible locking mechanism. This is a problem because the two mechanisms are mutually incompatible. At the moment, there’s no shell wrapper for fcntl();
    2) That fp.close() should actually remove the lock before entering the critical section. If you remove it, or move it just before the os.unlink(), the code should be fine.

  2. Francesco Pretto says:

    Forgot: in 2) , the fp.close() in the PYTHON script.

    Greetings,
    Francesco

  3. kang says:

    you’re correct about python, i haven’t noticed it
    about flock/fnctl, it is just a misunderstanding, the flock program achieves locking (using flock) by wrapping around the script. i clarified it ;)

    thanks!

  4. Francesco Pretto says:

    Yup, I noticed the misunderstanding now. Would be cool to have a ‘fcntl’ command similar to ‘flock’, so you can have a shell scripted critical section. I workarounded this using your python script and subprocess.call(‘critical-section.sh’, shell=True). The same can certainly be done for the fcntl C program (but don’t need now, so I didn’t investigate more :) .

    Thank you again.

Leave a Reply