views:

402

answers:

4

In C++, I have a few functions that need to write to a temp directory. Ideally, only one temp directory gets created that they all write to (to minimize I/O overhead). That directory should be automagically removed when the program exits.

However, I don't want to deal with creation and removal of the temp directory in the main function because I think only the functions that actually use the directory should be responsible for its creation and removal. And the main function doesn't necessarily know that a temp directory is used at all.

Here is what I tried (see code below): A getTempDir() function that can be globally called from anywhere creates a directory on its first call only and returns the dir name on every call. On the first call it also creates a static boost::shared_ptr to a little DirRemover object whose destructor will remove the directory. That destructor gets called automatically when the program exits.

The problem is that it won't call the FileRemover destructor on an unsuccessful exit of the program, or a kill etc. Are there better solutions?

Here is the code:

std::string getTempDir(){
  static bool alreadyThere = false;
  static std::string name = "";
  if(!alreadyThere){
    // create dir with
    // popen("mktemp -p /tmp","r")) 
    // and store its name in 'name' 
    removeAtEnd(name);
    alreadyThere = true;
  }
  return name;
}

void removeAtEnd(const std::string& name){
  static boost::shared_ptr<DirRemover> remover(new DirRemover(name));
}

struct DirRemover {
  std::string name;
  DirRemover(const std::string& n) : name(n){}
  ~DirRemover(){
    // remove 'name' dir with popen("rm -r ...")
  }
};
A: 

There is no way to do any cleanup if your process gets killed (eg. from the task manager).

Other process should do that cleanup if your program stops. Start that process and launch your program as its child process. This process creates the temp directory before start your program and removes it after your program finishes.

Although this still not solves the problem if your wrapper process gets killed.

Calmarius
AS other have noted, this depends on the exact mechanism with which the process is killed. Typically a SIGTERM should be tried before SIGKILL, and the former give the app the option of catching the signal and handling it gracefully...
dmckee
+2  A: 

For Unix:

::signal(SIGINT, sigintHandler);
::signal(SIGKILL, sigkillHandler); //etc

Make those handlers push a quit event onto your eventqueue (or equivalent) and ignore the signal. Your app will then be able to quit gracefully. However, there's one signal (SIGKILL) which can't be ignored. To handle that, determine the most critical resources you really wanna release on a kill (i'm not convinced a temp dir is that important. just delete in on next start.), and in your handler, call a release function on your relevant singletons.

To elaborate on singletons. ::signal cannot call bound functions, so your handler only has access to singletons/globals. If your design doesn't organize things with singletons (and it shouldn't), you can still often hack around that. For example if you have multiple MainWindows across the stack, then you can also have a global vector of pointers to all of them, which you can access from the handler if necessary.

Note that for common kills (like with kill foo) you get SIGQUIT rather than SIGKILL -- and SIGQUIT is nicely ignorable. SIGKILL is only for kill -9 foo (in which case the user mightdoes not even want you doing cleanup).

Yes, signals are hairy.

edit - I was informed SIGKILL is not only unignorable, but also uncatchable. And indeed, there isn't much of anything sane you can do on SIGKILL. Deleting a temp dir? Why? That temp dir was there for a reason.

Just pretend sigkill is a power outage and recover on next start -- unless you can do a specific hack as was commented by others about unlinking.

That's what I get for talking out of my ass.

+1 for throwing the word "singleton" out there. UNIX signals is a good way to handle this, yes.
strager
NB: SIGSTOP is not catchable either...
dmckee
+7  A: 

Using popen() to execute things like "rm -r" or "mktemp -p /tmp" is a recipe for disaster. In my opinion it is extremely bad style.

UNIX Specific: If you want temporary files to disappear even if your application terminates abnormally then the best way to go is to immediately unlink temp files after opening them. Your app will still have a file handle to them so you can work with the file. When your application terminates and the file descriptor is closed then the file will be automatically removed from the file system.

Btw, I see you are using Boost. Are you sure Boost.Filesystem does not have the above functionality?

St3fan
Good idea, unlinking. Haven't though about that (though I have been trapped by that behavior before ;P).
strager
Good idea indeed.
+1 Excellent idea - this could be the equivalent I've been looking for to open a file with FILE_FLAG_DELETE_ON_CLOSE attributes in Win32.
Steve Folly
A: 

A couple of thoughts:

  • Maintaining minimum coupling: why not create a service class which stands between your other code and the details of where stuff is getting written, and when it get deleted? Use the usual singleton pattern mthods to insure that it is initalized only once.

  • Insuring deletion: signal have already been mentioned as a way to allow you to cleanup gracefully in the event of a SIGFPE, SIGSEGV, SIGTERM, etc... These are no help in the event of SIGSTOP or SIGKILL. However, there is an old unix trick for files where you create the file on disk, then unlink it---at that point the file is still in use and is not actually deleted, but it will be when you're program relinquishes it's file descriptor---and you get automatic deletion. I don't know off the top of my head how this will work with a directory, but you can certainly make it work withh all the files contained therein. As a bonus (or cost, depending) those files are not visible or accessible to other processes after the call to unlink.


Added: See also:

dmckee