Someone asked here how to determine a function is being called from multiple threads. My take on this is that they are asking about concurrent access not sequential access. The accepted answer therefore seems inadequate. Detecting concurrent access is also a problem I need to solve for debugging purposes so I thought I'd take a stab at a solution.
This takes an RAII-style approach to the solution, requiring only an instantiation of the class at the top of the function in question.
template <std::ostream& stream> class ConcurrentPrint: public std::ostringstream
{
public:
~ConcurrentPrint()
{
std::scoped_lock lock(printMutex);
stream << this->str();
}
private:
static std::mutex printMutex;
};
template <std::ostream& stream> std::mutex ConcurrentPrint<stream>::printMutex;
class ConcurrencyChecker
{
public:
ConcurrencyChecker(const std::string& id_) :
id(id_)
{
std::scoped_lock lock(mapMutex);
if (++numInstances[id] > 1)
{
ConcurrentPrint<std::cerr>() << "Warning: concurrent access to " << id << " from " << std::this_thread::get_id() << std::endl;
abort();
}
}
~ConcurrencyChecker()
{
std::scoped_lock lock(mapMutex);
--numInstances[id];
}
private:
static std::map<std::string, int> numInstances;
static std::mutex cerrMutex;
const std::string id;
std::mutex mapMutex;
};
std::map<std::string, int> ConcurrencyChecker::numInstances = {};
Intended usage:
void functionToCheck()
{
ConcurrencyChecker check(__func__);
/* Function body */
}
Working example here
Things I'm interested in:
- Are there any flaws in this general approach and is there a better one?
- Any general improvements to the code
ConcurrencyChecker::cerrMutex
? \$\endgroup\$ – Toby Speight Jun 7 '19 at 7:28cerrMutex
is actually unneeded and just residual from before I brokeConcurrentPrint
into its own class. \$\endgroup\$ – j b Jun 7 '19 at 7:47