1
0
forked from 0ad/0ad

Pthread -> std::thread (2/7) - Remove pthread in Profilers

Use std::thread and thread_local instead of pthread specific calls.

Differential Revision: https://code.wildfiregames.com/D1916
This was SVN commit r22608.
This commit is contained in:
wraitii 2019-08-04 08:11:58 +00:00
parent accd72b3c1
commit 9ece3e7088
4 changed files with 37 additions and 67 deletions

View File

@ -463,7 +463,7 @@ static long get_memory_alloc_count()
// http://www.gnu.org/software/libc/manual/html_node/Hooks-for-Malloc.html
static intptr_t malloc_count = 0;
static void *(*old_malloc_hook) (size_t, const void*);
static pthread_mutex_t alloc_hook_mutex = PTHREAD_MUTEX_INITIALIZER;
static std::mutex alloc_hook_mutex;
static void *malloc_hook(size_t size, const void* UNUSED(caller))
{
// This doesn't really work across threads. The hooks are global variables, and
@ -473,24 +473,22 @@ static void *malloc_hook(size_t size, const void* UNUSED(caller))
// Two threads may execute the hook simultaneously, so we need to do the
// temporary unhooking in a thread-safe way, so for simplicity we just use a mutex.
pthread_mutex_lock(&alloc_hook_mutex);
std::lock_guard<std::mutex> lock(alloc_hook_mutex);
++malloc_count;
__malloc_hook = old_malloc_hook;
void* result = malloc(size);
old_malloc_hook = __malloc_hook;
__malloc_hook = malloc_hook;
pthread_mutex_unlock(&alloc_hook_mutex);
return result;
}
static void alloc_hook_initialize()
{
pthread_mutex_lock(&alloc_hook_mutex);
std::lock_guard<std::mutex> lock(alloc_hook_mutex);
old_malloc_hook = __malloc_hook;
__malloc_hook = malloc_hook;
// (we don't want to bother hooking realloc and memalign, because if they allocate
// new memory then they'll be caught by the malloc hook anyway)
pthread_mutex_unlock(&alloc_hook_mutex);
}
/*
It would be nice to do:

View File

@ -39,6 +39,8 @@ CProfiler2 g_Profiler2;
// A human-recognisable pattern (for debugging) followed by random bytes (for uniqueness)
const u8 CProfiler2::RESYNC_MAGIC[8] = {0x11, 0x22, 0x33, 0x44, 0xf4, 0x93, 0xbe, 0x15};
thread_local CProfiler2::ThreadStorage* CProfiler2::m_CurrentStorage = nullptr;
CProfiler2::CProfiler2() :
m_Initialised(false), m_FrameNumber(0), m_MgContext(NULL), m_GPU(NULL)
{
@ -148,8 +150,6 @@ static void* MgCallback(mg_event event, struct mg_connection *conn, const struct
void CProfiler2::Initialise()
{
ENSURE(!m_Initialised);
int err = pthread_key_create(&m_TLS, &CProfiler2::TLSDtor);
ENSURE(err == 0);
m_Initialised = true;
RegisterCurrentThread("main");
@ -235,11 +235,6 @@ void CProfiler2::Shutdown()
// the destructor is not called for the main thread
// we have to call it manually to avoid memory leaks
ENSURE(ThreadUtil::IsMainThread());
void * dataptr = pthread_getspecific(m_TLS);
TLSDtor(dataptr);
int err = pthread_key_delete(m_TLS);
ENSURE(err == 0);
m_Initialised = false;
}
@ -267,44 +262,30 @@ void CProfiler2::RecordGPURegionLeave(const char* id)
m_GPU->RegionLeave(id);
}
/**
* Called by pthreads when a registered thread is destroyed.
*/
void CProfiler2::TLSDtor(void* data)
{
ThreadStorage* storage = (ThreadStorage*)data;
storage->GetProfiler().RemoveThreadStorage(storage);
delete (ThreadStorage*)data;
}
void CProfiler2::RegisterCurrentThread(const std::string& name)
{
ENSURE(m_Initialised);
ENSURE(pthread_getspecific(m_TLS) == NULL); // mustn't register a thread more than once
// Must not register a thread more than once.
ENSURE(m_CurrentStorage == nullptr);
ThreadStorage* storage = new ThreadStorage(*this, name);
int err = pthread_setspecific(m_TLS, storage);
ENSURE(err == 0);
m_CurrentStorage = new ThreadStorage(*this, name);
AddThreadStorage(m_CurrentStorage);
RecordSyncMarker();
RecordEvent("thread start");
AddThreadStorage(storage);
}
void CProfiler2::AddThreadStorage(ThreadStorage* storage)
{
std::lock_guard<std::mutex> lock(m_Mutex);
m_Threads.push_back(storage);
m_Threads.push_back(std::unique_ptr<ThreadStorage>(storage));
}
void CProfiler2::RemoveThreadStorage(ThreadStorage* storage)
{
std::lock_guard<std::mutex> lock(m_Mutex);
m_Threads.erase(std::find(m_Threads.begin(), m_Threads.end(), storage));
m_Threads.erase(std::find_if(m_Threads.begin(), m_Threads.end(), [storage](const std::unique_ptr<ThreadStorage>& s) { return s.get() == storage; }));
}
CProfiler2::ThreadStorage::ThreadStorage(CProfiler2& profiler, const std::string& name) :
@ -727,11 +708,13 @@ void CProfiler2::ConstructJSONOverview(std::ostream& stream)
std::lock_guard<std::mutex> lock(m_Mutex);
stream << "{\"threads\":[";
for (size_t i = 0; i < m_Threads.size(); ++i)
bool first_time = true;
for (std::unique_ptr<ThreadStorage>& storage : m_Threads)
{
if (i != 0)
if (!first_time)
stream << ",";
stream << "{\"name\":\"" << CStr(m_Threads[i]->GetName()).EscapeToPrintableASCII() << "\"}";
stream << "{\"name\":\"" << CStr(storage->GetName()).EscapeToPrintableASCII() << "\"}";
first_time = false;
}
stream << "]}";
}
@ -904,23 +887,17 @@ const char* CProfiler2::ConstructJSONResponse(std::ostream& stream, const std::s
std::lock_guard<std::mutex> lock(m_Mutex); // lock against changes to m_Threads or deletions of ThreadStorage
ThreadStorage* storage = NULL;
for (size_t i = 0; i < m_Threads.size(); ++i)
{
if (m_Threads[i]->GetName() == thread)
{
storage = m_Threads[i];
break;
}
}
auto it = std::find_if(m_Threads.begin(), m_Threads.end(), [&thread](std::unique_ptr<ThreadStorage>& storage) {
return storage->GetName() == thread;
});
if (!storage)
if (it == m_Threads.end())
return "cannot find named thread";
stream << "{\"events\":[\n";
stream << "[\n";
buffer = storage->GetBuffer();
buffer = (*it)->GetBuffer();
}
BufferVisitor_Dump visitor(stream);
@ -937,22 +914,17 @@ void CProfiler2::SaveToFile()
std::ofstream stream(OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc);
ENSURE(stream.good());
std::vector<ThreadStorage*> threads;
{
std::lock_guard<std::mutex> lock(m_Mutex);
threads = m_Threads;
}
stream << "profileDataCB({\"threads\": [\n";
for (size_t i = 0; i < threads.size(); ++i)
bool first_time = true;
for (std::unique_ptr<ThreadStorage>& storage : m_Threads)
{
if (i != 0)
if (!first_time)
stream << ",\n";
stream << "{\"name\":\"" << CStr(threads[i]->GetName()).EscapeToPrintableASCII() << "\",\n";
stream << "{\"name\":\"" << CStr(storage->GetName()).EscapeToPrintableASCII() << "\",\n";
stream << "\"data\": ";
ConstructJSONResponse(stream, threads[i]->GetName());
ConstructJSONResponse(stream, storage->GetName());
stream << "\n}";
first_time = false;
}
stream << "\n]});\n";
}

