HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppModerate

Macro to run code once during the lifetime of the program

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
oncelifetimetheduringprogrammacrocoderun

Problem

I'm using this little macro a lot:

#define RUN_ONCE(runcode) \
{ \
    static bool code_ran = 0; \
    if(!code_ran){ \ 
        code_ran = 1; \
        runcode; \
    } \
}


I find it useful when I want to initialize stuff just once when I don't really care about performance (for example, if it's inside a render loop, it takes just 60 if's per sec). In a random generator, I can do RUN_ONCE(init_random()), so I don't need to separately initialize it, which prevents me from crashing when I call the function without initializing it first.

I'd like to know if any of you use a similar system, and if there's better way of doing this. Or if I should stop using this method immediately...

Solution

It is not thread safe. You can use Boost, or standard C++11 std::call_once.

Answer to comment:

Boost and C++11 are defining include library for launching threads and thread synchronization (locks, atomic variables…). The call_once function can either use those to ensure thread safety or use the thread lib of the OS (pthreads for *nix).

A very simplified (unefficent) implementation might be:

static std::mutex mutex;
static bool called = false;
{
   std::lock_guard lock(mutex);
   if (!called) {
      f(); // <- User code
      called = true;
   }
}


It is not very efficient because the lock is always taken. An optimisation it to atomically check the once flag before taking the mutex:

static std::mutex mutex;
static std::atomic called = false;
{
   if (!called) {
     std::lock_guard lock(mutex);
     if (!called) {
        f(); // <- User code
        called = true;
     }
   }
}


The implementation of pthread_once in glibc is interesting: it is much more complicated as it tries to behave correctly in presence of fork.

Boost/C++11 use a functor (function pointer or object with operator()): this way it can be implemented as a function and not a macro (you can use lambda in C++11 to avoid defining a separate function).

Code Snippets

static std::mutex mutex;
static bool called = false;
{
   std::lock_guard<std::mutex> lock(mutex);
   if (!called) {
      f(); // <- User code
      called = true;
   }
}
static std::mutex mutex;
static std::atomic<bool> called = false;
{
   if (!called) {
     std::lock_guard<std::mutex> lock(mutex);
     if (!called) {
        f(); // <- User code
        called = true;
     }
   }
}

Context

StackExchange Code Review Q#4422, answer score: 18

Revisions (0)

No revisions yet.