A simple way to run only one process per application in C++
Some times it might be necessary to limit the number of processes running at the same time for a certain application. There might be several reasons for this, to prevent data corruption for example. This is a simple cross-platform way to do it.
We’re going to use C++ 17 and the new filesystem library, this will permit us to minimize the use of ifdef
since we won’t have to use the POSIX or Windows APIs that much.
Our solution will work roughly like this:
- On launch read the lock file to check if there are other processes running
- Write the current process ID into the lock file
- On close remove the current process ID from the lock file
In case our applications crashes obviously it won’t be able to remove its process ID from the lock file but the solution we’re going to use won’t have to worry about that.
We’ll get the includes out of the way right now, so if you want to try the code after each step you’ll have no issue. These all are you need:
|
|
Lock File & Process ID
First of all we’ll need to reliably retrieve the path of our lock file, and keep using that all the time. This is where we’ll use the new filesystem library, luckily it offers us a function that returns a path to the OS temporary directory. We can name our file however we want, but it’s important that its name is unique and doesn’t change at runtime.
|
|
As you might have noticed we declared our file
variable as static
, this will initialize the variable only the first time that piece of code is executed and never do it again during the life time of the program. You can find more information on static local variables on the official documentation.
Now we need to know our current process ID, to do that we must use the OS APIs and is one of the few spots where we have to use ifdef
, on Windows we’ll use the PSAPI, on the other platforms we use POSIX.
|
|
There’s nothing magical about this function, it returns the process ID and that’s it.
Lock and Unlock
The two previous functions are enough to handle the lock and unlock steps. The locking is pretty straightforward, open the file, append the current process ID and close the file.
|
|
The unlock is a bit more complicated instead; it has to open the lock file, read all the process IDs, remove the current process ID and write back the others.
|
|
This are the building blocks to handle our lock file, but we need to put it to use or it would be kind of pointless to just save the running processes of our application.
Am I Alone?
Our goal is to verify if there are other processes of our application running so we’ll use the lock file to do just that. Our isOnlyInstance
function will return whether there is another process running by searching in the currently running processes if there is one with the same ID as the ones in the lock file.
|
|
Now we need to implement our processList
function, as its name clearly says it returns a list of running processes. This is another spot for ifdefs
.
On Windows we’re using the EnumProcesses function from the PSAPI, it expects a DWORD[]
that will be filled with the running process IDs, the size of that array and a LPDWORD
, that is a DWORD*
, that will contain the number of bytes returned in the processes array. A DWORD
is a Windows typedef
for unsigned long
. We then iterate the array of IDs and return it as a vector of strings.
Instead on other platforms we take advantage of /proc
, that contains a folder named after each running process ID, who themselves will contain the informations of that process. We won’t need more informations other the IDs so we just iterate the directories in /proc
, push into our vector and return.
Here we’re using again the filesystem
library, notice how convenient it is to iterate a directory content using a directory_iterator. p
in this case is a directory_entry that we convert to path to retrieve the directory name, the process ID in this case.
|
|
Usage
Now that we have all the pieces we can put our mini library to use.
|
|
This is just a minimal example, it prints the process ID and notifies you if another process is running, then locks the current one and waits 5 seconds before unlocking and terminating.
What about crashes though?
Crash and Burn!
The code as it is fine and working, we could stop here and call it a day. The problem is that in case of crashes some IDs might be stuck in our lock file until the next reboot, if the user rarely reboots and the app crashes several times — I hope it doesn’t — our lock file might grow a bit. This should be rare but nonetheless we can handle that by cleaning the lock file ourselves.
Our cleanup function will read all the IDs from the lock file and rewrite it removing those not currently running. To use it we can just call it atop our main
and that’s it, but it’s not strictly necessary.
|
|
Final thoughts
Now we’re pretty much done, that’s nothing much to do here, you can find an example project on Github. Feel free to use it for your project, the license is pretty permissive.
Some of you might ask why we went through all the trouble of saving our process IDs to a file instead of searching for a process with the same name, that wouldn’t be the most efficient way though. Neither Windows' PSAPI nor POSIX offer a way to get a list of processes given its name, so we should have iterated all processes every time we open our application to know if there was another one running. Using the lock file mechanism we know right away if another instance is running, if it’s empty just let it be, instead if there is one or more IDs we verify if at least one of them is the list of running process IDs.
Thank you for reading this trough the end, I hope you enjoyed. If you have anything to say feel free to leave a comment.