Skip to content

Commit

Permalink
Use std::call_once instead of double-checked locking in singleton cla…
Browse files Browse the repository at this point in the history
…sses

- Add tests for singleton classes
  • Loading branch information
bllanos committed Apr 10, 2024
1 parent 11e5954 commit 7ac2302
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 29 deletions.
54 changes: 25 additions & 29 deletions include/cpr/singleton.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef CPR_SINGLETON_H
#define CPR_SINGLETON_H

#include <cassert>
#include <mutex>

#ifndef CPR_DISABLE_COPY
Expand All @@ -10,38 +11,33 @@
#endif

#ifndef CPR_SINGLETON_DECL
#define CPR_SINGLETON_DECL(Class) \
public: \
static Class* GetInstance(); \
static void ExitInstance(); \
\
private: \
CPR_DISABLE_COPY(Class) \
static Class* s_pInstance; \
static std::mutex s_mutex;
#define CPR_SINGLETON_DECL(Class) \
public: \
static Class* GetInstance(); \
static void ExitInstance(); \
\
private: \
CPR_DISABLE_COPY(Class) \
static Class* s_pInstance; \
static std::once_flag s_getFlag; \
static std::once_flag s_exitFlag;
#endif

#ifndef CPR_SINGLETON_IMPL
#define CPR_SINGLETON_IMPL(Class) \
Class* Class::s_pInstance = nullptr; \
std::mutex Class::s_mutex; \
Class* Class::GetInstance() { \
if (s_pInstance == nullptr) { \
s_mutex.lock(); \
if (s_pInstance == nullptr) { \
s_pInstance = new Class; \
} \
s_mutex.unlock(); \
} \
return s_pInstance; \
} \
void Class::ExitInstance() { \
s_mutex.lock(); \
if (s_pInstance) { \
delete s_pInstance; \
s_pInstance = nullptr; \
} \
s_mutex.unlock(); \
#define CPR_SINGLETON_IMPL(Class) \
Class* Class::s_pInstance = nullptr; \
std::once_flag Class::s_getFlag{}; \
std::once_flag Class::s_exitFlag{}; \
Class* Class::GetInstance() { \
std::call_once(Class::s_getFlag, []() { s_pInstance = new Class; }); \
return s_pInstance; \
} \
void Class::ExitInstance() { \
std::call_once(Class::s_exitFlag, []() { \
assert(s_pInstance != 0); \
delete s_pInstance; \
s_pInstance = nullptr; \
}); \
}
#endif

Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ add_cpr_test(interceptor_multi)
add_cpr_test(multiperform)
add_cpr_test(resolve)
add_cpr_test(multiasync)
add_cpr_test(singleton)

if (ENABLE_SSL_TESTS)
add_cpr_test(ssl)
Expand Down
23 changes: 23 additions & 0 deletions test/singleton_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <gtest/gtest.h>

#include "singleton_tests.hpp"

// NOLINTNEXTLINE (cppcoreguidelines-avoid-non-const-global-variables)
CPR_SINGLETON_IMPL(TestSingleton)

TEST(SingletonTests, GetInstanceTest) {
const TestSingleton* singleton = TestSingleton::GetInstance();
EXPECT_NE(singleton, nullptr);
}

TEST(SingletonTests, ExitInstanceTest) {
TestSingleton* singleton = TestSingleton::GetInstance();
TestSingleton::ExitInstance();
singleton = TestSingleton::GetInstance();
EXPECT_EQ(singleton, nullptr);
}

int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
9 changes: 9 additions & 0 deletions test/singleton_tests.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#include "cpr/cpr.h"

class TestSingleton {
CPR_SINGLETON_DECL(TestSingleton)
private:
TestSingleton() = default;
};

0 comments on commit 7ac2302

Please sign in to comment.