Daniel Singleton for C++

Created by Denes Daniel

The Daniel Singleton is my attempt to make a perfect solution to create singletons in C++. As you may know, problems arise when you have multiple (different) singletons using each other in their destructors, and/or if you want to have lazy initialization in multithreaded programs. I used a template approach to solve the problems, so you can easily create any number of singleton classes; they just have to rely on the template code provided.

Basic structure

The solution's core is the template class StonKeeper (stonkeeper.h). It is responsible for creating and destroying the singleton at the right time based on dependencies. It has two template parameters: Ston (which is the singleton class being handled by StonKeeper), and Manager (which is a class responsible for thread-aware construction and destruction of the singleton instance). Manager is also a template, with one template parameter: Ston.
So what exactly does StonKeeper and its parameter, Manager do? StonKeeper handles all the dependency and lifetime stuff, but the actual singleton instance is being constructed, held and destructed by Manager. This separation makes it possible to write multiple Manager classes: one which is pretty easy and fast, but works only for single thread singleton usage (stonmanager.h), one that uses RogueWave mutexes to prevent creation of multiple singleton instances (stonmanagerrw.h), one for programs using pthreads (not implemented yet), etc. So, Manager is essentially just some code logic extracted fron StonKeeper, to make it replacable.

How it works

Below you can see two singleton classes using the templates. StonLog (ston_log.h, ston_log.cpp) represents a dummy logger class, while StonDBC (ston_dbc.h, ston_dbc.cpp) is the same for a database connection. The database connection is assumed to be using the logger, even in its destructor.

Note: The comments marked with [TEST] are for testing only, they should be removed from production source code.

ston_log.h

#ifndef STON_LOG_H
#define STON_LOG_H

#include "stonkeeper.h"

// Needed only for using StonManagerRW
#include "stonmanagerrw.h"

class StonLog {
// === Declarations needed for using StonKeeper === //
    // The two declarations below define the StonKeeper template instance to use (must be the same)
    // - First template parameter must be your class
    // - Second template parameter must be a Manager class for StonKeeper
    // [TEST] Choose Manager: StonManager (single-threaded) or StonManagerRW (multi-threaded)
    friend class StonKeeper<StonLog, StonManagerRW>;
    typedef StonKeeper<StonLog, StonManagerRW> TKeeper;
    // Function to be able to define your dependencies
    static void setupDepends();
    // Private constructor, copy constructor, assignment operator, destructor
    StonLog();
    StonLog(StonLog const &);
    StonLog & operator =(StonLog const &);
    ~StonLog();
public:
    // Function others will call to define they depend on you
    inline static bool keepAlive() { return TKeeper::keepSingletonAlive(); }
    // Function to get the singleton instance
    inline static StonLog * getInstance() { return TKeeper::getSingleton(); }

// === Actual implementation details of your class === //
private:
    char const * message;
public:
    void doUse();
};

#endif

ston_log.cpp

#include <iostream>
#include "ston_log.h"

// Needed only for using StonDBC
#include "ston_dbc.h"

// Function to be able to define your dependencies
void StonLog::setupDepends() {
    // Call the static keepAlive() of every Singleton class that this one depends on
    //StonDBC::keepAlive(); // [TEST] (Un)Comment to define dependencies
}

StonLog::StonLog() : message("Using Log\n") {
    std::cout << "Constructing Singleton: Log\n";
}

StonLog::~StonLog() {
    std::cout << "Destructing Singleton: Log\n";
}

void StonLog::doUse() {
    std::cout << message;
}

ston_dbc.h

#ifndef STON_DBC_H
#define STON_DBC_H

#include "stonkeeper.h"

// Needed only for using StonManagerRW
#include "stonmanagerrw.h"

class StonDBC {
// === Declarations needed for using StonKeeper === //
    // The two declarations below define the StonKeeper template instance to use (must be the same)
    // - First template parameter must be your class
    // - Second template parameter must be a Manager class for StonKeeper
    // [TEST] Choose Manager: StonManager (single-threaded) or StonManagerRW (multi-threaded)
    friend class StonKeeper<StonDBC, StonManagerRW>;
    typedef StonKeeper<StonDBC, StonManagerRW> TKeeper;
    // Function to be able to define your dependencies
    static void setupDepends();
    // Private constructor, copy constructor, assignment operator, destructor
    StonDBC();
    StonDBC(StonDBC const &);
    StonDBC & operator =(StonDBC const &);
    ~StonDBC();
public:
    // Function others will call to define they depend on you
    inline static bool keepAlive() { return TKeeper::keepSingletonAlive(); }
    // Function to get the singleton instance
    inline static StonDBC * getInstance() { return TKeeper::getSingleton(); }

// === Actual implementation details of your class === //
private:
    char const * message;
public:
    void doUse();
};

