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()); }
February 11th, 2010 at 12:58 pm
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.
February 11th, 2010 at 12:59 pm
Forgot: in 2) , the fp.close() in the PYTHON script.
Greetings,
Francesco
February 25th, 2010 at 1:47 pm
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!
February 28th, 2010 at 1:14 pm
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.