View File

@ -78,6 +78,9 @@
#ifndef INCLUDED_PROFILER2
#define INCLUDED_PROFILER2
#include <map>
#include <thread>
#include "lib/timer.h"
#include "ps/ThreadUtil.h"
@ -436,13 +439,10 @@ public:
private:
void InitialiseGPU();
static void TLSDtor(void* data);
ThreadStorage& GetThreadStorage()
{
ThreadStorage* storage = (ThreadStorage*)pthread_getspecific(m_TLS);
ASSERT(storage);
return *storage;
ENSURE(m_CurrentStorage);
return *m_CurrentStorage;
}
bool m_Initialised;
@ -451,12 +451,12 @@ private:
mg_context* m_MgContext;
pthread_key_t m_TLS;
CProfiler2GPU* m_GPU;
std::mutex m_Mutex;
std::vector<ThreadStorage*> m_Threads; // thread-safe; protected by m_Mutex
static thread_local ThreadStorage* m_CurrentStorage;
std::vector<std::unique_ptr<ThreadStorage>> m_Threads; // thread-safe; protected by m_Mutex
};
extern CProfiler2 g_Profiler2;

View File

@ -37,7 +37,7 @@ class CProfiler2GPU_base
protected:
CProfiler2GPU_base(CProfiler2& profiler, const char* name) :
m_Profiler(profiler), m_Storage(profiler, name)
m_Profiler(profiler), m_Storage(*new CProfiler2::ThreadStorage(profiler, name))
{
m_Storage.RecordSyncMarker(m_Profiler.GetTime());
m_Storage.Record(CProfiler2::ITEM_EVENT, m_Profiler.GetTime(), "thread start");
@ -51,7 +51,7 @@ protected:
}
CProfiler2& m_Profiler;
CProfiler2::ThreadStorage m_Storage;
CProfiler2::ThreadStorage& m_Storage;
};
//////////////////////////////////////////////////////////////////////////