#endif

ston_dbc.cpp

#include <iostream>
#include "ston_dbc.h"

// Needed only for using StonLog
#include "ston_log.h"

// Function to be able to define your dependencies
void StonDBC::setupDepends() {
    // Call the static keepAlive() of every Singleton class that this one depends on
    StonLog::keepAlive(); // [TEST] (Un)Comment to define dependencies
}

StonDBC::StonDBC() : message("Using DB connection\n") {
    std::cout << "Constructing Singleton: DB connection\n";
}

StonDBC::~StonDBC() {
    std::cout << "Destructing Singleton: DB connection\n";
}

void StonDBC::doUse() {
    std::cout << message;
} 

As you can see, I separated the singleton class headers into two parts: "Declarations needed for using StonKeeper" and "Actual implementation details". The latter part is really simple in my dummy classes, and are not really relevant, so I will focus on the former. This part will always look like this, so this is what everyone has to understand, who wants to create and use a Daniel Singleton. It's really simple, so let's see what we have there!
  • friend + typedef: You have to make the StonKeeper (parameterized with your class and the Manager of your choice) your friend, so it will be able to create and destroy the singleton instance. The TKeeper typedef is used in the implementation of the two public static inline methods.
  • setupDepends(): A function you have to implement, that "declares" your dependencies. You "declare" them by simply calling the static keepAlive() of every other singleton class, that this one depends on (order doesn't matter).
  • Private constructor, copy constructor, assignment operator, destructor: needed to protect the singleton from being created or destroyed from outside the class (except for the friend StonKeeper).
  • keepAlive(): Function others will call to define they depend on you. You have to make it call through to TKeeper::keepSingletonAlive().
  • getInstance(): Function to get the singleton instance. You have to make it call through to TKeeper::getSingleton().
And that's all there is to it! Everything else is handled by the StonKeeper and the Manager you chose.

One thing to look out for: if you have some other static object, that uses a singleton in its constructor or destructor, that object should make a call to Singleton::keepAlive() in its constructor (before trying to use the singleton instance of course). If that is not an option, and you don't know how to get around the problem, you can always make a file scope static boolean variable and initialize it with the return value of Singleton::keepAlive() (since it returns a boolean) at the start of each file where you use the singleton in the static initialization or destruction phase of the program (before of after the execution of the main() method).

Implementation details

So let's see the core files, and a little explanation on how they work.

stonkeeper.h

/*
 * Daniel Singleton for C++
 * Created by Denes Daniel
 *
 * This file is licensed to you under the Apache License, Version 2.0 :
 * http://www.apache.org/licenses/LICENSE-2.0
 * Distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
 */
 
#ifndef STONKEEPER_H
#define STONKEEPER_H

// Singleton Keeper implementation
// Responsible for creating and destroying Singleton at the right time based on dependencies

// Include default Manager class for StonKeeper
#include "stonmanager.h"

template <class Ston, template <class> class Manager = StonManager>
class StonKeeper {
    friend class Manager<Ston>;
    typedef Manager<Ston> TManager;
    static bool initRunning;
    static bool initComplete;
    static bool doInit;
    static Ston * newSingleton();
    static void delSingleton(Ston * inst);
    StonKeeper();
    StonKeeper(StonKeeper<Ston, Manager> const &);
    StonKeeper<Ston, Manager> & operator =(StonKeeper<Ston, Manager> const &);
    ~StonKeeper();
public:
    class SingletonNotUsableException {};
    class CircularDependencyException {};
    static bool keepSingletonAlive();
    static Ston * getSingleton();
};

template <class Ston, template <class> class Manager>
bool StonKeeper<Ston, Manager>::initRunning = false;

template <class Ston, template <class> class Manager>
bool StonKeeper<Ston, Manager>::initComplete = false;

template <class Ston, template <class> class Manager>
bool StonKeeper<Ston, Manager>::doInit = StonKeeper<Ston, Manager>::keepSingletonAlive();

template <class Ston, template <class> class Manager>
inline Ston * StonKeeper<Ston, Manager>::newSingleton() {
    return new Ston();
}

template <class Ston, template <class> class Manager>
inline void StonKeeper<Ston, Manager>::delSingleton(Ston * inst) {
    delete inst;
}

template <class Ston, template <class> class Manager>
inline StonKeeper<Ston, Manager>::StonKeeper() {
}

template <class Ston, template <class> class Manager>
StonKeeper<Ston, Manager>::~StonKeeper() {
    TManager::killSingleton();
}

template <class Ston, template <class> class Manager>
bool StonKeeper<Ston, Manager>::keepSingletonAlive() {
    if(!initComplete) {
        if(initRunning) throw CircularDependencyException();
        initRunning = true;
        try {
            Ston::setupDepends();
            TManager::doInit();
        }
        catch (...) {
            initRunning = false;
            throw;
        }
        static StonKeeper<Ston, Manager> keeper;
        initRunning = false;
        initComplete = true;
    }
    return true;
}

template <class Ston, template <class> class Manager>
Ston * StonKeeper<Ston, Manager>::getSingleton() {
    Ston * inst = TManager::getSingleton();
    if(!inst) {
        if(!initComplete) {
            (void)doInit; // Needed so init is not optimized away
            throw SingletonNotUsableException();
        }
        inst = TManager::makeSingleton();
        if(!inst) throw SingletonNotUsableException();
    }
    return inst;
}

#endif

The StonKeeper (for a given singleton class) has two static boolean variables as its state, initRunning and initComplete; both are statically initialized to false. The third static boolean, doInit is not really used as a variable, its purpose is to run StonKeeper::keepSingletonAlive() before program execution starts (before main() is called). This is accomplished by returning a dummy bool value from the function, and assigning that to the startup value of doInit. This function is where all the magic happens.
When StonKeeper::keepSingletonAlive() is first ran, both initRunning and initComplete are false, so it enters the first if statement, but skips the second one. Then it sets initRunning to true, and tries to run the singleton's setupDepends() function. Remember, this is the function which calls others singletons' keepAlive() method, which in turn call their StonKeeper's keepSingletonAlive(). So we're starting a recursion here, digging to the root of the dependency tree. If there is a circular dependency, we end up calling the original keepSingletonAlive() again, which again enters the first if, but now it enters the second too; and throws a CircularDependencyException. So you can't even run your program, if it is invalid. Assuming that there are no circular dependencies, we are going to find the dependency root, where Ston::setupDepends() will be empty, so keepSingletonAlive() carries on with the next line, which is TManager::doInit(). (TManager is the chosen Manager class with Ston as the template parameter.) This call makes it possible for the Manager to initialize before it is first used. After this is all done, we create a static StonKeeper instance, whose sole purpose is to govern the lifetime of the singleton: when the static object is destructed, the program has finished, so the singletons have to be destroyed. Remember, we are at the root of the dependency tree, so we created that StonKeeper first, which will have to be destroyed last. This way, C++ will guarantee correct destruction order for us! (Keep in mind that the singleton instance itself does not exist at this point, it will be created later, lazily.) Once the StonKeeper is created, we set initRunning to false, initComplete to true (so any further calls to this keepSingletonAlive() won't do anything), and then return. Because we were at the bottom of the recursion, the dependent singleton's keepSingletonAlive() will continue from where it was, creating the StonKeeper objects for all singletons in reverse order, up to the initial one. Eventually, all StonKeeper's keepSingletonAlive() will get called (at least once, by the initialization of the doInit variable), and all static StonKeeper objects will exist, and they will kill the singletons at the end of program execution, in correct order (based on the dependencies). If you look at StonKeeper's destructor, you can see that it only contains a call to TManager::killSingleton(), which actually does the destruction of the singleton instance.

