2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 21:45:37 +00:00

[#891,!592] make StatsMgr thread safe

This commit is contained in:
Razvan Becheriu
2019-11-14 18:13:44 +02:00
parent 86d384da7a
commit 0a00312c79
6 changed files with 317 additions and 188 deletions

View File

@@ -7,39 +7,111 @@
#include <config.h>
#include <stats/context.h>
#include <util/multi_threading_mgr.h>
#include <map>
using namespace std;
using namespace isc::data;
using namespace isc::util;
namespace isc {
namespace stats {
ObservationPtr StatContext::get(const std::string& name) const {
std::map<std::string, ObservationPtr>::const_iterator obs = stats_.find(name);
if (obs == stats_.end()) {
return (ObservationPtr());
} else {
return (obs->second);
}
ObservationPtr result;
auto lambda = [&]() {
auto obs = stats_.find(name);
if (obs != stats_.end()) {
result = obs->second;
}
};
MultiThreadingMgr::call(mutex_, lambda);
return (result);
}
void StatContext::add(const ObservationPtr& obs) {
std::map<std::string, ObservationPtr>::iterator existing = stats_.find(obs->getName());
if (existing == stats_.end()) {
stats_.insert(make_pair(obs->getName() ,obs));
} else {
isc_throw(DuplicateStat, "Statistic named " << obs->getName()
<< " already exists.");
}
auto lambda = [&]() {
auto existing = stats_.find(obs->getName());
if (existing == stats_.end()) {
stats_.insert(make_pair(obs->getName() ,obs));
} else {
isc_throw(DuplicateStat, "Statistic named " << obs->getName()
<< " already exists.");
}
};
MultiThreadingMgr::call(mutex_, lambda);
}
bool StatContext::del(const std::string& name) {
std::map<std::string, ObservationPtr>::iterator obs = stats_.find(name);
if (obs == stats_.end()) {
return (false);
} else {
stats_.erase(obs);
return (true);
}
bool result = false;
auto lambda = [&]() {
auto obs = stats_.find(name);
if (obs != stats_.end()) {
stats_.erase(obs);
result = true;
}
};
MultiThreadingMgr::call(mutex_, lambda);
return (result);
}
size_t StatContext::size() {
size_t size;
MultiThreadingMgr::call(mutex_, [&]() {size = stats_.size();});
return (size);
}
void StatContext::clear() {
MultiThreadingMgr::call(mutex_, [&]() {stats_.clear();});
}
void StatContext::reset() {
auto lambda = [&]() {
// Let's iterate over all stored statistics...
for (auto s : stats_) {
// ... and reset each statistic.
s.second->reset();
}
};
MultiThreadingMgr::call(mutex_, lambda);
}
ConstElementPtr
StatContext::getAll() const {
ElementPtr map = isc::data::Element::createMap(); // a map
auto lambda = [&]() {
// Let's iterate over all stored statistics...
for (auto s : stats_) {
// ... and add each of them to the map.
map->set(s.first, s.second->getJSON());
}
};
MultiThreadingMgr::call(mutex_, lambda);
return (map);
}
void
StatContext::setMaxSampleAgeAll(const StatsDuration& duration) {
auto lambda = [&]() {
// Let's iterate over all stored statistics...
for (auto s : stats_) {
// ... and set duration limit for each statistic.
s.second->setMaxSampleAge(duration);
}
};
MultiThreadingMgr::call(mutex_, lambda);
}
void
StatContext::setMaxSampleCountAll(uint32_t max_samples) {
auto lambda = [&]() {
// Let's iterate over all stored statistics...
for (auto s : stats_) {
// ... and set count limit for each statistic.
s.second->setMaxSampleCount(max_samples);
}
};
MultiThreadingMgr::call(mutex_, lambda);
}
};

View File

@@ -9,6 +9,7 @@
#include <stats/observation.h>
#include <boost/shared_ptr.hpp>
#include <mutex>
#include <string>
namespace isc {
@@ -28,7 +29,7 @@ public:
/// all statistics related to a given subnet or all statistics related to a
/// given network interface.
struct StatContext {
public:
public:
/// @brief attempts to get an observation
/// @param name name of the statistic
@@ -45,14 +46,23 @@ struct StatContext {
/// @return true if successful, false if no such statistic was found
bool del(const std::string& name);
size_t size();
void clear();
void reset();
void setMaxSampleAgeAll(const StatsDuration& duration);
void setMaxSampleCountAll(uint32_t max_samples);
isc::data::ConstElementPtr getAll() const;
private:
/// @brief Statistics container
///
/// It is public to allow various operations that require iterating over
/// all elements. In particular, two operations (setting all stats to 0;
/// reporting all stats) will take advantage of this. Alternatively, we
/// could make it protected and then return a pointer to it, but that
/// would defeat the purpose of the hermetization in the first place.
std::map<std::string, ObservationPtr> stats_;
mutable std::mutex mutex_;
};
/// @brief Pointer to the statistics context

View File

@@ -11,183 +11,220 @@
#include <cc/data.h>
#include <cc/command_interpreter.h>
#include <util/boost_time_utils.h>
#include <util/multi_threading_mgr.h>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace std;
using namespace isc::data;
using namespace isc::config;
using namespace isc::util;
namespace isc {
namespace stats {
StatsMgr& StatsMgr::instance() {
StatsMgr&
StatsMgr::instance() {
static StatsMgr stats_mgr;
return (stats_mgr);
}
StatsMgr::StatsMgr() :
global_(new StatContext()) {
}
void StatsMgr::setValue(const std::string& name, const int64_t value) {
setValueInternal(name, value);
void
StatsMgr::setValue(const string& name, const int64_t value) {
MultiThreadingMgr::call(mutex_, [&]() {setValueInternal(name, value);});
}
void StatsMgr::setValue(const std::string& name, const double value) {
setValueInternal(name, value);
void
StatsMgr::setValue(const string& name, const double value) {
MultiThreadingMgr::call(mutex_, [&]() {setValueInternal(name, value);});
}
void StatsMgr::setValue(const std::string& name, const StatsDuration& value) {
setValueInternal(name, value);
}
void StatsMgr::setValue(const std::string& name, const std::string& value) {
setValueInternal(name, value);
void
StatsMgr::setValue(const string& name, const StatsDuration& value) {
MultiThreadingMgr::call(mutex_, [&]() {setValueInternal(name, value);});
}
void StatsMgr::addValue(const std::string& name, const int64_t value) {
addValueInternal(name, value);
void
StatsMgr::setValue(const string& name, const string& value) {
MultiThreadingMgr::call(mutex_, [&]() {setValueInternal(name, value);});
}
void StatsMgr::addValue(const std::string& name, const double value) {
addValueInternal(name, value);
void
StatsMgr::addValue(const string& name, const int64_t value) {
MultiThreadingMgr::call(mutex_, [&]() {addValueInternal(name, value);});
}
void StatsMgr::addValue(const std::string& name, const StatsDuration& value) {
addValueInternal(name, value);
void
StatsMgr::addValue(const string& name, const double value) {
MultiThreadingMgr::call(mutex_, [&]() {addValueInternal(name, value);});
}
void StatsMgr::addValue(const std::string& name, const std::string& value) {
addValueInternal(name, value);
void
StatsMgr::addValue(const string& name, const StatsDuration& value) {
MultiThreadingMgr::call(mutex_, [&]() {addValueInternal(name, value);});
}
ObservationPtr StatsMgr::getObservation(const std::string& name) const {
void
StatsMgr::addValue(const string& name, const string& value) {
MultiThreadingMgr::call(mutex_, [&]() {addValueInternal(name, value);});
}
ObservationPtr
StatsMgr::getObservation(const string& name) const {
ObservationPtr result;
MultiThreadingMgr::call(mutex_, [&]() {result = getObservationInternal(name);});
return (result);
}
ObservationPtr
StatsMgr::getObservationInternal(const string& name) const {
/// @todo: Implement contexts.
// Currently we keep everything in a global context.
return (global_->get(name));
}
void StatsMgr::addObservation(const ObservationPtr& stat) {
void
StatsMgr::addObservation(const ObservationPtr& stat) {
MultiThreadingMgr::call(mutex_, [&]() {addObservationInternal(stat);});
}
void
StatsMgr::addObservationInternal(const ObservationPtr& stat) {
/// @todo: Implement contexts.
// Currently we keep everything in a global context.
return (global_->add(stat));
}
bool StatsMgr::deleteObservation(const std::string& name) {
bool
StatsMgr::deleteObservation(const string& name) {
bool result;
MultiThreadingMgr::call(mutex_, [&]() {result = deleteObservationInternal(name);});
return (result);
}
bool
StatsMgr::deleteObservationInternal(const string& name) {
/// @todo: Implement contexts.
// Currently we keep everything in a global context.
return (global_->del(name));
}
bool StatsMgr::setMaxSampleAge(const std::string& name,
const StatsDuration& duration) {
ObservationPtr obs = getObservation(name);
if (obs) {
obs->setMaxSampleAge(duration);
return (true);
} else {
return (false);
}
bool
StatsMgr::setMaxSampleAge(const string& name, const StatsDuration& duration) {
bool result = false;
auto lambda = [&]() {
ObservationPtr obs = getObservationInternal(name);
if (obs) {
obs->setMaxSampleAge(duration);
result = true;
}
};
MultiThreadingMgr::call(mutex_, lambda);
return (result);
}
bool StatsMgr::setMaxSampleCount(const std::string& name,
uint32_t max_samples) {
ObservationPtr obs = getObservation(name);
if (obs) {
obs->setMaxSampleCount(max_samples);
return (true);
} else {
return (false);
}
bool
StatsMgr::setMaxSampleCount(const string& name, uint32_t max_samples) {
bool result = false;
auto lambda = [&]() {
ObservationPtr obs = getObservationInternal(name);
if (obs) {
obs->setMaxSampleCount(max_samples);
result = true;
}
};
MultiThreadingMgr::call(mutex_, lambda);
return (result);
}
void StatsMgr::setMaxSampleAgeAll(const StatsDuration& duration) {
// Let's iterate over all stored statistics...
for (std::map<std::string, ObservationPtr>::iterator s = global_->stats_.begin();
s != global_->stats_.end(); ++s) {
// ... and set duration limit for each statistic.
s->second->setMaxSampleAge(duration);
}
void
StatsMgr::setMaxSampleAgeAll(const StatsDuration& duration) {
MultiThreadingMgr::call(mutex_, [&]() {global_->setMaxSampleAgeAll(duration);});
}
void StatsMgr::setMaxSampleCountAll(uint32_t max_samples) {
// Let's iterate over all stored statistics...
for (std::map<std::string, ObservationPtr>::iterator s = global_->stats_.begin();
s != global_->stats_.end(); ++s) {
// ... and set count limit for each statistic.
s->second->setMaxSampleCount(max_samples);
}
void
StatsMgr::setMaxSampleCountAll(uint32_t max_samples) {
MultiThreadingMgr::call(mutex_, [&]() {global_->setMaxSampleCountAll(max_samples);});
}
bool StatsMgr::reset(const std::string& name) {
ObservationPtr obs = getObservation(name);
if (obs) {
obs->reset();
return (true);
} else {
return (false);
}
bool
StatsMgr::reset(const string& name) {
bool result = false;
auto lambda = [&]() {
ObservationPtr obs = getObservationInternal(name);
if (obs) {
obs->reset();
result = true;
}
};
MultiThreadingMgr::call(mutex_, lambda);
return (result);
}
bool StatsMgr::del(const std::string& name) {
return (global_->del(name));
bool
StatsMgr::del(const string& name) {
bool result;
MultiThreadingMgr::call(mutex_, [&]() {result = global_->del(name);});
return (result);
}
void StatsMgr::removeAll() {
global_->stats_.clear();
void
StatsMgr::removeAll() {
MultiThreadingMgr::call(mutex_, [&]() {global_->clear();});
}
isc::data::ConstElementPtr StatsMgr::get(const std::string& name) const {
ConstElementPtr
StatsMgr::get(const string& name) const {
isc::data::ElementPtr response = isc::data::Element::createMap(); // a map
ObservationPtr obs = getObservation(name);
if (obs) {
response->set(name, obs->getJSON()); // that contains observations
}
auto lambda = [&]() {
ObservationPtr obs = getObservationInternal(name);
if (obs) {
response->set(name, obs->getJSON()); // that contains observations
}
};
MultiThreadingMgr::call(mutex_, lambda);
return (response);
}
isc::data::ConstElementPtr StatsMgr::getAll() const {
isc::data::ElementPtr map = isc::data::Element::createMap(); // a map
// Let's iterate over all stored statistics...
for (std::map<std::string, ObservationPtr>::iterator s = global_->stats_.begin();
s != global_->stats_.end(); ++s) {
// ... and add each of them to the map.
map->set(s->first, s->second->getJSON());
}
return (map);
ConstElementPtr
StatsMgr::getAll() const {
ConstElementPtr result;
MultiThreadingMgr::call(mutex_, [&]() {result = global_->getAll();});
return (result);
}
void StatsMgr::resetAll() {
// Let's iterate over all stored statistics...
for (std::map<std::string, ObservationPtr>::iterator s = global_->stats_.begin();
s != global_->stats_.end(); ++s) {
// ... and reset each statistic.
s->second->reset();
}
void
StatsMgr::resetAll() {
MultiThreadingMgr::call(mutex_, [&]() {global_->reset();});
}
size_t StatsMgr::getSize(const std::string& name) const {
ObservationPtr obs = getObservation(name);
size_t
StatsMgr::getSize(const string& name) const {
size_t size = 0;
if (obs) {
size = obs->getSize();
}
auto lambda = [&]() {
ObservationPtr obs = getObservationInternal(name);
if (obs) {
size = obs->getSize();
}
};
MultiThreadingMgr::call(mutex_, lambda);
return (size);
}
size_t StatsMgr::count() const {
return (global_->stats_.size());
size_t
StatsMgr::count() const {
size_t result;
MultiThreadingMgr::call(mutex_, [&]() {result = global_->size();});
return (result);
}
isc::data::ConstElementPtr
StatsMgr::statisticSetMaxSampleAgeHandler(const std::string& /*name*/,
const isc::data::ConstElementPtr& params) {
std::string name, error;
ConstElementPtr
StatsMgr::statisticSetMaxSampleAgeHandler(const string& /*name*/,
const ConstElementPtr& params) {
string name, error;
StatsDuration duration;
if (!StatsMgr::getStatName(params, name, error)) {
return (createAnswer(CONTROL_RESULT_ERROR, error));
@@ -204,10 +241,10 @@ StatsMgr::statisticSetMaxSampleAgeHandler(const std::string& /*name*/,
}
}
isc::data::ConstElementPtr
StatsMgr::statisticSetMaxSampleCountHandler(const std::string& /*name*/,
const isc::data::ConstElementPtr& params) {
std::string name, error;
ConstElementPtr
StatsMgr::statisticSetMaxSampleCountHandler(const string& /*name*/,
const ConstElementPtr& params) {
string name, error;
uint32_t max_samples;
if (!StatsMgr::getStatName(params, name, error)) {
return (createAnswer(CONTROL_RESULT_ERROR, error));
@@ -224,10 +261,10 @@ StatsMgr::statisticSetMaxSampleCountHandler(const std::string& /*name*/,
}
}
isc::data::ConstElementPtr
StatsMgr::statisticGetHandler(const std::string& /*name*/,
const isc::data::ConstElementPtr& params) {
std::string name, error;
ConstElementPtr
StatsMgr::statisticGetHandler(const string& /*name*/,
const ConstElementPtr& params) {
string name, error;
if (!StatsMgr::getStatName(params, name, error)) {
return (createAnswer(CONTROL_RESULT_ERROR, error));
}
@@ -235,14 +272,13 @@ StatsMgr::statisticGetHandler(const std::string& /*name*/,
StatsMgr::instance().get(name)));
}
isc::data::ConstElementPtr
StatsMgr::statisticResetHandler(const std::string& /*name*/,
const isc::data::ConstElementPtr& params) {
std::string name, error;
ConstElementPtr
StatsMgr::statisticResetHandler(const string& /*name*/,
const ConstElementPtr& params) {
string name, error;
if (!StatsMgr::getStatName(params, name, error)) {
return (createAnswer(CONTROL_RESULT_ERROR, error));
}
if (StatsMgr::instance().reset(name)) {
return (createAnswer(CONTROL_RESULT_SUCCESS,
"Statistic '" + name + "' reset."));
@@ -252,10 +288,10 @@ StatsMgr::statisticResetHandler(const std::string& /*name*/,
}
}
isc::data::ConstElementPtr
StatsMgr::statisticRemoveHandler(const std::string& /*name*/,
const isc::data::ConstElementPtr& params) {
std::string name, error;
ConstElementPtr
StatsMgr::statisticRemoveHandler(const string& /*name*/,
const ConstElementPtr& params) {
string name, error;
if (!StatsMgr::getStatName(params, name, error)) {
return (createAnswer(CONTROL_RESULT_ERROR, error));
}
@@ -269,33 +305,33 @@ StatsMgr::statisticRemoveHandler(const std::string& /*name*/,
}
isc::data::ConstElementPtr
StatsMgr::statisticRemoveAllHandler(const std::string& /*name*/,
const isc::data::ConstElementPtr& /*params*/) {
ConstElementPtr
StatsMgr::statisticRemoveAllHandler(const string& /*name*/,
const ConstElementPtr& /*params*/) {
StatsMgr::instance().removeAll();
return (createAnswer(CONTROL_RESULT_SUCCESS,
"All statistics removed."));
}
isc::data::ConstElementPtr
StatsMgr::statisticGetAllHandler(const std::string& /*name*/,
const isc::data::ConstElementPtr& /*params*/) {
ConstElementPtr
StatsMgr::statisticGetAllHandler(const string& /*name*/,
const ConstElementPtr& /*params*/) {
ConstElementPtr all_stats = StatsMgr::instance().getAll();
return (createAnswer(CONTROL_RESULT_SUCCESS, all_stats));
}
isc::data::ConstElementPtr
StatsMgr::statisticResetAllHandler(const std::string& /*name*/,
const isc::data::ConstElementPtr& /*params*/) {
ConstElementPtr
StatsMgr::statisticResetAllHandler(const string& /*name*/,
const ConstElementPtr& /*params*/) {
StatsMgr::instance().resetAll();
return (createAnswer(CONTROL_RESULT_SUCCESS,
"All statistics reset to neutral values."));
}
isc::data::ConstElementPtr
StatsMgr::statisticSetMaxSampleAgeAllHandler(const std::string& /*name*/,
const isc::data::ConstElementPtr& params) {
std::string error;
ConstElementPtr
StatsMgr::statisticSetMaxSampleAgeAllHandler(const string& /*name*/,
const ConstElementPtr& params) {
string error;
StatsDuration duration;
if (!StatsMgr::getStatDuration(params, duration, error)) {
return (createAnswer(CONTROL_RESULT_ERROR, error));
@@ -305,10 +341,10 @@ StatsMgr::statisticSetMaxSampleAgeAllHandler(const std::string& /*name*/,
"All statistics duration limit are set."));
}
isc::data::ConstElementPtr
StatsMgr::statisticSetMaxSampleCountAllHandler(const std::string& /*name*/,
const isc::data::ConstElementPtr& params) {
std::string error;
ConstElementPtr
StatsMgr::statisticSetMaxSampleCountAllHandler(const string& /*name*/,
const ConstElementPtr& params) {
string error;
uint32_t max_samples;
if (!StatsMgr::getStatMaxSamples(params, max_samples, error)) {
return (createAnswer(CONTROL_RESULT_ERROR, error));
@@ -319,9 +355,9 @@ StatsMgr::statisticSetMaxSampleCountAllHandler(const std::string& /*name*/,
}
bool
StatsMgr::getStatName(const isc::data::ConstElementPtr& params,
std::string& name,
std::string& reason) {
StatsMgr::getStatName(const ConstElementPtr& params,
string& name,
string& reason) {
if (!params) {
reason = "Missing mandatory 'name' parameter.";
return (false);
@@ -335,15 +371,14 @@ StatsMgr::getStatName(const isc::data::ConstElementPtr& params,
reason = "'name' parameter expected to be a string.";
return (false);
}
name = stat_name->stringValue();
return (true);
}
bool
StatsMgr::getStatDuration(const isc::data::ConstElementPtr& params,
StatsMgr::getStatDuration(const ConstElementPtr& params,
StatsDuration& duration,
std::string& reason) {
string& reason) {
if (!params) {
reason = "Missing mandatory 'duration' parameter.";
return (false);
@@ -353,24 +388,20 @@ StatsMgr::getStatDuration(const isc::data::ConstElementPtr& params,
reason = "Missing mandatory 'duration' parameter.";
return (false);
}
int64_t time_duration = stat_duration->intValue();
int64_t hours = time_duration / 3600;
time_duration -= hours * 3600;
int64_t minutes = time_duration / 60;
time_duration -= minutes * 60;
int64_t seconds = time_duration;
duration = boost::posix_time::time_duration(hours, minutes, seconds, 0);
return (true);
}
bool
StatsMgr::getStatMaxSamples(const isc::data::ConstElementPtr& params,
StatsMgr::getStatMaxSamples(const ConstElementPtr& params,
uint32_t& max_samples,
std::string& reason) {
string& reason) {
if (!params) {
reason = "Missing mandatory 'max-samples' parameter.";
return (false);
@@ -384,7 +415,6 @@ StatsMgr::getStatMaxSamples(const isc::data::ConstElementPtr& params,
reason = "'max-samples' parameter expected to be an integer.";
return (false);
}
max_samples = stat_max_samples->intValue();
return (true);
}

View File

@@ -12,6 +12,7 @@
#include <boost/noncopyable.hpp>
#include <map>
#include <mutex>
#include <string>
#include <vector>
#include <sstream>
@@ -224,6 +225,8 @@ class StatsMgr : public boost::noncopyable {
/// @return Pointer to the Observation object
ObservationPtr getObservation(const std::string& name) const;
ObservationPtr getObservationInternal(const std::string& name) const;
/// @brief Generates statistic name in a given context
///
/// Example:
@@ -447,14 +450,13 @@ private:
/// @throw InvalidStatType is statistic exists and has a different type.
template<typename DataType>
void setValueInternal(const std::string& name, DataType value) {
// If we want to log each observation, here would be the best place for it.
ObservationPtr stat = getObservation(name);
ObservationPtr stat = getObservationInternal(name);
if (stat) {
stat->setValue(value);
} else {
stat.reset(new Observation(name, value));
addObservation(stat);
addObservationInternal(stat);
}
}
@@ -472,14 +474,13 @@ private:
/// @throw InvalidStatType is statistic exists and has a different type.
template<typename DataType>
void addValueInternal(const std::string& name, DataType value) {
// If we want to log each observation, here would be the best place for it.
ObservationPtr existing = getObservation(name);
ObservationPtr existing = getObservationInternal(name);
if (!existing) {
// We tried to add to a non-existing statistic. We can recover from
// that. Simply add the new incremental value as a new statistic and
// we're done.
setValue(name, value);
setValueInternal(name, value);
return;
} else {
// Let's hope it is of correct type. If not, the underlying
@@ -497,6 +498,8 @@ private:
/// @param stat observation
void addObservation(const ObservationPtr& stat);
void addObservationInternal(const ObservationPtr& stat);
/// @private
/// @brief Tries to delete an observation.
@@ -505,6 +508,8 @@ private:
/// @return true if deleted, false if not found
bool deleteObservation(const std::string& name);
bool deleteObservationInternal(const std::string& name);
/// @brief Utility method that attempts to extract statistic name
///
/// This method attempts to extract statistic name from the params
@@ -562,6 +567,9 @@ private:
// This is a global context. All statistics will initially be stored here.
StatContextPtr global_;
/// @brief Mutex for protecting stats.
mutable std::mutex mutex_;
};
};

View File

@@ -24,8 +24,7 @@ TEST(ContextTest, basic) {
StatContext ctx;
// By default the context does not hold any statistics.
EXPECT_EQ(0, ctx.stats_.size());
EXPECT_TRUE(ctx.stats_.empty());
EXPECT_EQ(0, ctx.size());
// It should be possible to add 'a' statistic
EXPECT_NO_THROW(ctx.add(a));
@@ -38,8 +37,7 @@ TEST(ContextTest, basic) {
EXPECT_NO_THROW(ctx.add(c));
// By now we should have 3 statistics recorded
EXPECT_EQ(3, ctx.stats_.size());
EXPECT_FALSE(ctx.stats_.empty());
EXPECT_EQ(3, ctx.size());
// Let's try to retrieve them
ObservationPtr from_ctx;

View File

@@ -8,6 +8,7 @@
#define MULTI_THREADING_MGR_H
#include <boost/noncopyable.hpp>
#include <mutex>
namespace isc {
namespace util {
@@ -62,6 +63,16 @@ public:
/// @param enabled The new mode.
void setMode(bool enabled);
template<typename Callable>
static void call(std::mutex& lk, const Callable& f) {
if (MultiThreadingMgr::instance().getMode()) {
std::lock_guard<std::mutex> lock(lk);
f();
} else {
f();
}
}
protected:
/// @brief Constructor.