wqyfavor
wqyfavor

Reputation: 539

Is this code thread-safe with C++

I have read articles about double-check which may not be safe with C++ on arbitrary CPU. I am not quite sure whether my codes are absolutely safe or not. Class Database represents database file, and the connection is dynamically created when needed. Database::GetConnection may be called from different threads simultaneously. If the code is not absolutely safe, how can I modify the code. Thank you.

#include <mutex>
#include "sqlite3.h"

class Connection
{
private:
    sqlite3* _handle;
    std::string _path;
public:
    Connection(const std::string& path) : _path(path) {
        sqlite3_open(_path.c_str(), &_handle);
        // some other initialization work..
    }
}

class Database
{
private:
    typedef enum
    {
        Void,
        Connected,
        // some other status...
    } Status;

    std::string _path;
    std::mutex _mutex;
    Status _status;
    Connection* _connection;

    void OpenDatabase()
    {
        _connection = new Connection(_path);
        _status = Connected;
    }
public:
    Connection* GetConnection()
    {
        if (_status == Connected)
            return _connection;

        std::lock_guard<std::mutex> guard(_mutex);
        if (_status != Connected)
            OpenDatabase();
        return _connection;
    }
public:
    Database(const std::string& path) : _path(path), _status(Void) {};
};

Upvotes: 0

Views: 108

Answers (1)

txtechhelp
txtechhelp

Reputation: 6752

For efficiency ... is safe enough

You're dealing with a database, efficiency is moot and safe enough is up to you

Enough implies a level of allowable uncertainty. Are you ok with that?

What happens in that 0.001% chance it's not safe enough? Does the software crash? Does the hardware explode? Do birds turn inside out and fall from the sky?

If your OpenDatabase function is locked everywhere else in your Database class, then what's wrong with removing the first check and leaving your GetConnection function as so:

Connection* GetConnection()
{
    std::lock_guard<std::mutex> guard(_mutex);
    if (_status != Connected)
        OpenDatabase();
    return _connection;
}

You're dealing with a database, so the few milliseconds it takes to your CPU to create and store the std::lock_guard and lock/unlock the _mutex object then preform a simple boolean check is massively insignificant compared to the 100's of milliseconds building a connection and transactional state for the database can take.

Save the birds.

Upvotes: 1

Related Questions