The other interesting part is the StonKeeper::getSingleton() method. This works according to DCLP (Double-Checked Locking Pattern), which I have read to be broken, but I think I may have found a way to fix that. (If the fix turns out to be wrong, I will probably simplify this part.) To be able to work with DCLP, Manager classes have two methods that return a singleton pointer: getSingleton() (which should quickly return a pointer to the singleton instance if it's alive, or a null pointer if it's not), and makeSingleton() (which should return a pointer to the singleton instance, creating it if necessary, but only once). According to DCLP, StonKeeper::getSingleton() first tries to get the instance pointer by calling TManager::getSingleton(). If the pointer returned is not the null pointer, then the singleton is already alive, so we simply return the pointer to the caller. If it is the null pointer, then we have more work to do. First we check if initComplete is still false: if it is, initialization didn't happen (possibly some non-singleton static object using a singleton without calling Singleton::keepAlive() first), so we throw an exception. (The strange (void)doInit; statement is there to prevent the compiler from optimizing away the doInit variable completely, as this would make the program skip initialization.) If initComplete is true, the code tries to get the instance pointer by calling TManager::makeSingleton(), which is capable of creating the singleton (but is slower than TManager::getSingleton(), since it has to look out for thread safety). This call can only return a null pointer if the StonKeeper is already destroyed: in this case, there would be nothing left to destroy the singleton; this if why an exception is thrown in this case. (This scenario should not happen with proper configuration anyway, since the StonKeeper should live longer than all objects that possibly use the singleton.) So in any scenario, StonKeeper::getSingleton() either returns a valid pointer to the living singleton instance, or throws an exception, meaning that the singleton "configuration" is invalid.

