4

I have a console application which I am trying to make able to run just once at a time. I have used boost interprocess library shared_memory_object to do that. See the code snippet below,

  boost::scoped_ptr<shared_memory_object> sharedMem;

  try
  {
     sharedMem.reset(
       new shared_memory_object(create_only, "shared_memory", read_write));
  } catch(...)
  {
     // executable is already running
      cerr << "Another instance of this program is running!" << endl;
      return 1;
  }

  // do something

  shared_memory_object::remove("shared_memory");  // remove the shared memory before exiting the application

The thing is that, the method prevents my application from running more than once at the same time; however, let's assume that the user stops the program running, then the memory will not be freed and next time when the user tries to run the program again, it will not run. Do you have any suggestions ?

P.S. C++ console application, OS: Ubuntu (but a solution which will work on the other platforms as well would be perfect ). Thank you

serhatg
  • 312
  • 1
  • 3
  • 15
  • 1
    I assume that when you say that the user stops the program, you mean that the user presses CTRL-C? That will result in the shell sending your process the `SIGINT` signal, which you can catch and handle, for example to clean up resources that otherwise aren't released. – Some programmer dude Nov 12 '14 at 08:45
  • Yeap, that's what I mean as well as just closing the window would cause the same problem I suppose ? Thank you. – serhatg Nov 12 '14 at 08:46
  • 2
    Might be better to use a named mutex instead, because it should be automatically released when the process dies. – cdhowie Nov 12 '14 at 08:55
  • Hmm. In the dupe it's not the accepted answer. I'll flag for a moderator to undo the dupe and I'll move the answer here instead. – sehe Nov 12 '14 at 09:49
  • I did make a search actually, but didn't come across w your answer. I will check it out now, cheers. – serhatg Nov 12 '14 at 10:09

2 Answers2

5

What you need to do is catch unexpected terminations of the program and free the shared memory object accordingly. You can catch SIGINT as follows using the signal.h POSIX header:

#include <signal.h>

void handleSIGINT(int param=0) {
    // Reset the shared memory object here
}

int main() {

   // Connect the signal handler to SIGINT
   signal(SIGINT, handleSIGINT);

   // Etc...

}

And you can catch program termination in the same manner using the atexit() function. Documentation here.

P.P
  • 117,907
  • 20
  • 175
  • 238
  • Thank you, that worked out well, but are there any withdraws of using this method for this purpose ? I mean using shared_memory_object in order to check if another instance of the program is running. – serhatg Nov 12 '14 at 09:08
  • @serhatg Not all signals can be captured (SIGKILL for example). You should really use a resource that the OS will automatically release when the process dies for any reason. Have a look at named mutexes. – cdhowie Nov 12 '14 at 09:10
  • I see, ok I will take a look to mutexes. Thanks a lot. – serhatg Nov 12 '14 at 09:12
  • @serhatg I've teleported my existing answer to this question, so it can serve as a "canonical" answer to this subject. Find it **[here](http://stackoverflow.com/a/26884150/85371)** – sehe Nov 12 '14 at 10:04
  • 1
    Replaced cplusplus.com's manual with posix manual. Hope you don't mind ;-) – P.P Nov 12 '14 at 11:10
4

NOTICE Answer tele-ported from How to limit the number of running instances in C++. It's in place here since it addressess a portable solution using Boost Interprocess and Boost Asio, in a detailed fashion.

Note that the solution is more generic, in that you can use it to limit instances to a specific maximum, rather than just 1

On linux (and perhaps other OSes?) you can use a lock file idiom (but it's not supported with some file-systems and old kernels).

I would suggest to use Interprocess synchronisation objects.

E.g., using a Boost Interprocess named semaphore:

#include <boost/interprocess/sync/named_semaphore.hpp>
#include <boost/thread.hpp>
#include <cassert>

int main()
{
    using namespace boost::interprocess;
    named_semaphore sem(open_or_create, "ffed38bd-f0fc-4f79-8838-5301c328268c", 0ul);

    if (sem.try_wait())
    {
        std::cout << "Oops, second instance\n";
    }
    else
    {
        sem.post();

        // feign hard work for 30s
        boost::this_thread::sleep_for(boost::chrono::seconds(30));

        if (sem.try_wait())
        {
            sem.remove("ffed38bd-f0fc-4f79-8838-5301c328268c");
        }
    }
}

If you start one copy in the back ground, new copies will "refuse" to start ("Oops, second instance") for about 30s.

I have a feeling it might be easier to reverse the logic here. Mmm. Lemme try.

some time passes

Hehe. That was more tricky than I thought.

The thing is, you want to make sure that the lock doesn't remain when your application is interrupted or killed. In the interest of sharing the techniques for portably handling the signals:

#include <boost/interprocess/sync/named_semaphore.hpp>
#include <boost/thread.hpp>
#include <cassert>
#include <boost/asio.hpp>

#define MAX_PROCESS_INSTANCES 3

boost::interprocess::named_semaphore sem(
        boost::interprocess::open_or_create, 
        "4de7ddfe-2bd5-428f-b74d-080970f980be",
        MAX_PROCESS_INSTANCES);

// to handle signals:
boost::asio::io_service service;
boost::asio::signal_set sig(service);

int main()
{

    if (sem.try_wait())
    {
        sig.add(SIGINT);
        sig.add(SIGTERM);
        sig.add(SIGABRT);
        sig.async_wait([](boost::system::error_code,int sig){ 
                std::cerr << "Exiting with signal " << sig << "...\n";
                sem.post();
            });
        boost::thread sig_listener([&] { service.run(); });

        boost::this_thread::sleep_for(boost::chrono::seconds(3));

        service.post([&] { sig.cancel(); });
        sig_listener.join();
    }
    else
    {
        std::cout << "More than " << MAX_PROCESS_INSTANCES << " instances not allowed\n";
    }
}

There's a lot that could be explained there. Let me know if you're interested.

NOTE It should be quite obvious that if kill -9 is used on your application (forced termination) then all bets are off and you'll have to either remove the Name Semaphore object or explicitly unlock it (post()).

Here's a testrun on my system:

sehe@desktop:/tmp$ (for a in {1..6}; do ./test& done; time wait)
More than 3 instances not allowed
More than 3 instances not allowed
More than 3 instances not allowed
Exiting with signal 0...
Exiting with signal 0...
Exiting with signal 0...

real    0m3.005s
user    0m0.013s
sys 0m0.012s
Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thank you for the answer. I couldn't manage to compile it yet because of some errors related to asio.hpp, but I didn't quite understand how the program will react when kill -9 signal is sent. Are we able to catch this signal or not ? If we are, then my problem with using shared_memory_object will be solved basically. Thank you. – serhatg Nov 12 '14 at 10:41
  • @serhatg [The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.](http://man7.org/linux/man-pages/man7/signal.7.html). See http://stackoverflow.com/a/3908710/85371 for constructive workarounds – sehe Nov 12 '14 at 10:43
  • I see, what does this code snippet do in such a case ? As you said, there is much there :) Is the semaphore being released when the process is killed ? – serhatg Nov 12 '14 at 10:49
  • 1
    @serhatg By definition, no; compare `(for a in {1..6}; do ./test& done; time wait)& sleep 1; killall test;` with `(for a in {1..6}; do ./test& done; time wait)& sleep 1; killall -9 test;`. This is not a deficiency of the solution, it's a given with interprocess synchonization objects (although on windows the situation maybe different, I'd need to check). So you need a monitoring process that frees the semaphore. Or a maintenance tool that does so on demand (say, on reboot). – sehe Nov 12 '14 at 10:53