1

I have a program that uses a work queue to execute tasks, and is supposed to run as a daemon. I had been achieving this using the following code:

bool seedDaemon() {
        using namespace std;
        int childpid = 0;
        pid_t pid = 0;
        if( ( childpid = fork() ) < 0 ) {
                return false;
        }
        else if( childpid > 0 ){
                exit(0);
        }
        setsid();
        umask(0);
        std::cout<< "[OK]\n";
        close( fileno(stderr) );
        close( fileno(stdout) );
        close( STDIN_FILENO );
        return true;
}

This would close the original process and start another one. However, this has caused the problem where threads I had created for performing my task were not appearing again after the fork. My work queue is instantiated globally, All other values and memory addresses copy over correctly to the child, but the threads do not.

For reference, here is the pool class:

pool.h:

#ifndef __POOL_H
#define __POOL_H
class tpool {
        public:
                tpool( std::size_t tpool_size );
                ~tpool();
                template< typename Task >
                void run_task( Task task ){ // add item to the queue
                        io_service_.post( boost::bind( &tpool::wrap_task, this,
                                boost::function< void() > ( task ) ) );
                }
        private:
                boost::asio::io_service io_service_;
                boost::asio::io_service::work work_;
                boost::thread_group threads_;
                boost::mutex mutex_;
                void wrap_task( boost::function< void() > task );
};

extern tpool dbpool; 
#endif

pool.cpp:

#include <boost/asio/io_service.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include "pool.h"
tpool::tpool( std::size_t tpool_size ) : work_( io_service_ ), available_( tpool_size ) {
    for ( std::size_t i = 0; i < tpool_size; ++i ){
        threads_.create_thread( boost::bind( &boost::asio::io_service::run, &io_service_ ) );
    }
}
tpool::~tpool() {
    io_service_.stop();
    try {
        threads_.join_all();
    }
    catch( ... ) {}
}
void tpool::wrap_task( boost::function< void() > task ) {
    // run the supplied task
    try {
        task();
    } // suppress exceptions
    catch( ... ) {
    }
    boost::unique_lock< boost::mutex > lock( mutex_ );
    ++available_;
}
tpool dbpool( 50 );

So how can I solve this?

Oblivious12
  • 61
  • 1
  • 7

2 Answers2

5

The simplest way is to not use globals. They're bad for exactly the reasons you discovered (and more).

As a quick&dirty workaround you can have a

extern tpool dbpool();  // function!

which in the cpp is implemented as:

tpool& dbpool() {
    static tpool the_instance; // only initialized on first call
    return the_instance;
}

That way, as long as you don't call dbpool() it won't be initialized. (Initialization of the function local static has been thread safe since c++11).

Note While you're at it, add some namespaces, because having names like tpool in the global namespace is really a code smell in C++.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • My reasoning behind using globals was that It needs to be accessible and visible to the multiple modules and functions that call it, and I would constantly get a linker error that dbpool was decared but not defined. >.> – Oblivious12 Sep 27 '14 at 00:27
  • @Oblivious12 time to read about the ***static initialization order fiasco*** – sehe Sep 27 '14 at 06:37
2

You are aware that threads do not survive fork(), right? There's no harm in putting the thread allocation into a method you call after fork(). You know, sometimes the constructor isn't the best place to .... make things. Of course, avoiding a global so you can delay the construction until after the fork is fine.

You'll probably also want to look into detaching the daemon from the controlling terminal.

  • How do I detach a daemon from it's controlling terminal? And as I was on my way into work this morning that exact thought - creating a function to spawn the threads AFTER initialization... >.> – Oblivious12 Sep 27 '14 at 00:05