There are two more methods in StonKeeper that are interesting: newSingleton() and delSingleton(). Their purpose is to allow the Manager class (which is a friend of StonKeeper, but not Ston itself!) to actually construct and destruct a singleton. A bit funny that this actually happens in StonKeeper, but that's the price to pay to make Ston itself cleaner (the Manager does not have to be added as a friend).

stonmanager.h

/*
 * Daniel Singleton for C++
 * Created by Denes Daniel
 *
 * This file is licensed to you under the Apache License, Version 2.0 :
 * http://www.apache.org/licenses/LICENSE-2.0
 * Distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
 */
 
#ifndef STONMANAGER_H
#define STONMANAGER_H

// Singleton Manager implementation
// Responsible for thread-aware construction and destruction of Singleton
// StonManager is the default manager, NOT thread-safe

// Implementation details:
// - stonCreate is used to make sure Singleton is only created once.
//   Once the Singleton is destroyed, it can't be recreated.

// Forward declaration
template <class Ston, template <class> class Manager> class StonKeeper;

template <class Ston>
class StonManager {
// === Declarations needed to be a Manager class for StonKeeper === //
    // The two declarations below define the StonKeeper template instance to be used by (must be the same)
    // - First template parameter must be our template paramteter (Ston)
    // - Second template parameter must be this class template (referring to it through global namespace)
    friend class StonKeeper<Ston, ::StonManager>;
    typedef StonKeeper<Ston, ::StonManager> TKeeper;
    // Method called by StonKeeper to be able to initialize this class
    static void doInit();
    // Function that should quickly return a pointer to the singleton instance (if it's alive) or a null pointer (if it's not)
    static Ston * getSingleton();
    // Function that should return a pointer to the singleton instance (safely creating it if necessary, but only once)
    static Ston * makeSingleton();
    // Function that should kill the singleton instance (and prevent creation of it later on)
    static void killSingleton();
    // Private constructor (anything in this class must only be accessible to the friend StonKeeper)
    StonManager();

// === Actual implementation details of Manager class === //
    // Everything must be private (anything in this class must only be accessible to the friend StonKeeper)
    static bool stonCreate;
    static Ston * stonInst;
};

template <class Ston>
bool StonManager<Ston>::stonCreate = true;

template <class Ston>
Ston * StonManager<Ston>::stonInst = 0;

// Method called by StonKeeper (for the Manager to be able to initialize itself)
template <class Ston>
void StonManager<Ston>::doInit() {
}

// Function that should quickly return a pointer to the singleton instance (if it's alive) or a null pointer (if it's not)
template <class Ston>
inline Ston * StonManager<Ston>::getSingleton() {
    return stonInst;
}

// Function that should return a pointer to the singleton instance (safely creating it if necessary, but only once)
template <class Ston>
Ston * StonManager<Ston>::makeSingleton() {
    if(stonCreate) {
        stonInst = TKeeper::newSingleton();
        stonCreate = false;
    }
    return getSingleton();
}

// Function that should kill the singleton instance (and prevent creation of it later on)
template <class Ston>
void StonManager<Ston>::killSingleton() {
    stonCreate = false;
    if(stonInst) {
        TKeeper::delSingleton(stonInst);
        stonInst = 0;
    }
}

#endif

I think StonManager is pretty easy to understand. It has two variables: a boolean named stonCreate initialzed to true, and a pointer named stonInst, which is initialized to the null pointer. doInit() is empty, getSingleton() simply returns stonInst, and makeSingleton() creates the singleton (storing its pointer to stonInst) if stonCreate is true. stonCreate becomes false in two cases: if makeSingleton() does actually create the instance, or if killSingleton() is called (by the destructor of StonKeeper). So an instance can only be created once, and only before StonKeeper is destructed. In killSingleton(), stonInst is set back to the null pointer, so StonKeeper::getSingleton() can detect if someone tries to use the singleton instance after it was destroyed.

