A Half-Dozen Ways to Script a Singleton Application: Only One is Right

Tagged with Regular Expressions

"How do I make sure only one instance of my application is running?" is a frequently-asked question we've never seen answered to our satisfaction. We'll do so, now.

Why it Matters, When it Matters, to Whom it Matters

It's a good question: there are plenty of situations where it's important to have exactly one instance of an application running. A virus scanner or incremental backup utility or mail queue manager are among the examples for which two processes running simultaneously would likely interfere with each other. Any time you set up a crontab, you should analyze the consequence of cron launching a process now before the process from the last cycle has exited: is it safe? Might it lead to a deadlock?

As widespread and important as these situations are, they're frequently handled clumsily, fragilely, or simply mistakenly. While the headline above--"only one is right"--exaggerates slightly, in general a socket-based lock is simply superior to any alternative. Consider first a few of those alternatives:

Time to Leave These Traditions

As best we can tell, much of the advice given in this area originated over a quarter-century ago, and is now stale. Around 1980, Unix programming centered on processes and the filesystem, and singleton solutions were taken from those areas. The sockets application programming interface (API) only appeared in 1983 (the same year Richard Stallman launched the GNU project, and AT&T began to commercialize Unix), and Stevens' pivotal networking book was first published in 1990. Many of the technical solutions from this earlier epoch survive, though, repeated in advice given successive generations.

In software terms, we're after something like

 BOOL is_already_running(Process process_descriptor) {
	 ...
 }

 

One common approach is to scan the process table for a matching process. Early Unix gave little userland support for process-table introspection, so this was often by way of ps, through a pipeline such as

 ps -A | grep $EXECUTABLE_NAME | grep -v grep
 

Variations include clever regular expressions sent to grep to make it more precise.

Among the difficulties with this approach are that

  • The process table might include defunct processes;
  • inadvertent string matches are a nuisance: what if your process has the name mine, but another user is running coalminer?
  • programs often don't show up in the process table under their original names. For security reasons, for example, many applications are manipulated to obfuscate their launch names.

Another common approach relies on lock-files. Program mine starts by checking a mine.pid somewhere in the filesystem that lists the process's own process ID. If it's present, and the pid it names appears in the process table, then that process is probably a previous instance that should block the current one. Otherwise, the current process needs to write its own mine.pid.

Lock files are hard to get right; they're subject to race conditions, process-table "wrap-around" (what happens when the process manager re-uses process IDs?), security fragilities, and other pathologies. Named pipes have potential, in that they can communicate richer information than just a pid. flock is a system-level entry point that manages an advisory file lock. The command-line lockfile built-in provides a variation which relies on semaphores rather than the usual filesystem.

Semaphores are powerful, but poorly portable. While most of the other approaches mentioned here are equally available in any common language, and across common operating systems, semaphores are mostly bound to Unix. Because of their lack of portability, several common high-level languages, including Tcl and Ruby, confine semaphore management to extensions which are not part of the standard language implementation.

Locking of the sort we're after--only one process at a time--is often abbreviated as "mutual exclusion" (mutex). Several mutex libraries or utilities are available, including Daniel J. Bernstein's setlock. From the perspective of a high-level language, such extensions present challenges in packaging and deploying.

Even more esoteric approaches use system facilities like the init process to ensure a specific process appears exactly once in the system process table. While an advantage in specialized circumstances, these solutions typically involve difficult security configuration. Is there no easier way?

Socket Fits Just Right

In fact the standard IP socket has just the right mutex semantics for the one-process-at-a-time problem. Here's a small Perl program that models the behavior we're after:


 use strict;
 use Socket; 

 my $port = 31496; 

 my $proto = getprotobyname('tcp');  

 socket(SERVER, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; 
 setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1) or die "setsock: $!";  

 # grab a port on this machine 
 my $paddr = sockaddr_in($port, INADDR_ANY); 

 # bind to a port, then listen 
 if (!bind(SERVER, $paddr)) {
 print "Oops! There's already an instance of this program running.\n";
 exit(1);
 }
 listen(SERVER, SOMAXCONN) or die "listen: $!"; 

 my $client_addr; 
 while ($client_addr = accept(CLIENT, SERVER)) 
 { 
 }
 

If you launch an instance of this program perl example.pl, it just sits quietly, waiting. Try to launch a second example, though, and the second one immediately reports: "Oops! There's already ...".

There's more to it than that, though: the binding of the port to the process lasts only as long as the process itself. If something kills the first process--if you send it a signal, or it finishes its work and exits, the system loses power, or for any other reason--the socket is immediately released, and it becomes possible to start another process. There's no delicate handling of boundary lockfile cases, or potential to have something go wrong if the filesystem or process table is in an unusual state. If a different user tries to re-launch the application, he also will be blocked; there's no setting to adjust. The semantics of socket management are just right to set up a singleton application.

The assignment my $port = 31496; is a matter of convention; in general, the program can freely choose any socket, not just 31496, itself not already reserved for another use. The neat match between the socket API and our need for a mutex suggests several generalizations. This same program can be the basis of a range of processes, each one acting as a singleton on a different socket. With a couple of obvious changes, it would be possible to launch

 singleton 11000
 singleton 11001
 singleton 11002
	 ...
 

but a subsequent attempt to re-start singleton 11001 would exit.

Also note that some high-level libraries and languages make this coding even easier. Tcl, for example, emphasizes a more abstract TCP API, where a program as brief as

	socket -server dummy 31496
	vwait forever

 

models singleton behavior.

Summary

There are many ways to implement mutexes or near-mutexes in Unix. For management of singleton processes, a simple socket binding is nearly always best, even though common references continue to describe implementations that have been inferior for over two decades.

In December, LDN will focus on driver development. We'll be back then to explain the role of high-level languages in driver programming.

Kathryn and Cameron run their own consultancy, Phaseit, Inc., specializing in high-reliability and high-performance applications managed by high-level languages. While they like any software that works, they particularly like software, like socket-based mutexes, that works well and is simple. Cameron and Kathryn write about high-level languages and related topics in their "Regular Expressions" columns.

 

0
Copyright © 2008 Linux Foundation. All rights reserved.
LSB is a trademark of the Linux Foundation. Linux is a registered trademark of Linus Torvalds