StonManager is obviously not thread safe, but it's easy and fast, making it perfect for single threaded programs.

stonmanagerrw.h

/*
 * Daniel Singleton for C++
 * Created by Denes Daniel
 *
 * This file is licensed to you under the Apache License, Version 2.0 :
 * http://www.apache.org/licenses/LICENSE-2.0
 * Distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
 */
 
#ifndef STONMANAGERRW_H
#define STONMANAGERRW_H

// Singleton Manager implementation
// Responsible for thread-aware construction and destruction of Singleton
// StonManagerRW is thread-safe, uses RogueWave mutexes

// Implementation details:
// - stonCreate is used to make sure Singleton is only created once.
//   Once the Singleton is destroyed, it can't be recreated.
// - stonAlive indicates if stonInst can be safely used or not.
//   This approach prevents possible problems if pointer assignment is not atomic.
// - The guards for barrLock are used to make sure that stonAlive is only true
//   if stonInst points to a usable Singleton object. Without them, the compiler
//   or the CPU can rearrange use of these variables.

#include <rw/sync/RWMutexLock.h>

// Forward declaration
template <class Ston, template <class> class Manager> class StonKeeper;

template <class Ston>
class StonManagerRW {
// === Declarations needed to be a Manager class for StonKeeper === //
    // The two declarations below define the StonKeeper template instance to be used by (must be the same)
    // - First template parameter must be our template paramteter (Ston)
    // - Second template parameter must be this class template (referring to it through global namespace)
    friend class StonKeeper<Ston, ::StonManagerRW>;
    typedef StonKeeper<Ston, ::StonManagerRW> TKeeper;
    // Method called by StonKeeper to be able to initialize this class
    static void doInit();
    // Function that should quickly return a pointer to the singleton instance (if it's alive) or a null pointer (if it's not)
    static Ston * getSingleton();
    // Function that should return a pointer to the singleton instance (safely creating it if necessary, but only once)
    static Ston * makeSingleton();
    // Function that should kill the singleton instance (and prevent creation of it later on)
    static void killSingleton();
    // Private constructor (anything in this class must only be accessible to the friend StonKeeper)
    StonManagerRW();
    
// === Actual implementation details of Manager class === //
    // Everything must be private (anything in this class must only be accessible to the friend StonKeeper)
    static RWMutexLock lock;
    static bool stonCreate;
    static bool stonAlive;
    static Ston * stonInst;
};

template <class Ston>
RWMutexLock StonManagerRW<Ston>::lock(RW_STATIC_CTOR);

template <class Ston>
bool StonManagerRW<Ston>::stonCreate = true;

template <class Ston>
bool StonManagerRW<Ston>::stonAlive = false;

template <class Ston>
Ston * StonManagerRW<Ston>::stonInst = 0;

// Method called by StonKeeper (for the Manager to be able to initialize itself)
template <class Ston>
void StonManagerRW<Ston>::doInit() {
    RWMutexLock::LockGuard guard(lock);
}

// Function that should quickly return a pointer to the singleton instance (if it's alive) or a null pointer (if it's not)
template <class Ston>
inline Ston * StonManagerRW<Ston>::getSingleton() {
    return (stonAlive ? stonInst : 0);
}

// Function that should return a pointer to the singleton instance (safely creating it if necessary, but only once)
template <class Ston>
Ston * StonManagerRW<Ston>::makeSingleton() {
    RWMutexLock::LockGuard guard(lock);
    if(stonCreate) {
        RWMutexLock barrLock;
        {
            RWMutexLock::LockGuard barrier(barrLock);
            stonInst = TKeeper::newSingleton();
        }
        {
            RWMutexLock::LockGuard barrier(barrLock);
            stonAlive = true;
        }
        stonCreate = false;
    }
    return getSingleton();
}

// Function that should kill the singleton instance (and prevent creation of it later on)
template <class Ston>
void StonManagerRW<Ston>::killSingleton() {
    RWMutexLock::LockGuard guard(lock);
    stonCreate = false;
    if(stonAlive) {
        RWMutexLock barrLock;
        {
            RWMutexLock::LockGuard barrier(barrLock);
            stonAlive = false;
        }
        {
            RWMutexLock::LockGuard barrier(barrLock);
            TKeeper::delSingleton(stonInst);
        }
    }
}

#endif

StonManagerRW is my attempt to fix DCLP. After talking to several C++ programmers, I still think it's right... but I'm not 100% sure, so please tell me if it's not! (In this case, I will have to write a slower but flawless version, just for demonstration of the multi-threaded capabilities of the Daniel Singleton. Anyway, this RogueWave Manager is just meant to be a sample... you might as well want to write your own manager for pthreads, or whatever threading library you use.)

So let's see how it works! In addition to the variables used in StonManager, there is another boolean named stonAlive, which is initialized to false, and an RWMutexLock named lock, which is initialized in doInit() (called by StonKeeper::keepSingletonAlive(), so still in the single threaded part of the program execution; programs that do multithreading before main() is started or after it is finished are not supported).

As you can see in getSingleton(), stonAlive is used as a "mask" for stonInst (if stonAlive is false, a null pointer is returned instead of stonInst), which prevents some problems as you'll se below. (In addition, it makes the code work reliably even if pointer assignment is not atomic for some reason.)

Let's take a look at makeSingleton() then! We start by creating a guard on the shared mutex (the lock variable), and then checking if stonCreate is still true. If it's not, we simply fall back to getSingleton(); if it is, we have to create the singleton instance first. We do that in two steps:
1. stonInst = TKeeper::newSingleton();
2. stonAlive = true;
As you can see, both statements are wrapped in their own guarded block, where the mutex is a local variable (barrLock). Why? Well, the guarded blocks should have a useful property of not letting instructions "escape" out of them, neither compile time by the compiler, nor runtime by the CPU. This way, the blocks create a barrier between the two steps above: the first one has to be completed fully before the second one is even started. And if this is true, then getSingleton() will always return either a null pointer, or a pointer to a living instance, fixing the problem with DCLP. If it's not true... well, if that's the case for a given threading library, then I think that it's a bug in that library, since it can't guarantee that something inside a guarded block is executed while the mutex is held. At least that's what I think, but tell me if I'm wrong.

The same approach is used in killSingleton(), only in reverse order.
1. stonAlive = false;
2. TKeeper::delSingleton(stonInst);

Try it!

Here is a small program I created to test the solution. Try it yourself too!

main.cpp

#include <iostream>
#include "ston_log.h"
#include "ston_dbc.h"

// Needed for threading
#include <rw/thr/thrfunc.h>

class Problem {
public:
    Problem() {
        //StonDBC::keepAlive(); // [TEST] (Un)Comment to define dependencies of this object
        //StonLog::keepAlive(); // [TEST] (Un)Comment to define dependencies of this object
        std::cout << "Problem born\n";
    }
    ~Problem() {
        StonDBC::getInstance(); // [TEST] (Un)Comment to use / not use singleton
        //StonLog::getInstance(); // [TEST] (Un)Comment to use / not use singleton
        std::cout << "Problem died\n";
    }
};

//Problem prob; // [TEST] (Un)Comment to create / not create static object that possibly uses singletons

// Forward declarations
void testST();
void testMT();
void testMTFunc();

int main() {
    std::cout << "=====[ Startup ]=====\n";
    srand(time(0));
    // [NOTE] For multi-threaded tests you'll probably want to put some rwSleep()s inside the Manager class in use
    bool mt = true; // [TEST] Change to define single-threaded / multi-threaded test
    if(!mt) testST();
    else testMT();
    std::cout << "=====[ Shutdown ]=====\n";
}

void testST() {
    for (int i = 0; i < 10; ++i) {
        if(rand() % 2) StonDBC::getInstance()->doUse();
        else           StonLog::getInstance()->doUse();
    }
}

void testMT() {
    RWThreadFunction t1 = rwMakeThreadFunction(testMTFunc);
    RWThreadFunction t2 = rwMakeThreadFunction(testMTFunc);
    RWThreadFunction t3 = rwMakeThreadFunction(testMTFunc);
    RWThreadFunction t4 = rwMakeThreadFunction(testMTFunc);
    RWThreadFunction t5 = rwMakeThreadFunction(testMTFunc);
    t1.start(); t2.start(); t3.start(); t4.start(); t5.start();
    t1.join();  t2.join();  t3.join();  t4.join();  t5.join();
}

void testMTFunc() {
    for (int i = 0; i < 2; ++i) {
        if(rand() % 2) rwSleep(1);
        if(rand() % 2) StonDBC::getInstance()->doUse();
        else           StonLog::getInstance()->doUse();
    }
} 

If you find any errors, please contact me!

Denes Daniel

Last edited Feb 12, 2011 at 7:29 PM by PantherDD, version 30

Comments

No comments yet.