mirror of
https://github.com/InoriRus/Kyty.git
synced 2026-08-28 05:06:40 +00:00
fix deadlock
This commit is contained in:
@@ -79,7 +79,7 @@ if (KYTY_LINKER STREQUAL LD)
|
|||||||
set(KYTY_LD_OPTIONS "-Wl,--image-base=0x100000000000")
|
set(KYTY_LD_OPTIONS "-Wl,--image-base=0x100000000000")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(Kyty${KYTY_PROJECT_NAME}${CMAKE_BUILD_TYPE}${KYTY_COMPILER} VERSION 0.0.6)
|
project(Kyty${KYTY_PROJECT_NAME}${CMAKE_BUILD_TYPE}${KYTY_COMPILER} VERSION 0.0.7)
|
||||||
|
|
||||||
include(src_script.cmake)
|
include(src_script.cmake)
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ add_custom_target( KytyGitVersion
|
|||||||
|
|
||||||
list(APPEND KYTY_IWYU
|
list(APPEND KYTY_IWYU
|
||||||
emulator_obj
|
emulator_obj
|
||||||
#core_obj
|
core_obj
|
||||||
#math_obj
|
#math_obj
|
||||||
#scripts_obj
|
#scripts_obj
|
||||||
#sys_obj
|
#sys_obj
|
||||||
@@ -121,7 +121,7 @@ list(APPEND KYTY_IWYU
|
|||||||
|
|
||||||
list(APPEND KYTY_CLANG_TYDY
|
list(APPEND KYTY_CLANG_TYDY
|
||||||
emulator_obj
|
emulator_obj
|
||||||
#core_obj
|
core_obj
|
||||||
#core
|
#core
|
||||||
#math_obj
|
#math_obj
|
||||||
#scripts_obj
|
#scripts_obj
|
||||||
|
|||||||
@@ -22,11 +22,8 @@ enum LabelStatus
|
|||||||
NotActive,
|
NotActive,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Label
|
struct LabelCallbacks
|
||||||
{
|
{
|
||||||
VkDevice device = nullptr;
|
|
||||||
VkEvent event = nullptr;
|
|
||||||
LabelStatus status = LabelStatus::New;
|
|
||||||
uint64_t* dst_gpu_addr64 = nullptr;
|
uint64_t* dst_gpu_addr64 = nullptr;
|
||||||
uint64_t value64 = 0;
|
uint64_t value64 = 0;
|
||||||
uint32_t* dst_gpu_addr32 = nullptr;
|
uint32_t* dst_gpu_addr32 = nullptr;
|
||||||
@@ -34,7 +31,15 @@ struct Label
|
|||||||
LabelGpuObject::callback_t callback_1 = nullptr;
|
LabelGpuObject::callback_t callback_1 = nullptr;
|
||||||
LabelGpuObject::callback_t callback_2 = nullptr;
|
LabelGpuObject::callback_t callback_2 = nullptr;
|
||||||
uint64_t args[4] = {};
|
uint64_t args[4] = {};
|
||||||
CommandBuffer* buffer = nullptr;
|
};
|
||||||
|
|
||||||
|
struct Label
|
||||||
|
{
|
||||||
|
VkDevice device = nullptr;
|
||||||
|
VkEvent event = nullptr;
|
||||||
|
LabelStatus status = LabelStatus::New;
|
||||||
|
LabelCallbacks callbacks;
|
||||||
|
CommandBuffer* buffer = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LabelManager
|
class LabelManager
|
||||||
@@ -76,7 +81,8 @@ void LabelManager::ThreadRun(void* data)
|
|||||||
|
|
||||||
int active_count = 0;
|
int active_count = 0;
|
||||||
|
|
||||||
Vector<Label*> deleted_labels;
|
Vector<Label*> deleted_labels;
|
||||||
|
Vector<LabelCallbacks> fired_labels;
|
||||||
|
|
||||||
for (auto& label: manager->m_labels)
|
for (auto& label: manager->m_labels)
|
||||||
{
|
{
|
||||||
@@ -93,33 +99,7 @@ void LabelManager::ThreadRun(void* data)
|
|||||||
|
|
||||||
label->status = LabelStatus::NotActive;
|
label->status = LabelStatus::NotActive;
|
||||||
|
|
||||||
bool write = true;
|
fired_labels.Add(label->callbacks);
|
||||||
|
|
||||||
if (label->callback_1 != nullptr)
|
|
||||||
{
|
|
||||||
write = label->callback_1(label->args);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write && label->dst_gpu_addr64 != nullptr)
|
|
||||||
{
|
|
||||||
*label->dst_gpu_addr64 = label->value64;
|
|
||||||
|
|
||||||
printf(FG_BRIGHT_GREEN "EndOfPipe Signal!!! [0x%016" PRIx64 "] <- 0x%016" PRIx64 "\n" FG_DEFAULT,
|
|
||||||
reinterpret_cast<uint64_t>(label->dst_gpu_addr64), label->value64);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write && label->dst_gpu_addr32 != nullptr)
|
|
||||||
{
|
|
||||||
*label->dst_gpu_addr32 = label->value32;
|
|
||||||
|
|
||||||
printf(FG_BRIGHT_GREEN "EndOfPipe Signal!!! [0x%016" PRIx64 "] <- 0x%08" PRIx32 "\n" FG_DEFAULT,
|
|
||||||
reinterpret_cast<uint64_t>(label->dst_gpu_addr32), label->value32);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (label->callback_2 != nullptr)
|
|
||||||
{
|
|
||||||
label->callback_2(label->args);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,6 +116,37 @@ void LabelManager::ThreadRun(void* data)
|
|||||||
|
|
||||||
manager->m_mutex.Unlock();
|
manager->m_mutex.Unlock();
|
||||||
|
|
||||||
|
for (auto& label: fired_labels)
|
||||||
|
{
|
||||||
|
bool write = true;
|
||||||
|
|
||||||
|
if (label.callback_1 != nullptr)
|
||||||
|
{
|
||||||
|
write = label.callback_1(label.args);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write && label.dst_gpu_addr64 != nullptr)
|
||||||
|
{
|
||||||
|
*label.dst_gpu_addr64 = label.value64;
|
||||||
|
|
||||||
|
printf(FG_BRIGHT_GREEN "EndOfPipe Signal!!! [0x%016" PRIx64 "] <- 0x%016" PRIx64 "\n" FG_DEFAULT,
|
||||||
|
reinterpret_cast<uint64_t>(label.dst_gpu_addr64), label.value64);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write && label.dst_gpu_addr32 != nullptr)
|
||||||
|
{
|
||||||
|
*label.dst_gpu_addr32 = label.value32;
|
||||||
|
|
||||||
|
printf(FG_BRIGHT_GREEN "EndOfPipe Signal!!! [0x%016" PRIx64 "] <- 0x%08" PRIx32 "\n" FG_DEFAULT,
|
||||||
|
reinterpret_cast<uint64_t>(label.dst_gpu_addr32), label.value32);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (label.callback_2 != nullptr)
|
||||||
|
{
|
||||||
|
label.callback_2(label.args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Core::Thread::SleepMicro(100);
|
Core::Thread::SleepMicro(100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,20 +161,20 @@ Label* LabelManager::Create(GraphicContext* ctx, uint64_t* dst_gpu_addr, uint64_
|
|||||||
|
|
||||||
auto* label = new Label;
|
auto* label = new Label;
|
||||||
|
|
||||||
label->status = LabelStatus::New;
|
label->status = LabelStatus::New;
|
||||||
label->dst_gpu_addr64 = dst_gpu_addr;
|
label->callbacks.dst_gpu_addr64 = dst_gpu_addr;
|
||||||
label->value64 = value;
|
label->callbacks.value64 = value;
|
||||||
label->dst_gpu_addr32 = nullptr;
|
label->callbacks.dst_gpu_addr32 = nullptr;
|
||||||
label->value32 = 0;
|
label->callbacks.value32 = 0;
|
||||||
label->event = nullptr;
|
label->event = nullptr;
|
||||||
label->device = ctx->device;
|
label->device = ctx->device;
|
||||||
label->callback_1 = callback_1;
|
label->callbacks.callback_1 = callback_1;
|
||||||
label->callback_2 = callback_2;
|
label->callbacks.callback_2 = callback_2;
|
||||||
label->args[0] = args[0];
|
label->callbacks.args[0] = args[0];
|
||||||
label->args[1] = args[1];
|
label->callbacks.args[1] = args[1];
|
||||||
label->args[2] = args[2];
|
label->callbacks.args[2] = args[2];
|
||||||
label->args[3] = args[3];
|
label->callbacks.args[3] = args[3];
|
||||||
label->buffer = nullptr;
|
label->buffer = nullptr;
|
||||||
|
|
||||||
VkEventCreateInfo create_info {};
|
VkEventCreateInfo create_info {};
|
||||||
create_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
|
create_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
|
||||||
@@ -190,19 +201,19 @@ Label* LabelManager::Create(GraphicContext* ctx, uint32_t* dst_gpu_addr, uint32_
|
|||||||
|
|
||||||
auto* label = new Label;
|
auto* label = new Label;
|
||||||
|
|
||||||
label->status = LabelStatus::New;
|
label->status = LabelStatus::New;
|
||||||
label->dst_gpu_addr32 = dst_gpu_addr;
|
label->callbacks.dst_gpu_addr32 = dst_gpu_addr;
|
||||||
label->value32 = value;
|
label->callbacks.value32 = value;
|
||||||
label->dst_gpu_addr64 = nullptr;
|
label->callbacks.dst_gpu_addr64 = nullptr;
|
||||||
label->value64 = 0;
|
label->callbacks.value64 = 0;
|
||||||
label->event = nullptr;
|
label->event = nullptr;
|
||||||
label->device = ctx->device;
|
label->device = ctx->device;
|
||||||
label->callback_1 = callback_1;
|
label->callbacks.callback_1 = callback_1;
|
||||||
label->callback_2 = callback_2;
|
label->callbacks.callback_2 = callback_2;
|
||||||
label->args[0] = args[0];
|
label->callbacks.args[0] = args[0];
|
||||||
label->args[1] = args[1];
|
label->callbacks.args[1] = args[1];
|
||||||
label->args[2] = args[2];
|
label->callbacks.args[2] = args[2];
|
||||||
label->args[3] = args[3];
|
label->callbacks.args[3] = args[3];
|
||||||
|
|
||||||
VkEventCreateInfo create_info {};
|
VkEventCreateInfo create_info {};
|
||||||
create_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
|
create_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ KYTY_SUBSYSTEM_DEFINE(Threads);
|
|||||||
|
|
||||||
using thread_func_t = void (*)(void*);
|
using thread_func_t = void (*)(void*);
|
||||||
|
|
||||||
// struct ThreadPrivate;
|
struct ThreadPrivate;
|
||||||
|
struct MutexPrivate;
|
||||||
|
struct CondVarPrivate;
|
||||||
class String;
|
class String;
|
||||||
|
|
||||||
class Thread
|
class Thread
|
||||||
@@ -25,6 +27,9 @@ public:
|
|||||||
// Once a thread has finished, the id may be reused by another thread.
|
// Once a thread has finished, the id may be reused by another thread.
|
||||||
[[nodiscard]] String GetId() const;
|
[[nodiscard]] String GetId() const;
|
||||||
|
|
||||||
|
// The id is unique and can't be reused by another thread.
|
||||||
|
[[nodiscard]] int GetUniqueId() const;
|
||||||
|
|
||||||
static void Sleep(uint32_t millis);
|
static void Sleep(uint32_t millis);
|
||||||
static void SleepMicro(uint32_t micros);
|
static void SleepMicro(uint32_t micros);
|
||||||
static void SleepNano(uint64_t nanos);
|
static void SleepNano(uint64_t nanos);
|
||||||
@@ -41,7 +46,6 @@ public:
|
|||||||
KYTY_CLASS_NO_COPY(Thread);
|
KYTY_CLASS_NO_COPY(Thread);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ThreadPrivate;
|
|
||||||
ThreadPrivate* m_thread;
|
ThreadPrivate* m_thread;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,7 +64,6 @@ public:
|
|||||||
KYTY_CLASS_NO_COPY(Mutex);
|
KYTY_CLASS_NO_COPY(Mutex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct MutexPrivate;
|
|
||||||
MutexPrivate* m_mutex;
|
MutexPrivate* m_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -97,7 +100,6 @@ public:
|
|||||||
KYTY_CLASS_NO_COPY(CondVar);
|
KYTY_CLASS_NO_COPY(CondVar);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct CondVarPrivate;
|
|
||||||
CondVarPrivate* m_cond_var;
|
CondVarPrivate* m_cond_var;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+390
-140
@@ -1,14 +1,12 @@
|
|||||||
#include "Kyty/Core/Threads.h"
|
#include "Kyty/Core/Threads.h"
|
||||||
|
|
||||||
#include "Kyty/Core/DbgAssert.h"
|
#include "Kyty/Core/DbgAssert.h"
|
||||||
|
#include "Kyty/Core/Debug.h"
|
||||||
|
#include "Kyty/Core/MagicEnum.h"
|
||||||
#include "Kyty/Core/SafeDelete.h"
|
#include "Kyty/Core/SafeDelete.h"
|
||||||
#include "Kyty/Core/String.h"
|
#include "Kyty/Core/String.h"
|
||||||
|
#include "Kyty/Core/Vector.h"
|
||||||
|
|
||||||
//#define THREADS_SDL
|
|
||||||
|
|
||||||
#ifdef THREADS_SDL
|
|
||||||
#include "SDL.h"
|
|
||||||
#else
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -16,72 +14,111 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#endif
|
|
||||||
|
|
||||||
//#define KYTY_DEBUG_LOCKS
|
//#define KYTY_DEBUG_LOCKS
|
||||||
|
|
||||||
namespace Kyty::Core {
|
namespace Kyty::Core {
|
||||||
|
|
||||||
#ifdef THREADS_SDL
|
|
||||||
typedef uint64_t thread_id_t;
|
|
||||||
#else
|
|
||||||
using thread_id_t = std::thread::id;
|
using thread_id_t = std::thread::id;
|
||||||
#endif
|
|
||||||
|
|
||||||
struct Thread::ThreadPrivate
|
|
||||||
{
|
|
||||||
ThreadPrivate(thread_func_t func, void* arg)
|
|
||||||
: finished(false), auto_delete(false),
|
|
||||||
#ifdef THREADS_SDL
|
|
||||||
m_func(func), m_arg(arg)
|
|
||||||
{
|
|
||||||
sdl = SDL_CreateThread(thread_run, "sdl_thread", this);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
m_thread(func, arg)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool finished;
|
|
||||||
bool auto_delete;
|
|
||||||
#ifdef THREADS_SDL
|
|
||||||
SDL_Thread* sdl;
|
|
||||||
thread_func_t m_func;
|
|
||||||
void* m_arg;
|
|
||||||
|
|
||||||
static int thread_run(void* data)
|
|
||||||
{
|
|
||||||
ThreadPrivate* t = (ThreadPrivate*)data;
|
|
||||||
t->m_func(t->m_arg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
std::thread m_thread;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Mutex::MutexPrivate
|
|
||||||
{
|
|
||||||
#ifdef THREADS_SDL
|
|
||||||
SDL_mutex* sdl;
|
|
||||||
#else
|
|
||||||
#ifdef KYTY_DEBUG_LOCKS
|
#ifdef KYTY_DEBUG_LOCKS
|
||||||
std::recursive_timed_mutex m_mutex;
|
constexpr auto DBG_TRY_SECONDS = std::chrono::seconds(15);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct MutexPrivate
|
||||||
|
{
|
||||||
|
#ifdef KYTY_DEBUG_LOCKS
|
||||||
|
std::recursive_timed_mutex m_mutex;
|
||||||
#else
|
#else
|
||||||
std::recursive_mutex m_mutex;
|
std::recursive_mutex m_mutex;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CondVar::CondVarPrivate
|
struct CondVarPrivate
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
SDL_cond* sdl;
|
|
||||||
#else
|
|
||||||
std::condition_variable_any m_cv;
|
std::condition_variable_any m_cv;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WaitForGraph
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using T = int;
|
||||||
|
using R = MutexPrivate*;
|
||||||
|
using Cycle = Vector<int>;
|
||||||
|
|
||||||
|
enum class Link
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Own,
|
||||||
|
Wait,
|
||||||
|
CondVar
|
||||||
|
};
|
||||||
|
|
||||||
|
WaitForGraph() = default;
|
||||||
|
virtual ~WaitForGraph() = default;
|
||||||
|
|
||||||
|
KYTY_CLASS_NO_COPY(WaitForGraph);
|
||||||
|
|
||||||
|
void DbgDump(const Vector<Cycle>& list);
|
||||||
|
Vector<Cycle> DetectDeadlocks();
|
||||||
|
|
||||||
|
int Insert(T t, R r, Link link);
|
||||||
|
void Update(int index, Link link);
|
||||||
|
void DeleteByIndex(int index);
|
||||||
|
void Delete(T t, R r, Link link);
|
||||||
|
void Delete(T t);
|
||||||
|
void Delete(R r);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void FindCycles(Cycle* c, Vector<Cycle>* list);
|
||||||
|
|
||||||
|
static constexpr int MAX_EDGES = 1024;
|
||||||
|
|
||||||
|
struct Edge
|
||||||
|
{
|
||||||
|
T t = 0;
|
||||||
|
R r = nullptr;
|
||||||
|
Link link = Link::None;
|
||||||
|
uint64_t time = 0;
|
||||||
|
DebugStack stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::recursive_mutex m_mutex;
|
||||||
|
Edge m_edges[MAX_EDGES];
|
||||||
|
int m_edges_num = 0;
|
||||||
|
uint64_t m_time = 0;
|
||||||
|
bool m_disabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::atomic<WaitForGraph*> g_wait_for_graph = nullptr;
|
||||||
|
|
||||||
|
struct ThreadPrivate
|
||||||
|
{
|
||||||
|
ThreadPrivate(thread_func_t f, void* a): func(f), arg(a), m_thread(&Run, this) {}
|
||||||
|
|
||||||
|
static void Run(ThreadPrivate* t)
|
||||||
|
{
|
||||||
|
t->unique_id = Thread::GetThreadIdUnique();
|
||||||
|
t->started = true;
|
||||||
|
t->func(t->arg);
|
||||||
|
if (t->auto_delete)
|
||||||
|
{
|
||||||
|
#ifdef KYTY_DEBUG_LOCKS
|
||||||
|
if (g_wait_for_graph != nullptr)
|
||||||
|
{
|
||||||
|
g_wait_for_graph.load()->Delete(t->unique_id);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_func_t func;
|
||||||
|
void* arg;
|
||||||
|
std::atomic_bool finished = false;
|
||||||
|
std::atomic_bool auto_delete = false;
|
||||||
|
std::atomic_bool started = false;
|
||||||
|
int unique_id = 0;
|
||||||
|
std::thread m_thread;
|
||||||
};
|
};
|
||||||
|
|
||||||
static thread_id_t g_main_thread;
|
static thread_id_t g_main_thread;
|
||||||
@@ -90,26 +127,261 @@ static std::atomic<int> g_thread_counter = 0;
|
|||||||
|
|
||||||
KYTY_SUBSYSTEM_INIT(Threads)
|
KYTY_SUBSYSTEM_INIT(Threads)
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
g_main_thread = SDL_ThreadID();
|
|
||||||
#else
|
|
||||||
g_main_thread = std::this_thread::get_id();
|
g_main_thread = std::this_thread::get_id();
|
||||||
g_main_thread_int = Thread::GetThreadIdUnique();
|
g_main_thread_int = Thread::GetThreadIdUnique();
|
||||||
#endif
|
g_wait_for_graph = new WaitForGraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Threads) {}
|
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Threads) {}
|
||||||
|
|
||||||
KYTY_SUBSYSTEM_DESTROY(Threads) {}
|
KYTY_SUBSYSTEM_DESTROY(Threads) {}
|
||||||
|
|
||||||
Thread::Thread(thread_func_t func, void* arg): m_thread(new ThreadPrivate(func, arg)) // @suppress("Symbol is not resolved")
|
void WaitForGraph::DbgDump(const Vector<Cycle>& list)
|
||||||
{
|
{
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
|
||||||
|
m_disabled = true;
|
||||||
|
|
||||||
|
Vector<int> indices;
|
||||||
|
|
||||||
|
for (const auto& c: list)
|
||||||
|
{
|
||||||
|
printf("cycle: ");
|
||||||
|
for (int n: c)
|
||||||
|
{
|
||||||
|
printf("%d ", n);
|
||||||
|
indices.Add(n);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < m_edges_num; index++)
|
||||||
|
{
|
||||||
|
auto& e = m_edges[index];
|
||||||
|
if (e.link != Link::None && (indices.IsEmpty() || indices.Contains(index)))
|
||||||
|
{
|
||||||
|
printf("\n[%d] thread = %d, mutex = %" PRIx64 ", link = %s, time = %" PRIu64 "\n\n", index, e.t,
|
||||||
|
reinterpret_cast<uint64_t>(e.r), Core::EnumName(e.link).C_Str(), e.time);
|
||||||
|
e.stack.Print(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<WaitForGraph::Cycle> WaitForGraph::DetectDeadlocks()
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
|
||||||
|
m_disabled = true;
|
||||||
|
|
||||||
|
Cycle c;
|
||||||
|
Vector<Cycle> list;
|
||||||
|
|
||||||
|
FindCycles(&c, &list);
|
||||||
|
|
||||||
|
m_disabled = false;
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitForGraph::FindCycles(Cycle* c, Vector<Cycle>* list)
|
||||||
|
{
|
||||||
|
uint32_t size = c->Size();
|
||||||
|
int last = -1;
|
||||||
|
if (size > 0)
|
||||||
|
{
|
||||||
|
last = c->At(size - 1);
|
||||||
|
for (uint32_t i = 0; i < size - 1; i++)
|
||||||
|
{
|
||||||
|
if (c->At(i) == last)
|
||||||
|
{
|
||||||
|
list->Add(*c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cycle nc = *c;
|
||||||
|
const auto* l = (last >= 0 ? &m_edges[last] : nullptr);
|
||||||
|
for (int index = 0; index < m_edges_num; index++)
|
||||||
|
{
|
||||||
|
const auto& e = m_edges[index];
|
||||||
|
if (e.link != Link::None && (last < 0 || (l->link == Link::Own && l->r == e.r && e.link == Link::Wait) ||
|
||||||
|
(l->link == Link::Wait && l->t == e.t && e.link == Link::Own)))
|
||||||
|
{
|
||||||
|
nc.Add(index);
|
||||||
|
FindCycles(&nc, list);
|
||||||
|
nc.RemoveAt(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int WaitForGraph::Insert(T t, R r, Link link)
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
|
||||||
|
if (m_disabled)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Edge* edge = nullptr;
|
||||||
|
int index = 0;
|
||||||
|
for (index = 0; index < m_edges_num; index++)
|
||||||
|
{
|
||||||
|
auto& e = m_edges[index];
|
||||||
|
if (e.link == Link::None)
|
||||||
|
{
|
||||||
|
edge = &e;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edge == nullptr)
|
||||||
|
{
|
||||||
|
if (m_edges_num < MAX_EDGES)
|
||||||
|
{
|
||||||
|
edge = &m_edges[index];
|
||||||
|
m_edges_num++;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edge->t = t;
|
||||||
|
edge->r = r;
|
||||||
|
edge->link = link;
|
||||||
|
edge->time = ++m_time;
|
||||||
|
DebugStack::Trace(&edge->stack);
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitForGraph::Update(int index, Link link)
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
|
||||||
|
if (m_disabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= 0 && index < MAX_EDGES)
|
||||||
|
{
|
||||||
|
m_edges[index].link = link;
|
||||||
|
m_edges[index].time = ++m_time;
|
||||||
|
DebugStack::Trace(&m_edges[index].stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitForGraph::DeleteByIndex(int index)
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
|
||||||
|
if (m_disabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= 0 && index < MAX_EDGES)
|
||||||
|
{
|
||||||
|
m_edges[index].link = Link::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitForGraph::Delete(T t, R r, Link l)
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
|
||||||
|
if (m_disabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t max_time = 0;
|
||||||
|
int max_index = -1;
|
||||||
|
for (int index = 0; index < m_edges_num; index++)
|
||||||
|
{
|
||||||
|
auto& e = m_edges[index];
|
||||||
|
if (e.t == t && e.r == r && e.link == l && e.time > max_time)
|
||||||
|
{
|
||||||
|
max_time = e.time;
|
||||||
|
max_index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_index >= 0)
|
||||||
|
{
|
||||||
|
m_edges[max_index].link = Link::None;
|
||||||
|
if (max_index == m_edges_num - 1)
|
||||||
|
{
|
||||||
|
m_edges_num--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitForGraph::Delete(T t)
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
|
||||||
|
if (m_disabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < m_edges_num; index++)
|
||||||
|
{
|
||||||
|
auto& e = m_edges[index];
|
||||||
|
if (e.t == t && e.link != Link::None)
|
||||||
|
{
|
||||||
|
e.link = Link::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitForGraph::Delete(R r)
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_mutex);
|
||||||
|
|
||||||
|
if (m_disabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < m_edges_num; index++)
|
||||||
|
{
|
||||||
|
auto& e = m_edges[index];
|
||||||
|
if (e.r == r && e.link != Link::None)
|
||||||
|
{
|
||||||
|
e.link = Link::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread::Thread(thread_func_t func, void* arg): m_thread(new ThreadPrivate(func, arg))
|
||||||
|
{
|
||||||
|
while (!m_thread->started)
|
||||||
|
{
|
||||||
|
Core::Thread::SleepMicro(1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread::~Thread()
|
Thread::~Thread()
|
||||||
{
|
{
|
||||||
EXIT_IF(!m_thread->finished && !m_thread->auto_delete);
|
EXIT_IF(!m_thread->finished && !m_thread->auto_delete);
|
||||||
|
|
||||||
|
if (m_thread->finished)
|
||||||
|
{
|
||||||
|
#ifdef KYTY_DEBUG_LOCKS
|
||||||
|
if (g_wait_for_graph != nullptr)
|
||||||
|
{
|
||||||
|
g_wait_for_graph.load()->Delete(m_thread->unique_id);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
Delete(m_thread);
|
Delete(m_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,13 +389,7 @@ void Thread::Join()
|
|||||||
{
|
{
|
||||||
EXIT_IF(m_thread->finished || m_thread->auto_delete);
|
EXIT_IF(m_thread->finished || m_thread->auto_delete);
|
||||||
|
|
||||||
#ifdef THREADS_SDL
|
|
||||||
int status = -1;
|
|
||||||
SDL_WaitThread(m_thread->sdl, &status);
|
|
||||||
EXIT_IF(status != 0);
|
|
||||||
#else
|
|
||||||
m_thread->m_thread.join();
|
m_thread->m_thread.join();
|
||||||
#endif
|
|
||||||
|
|
||||||
m_thread->finished = true;
|
m_thread->finished = true;
|
||||||
}
|
}
|
||||||
@@ -133,107 +399,99 @@ void Thread::Detach()
|
|||||||
EXIT_IF(m_thread->finished || m_thread->auto_delete);
|
EXIT_IF(m_thread->finished || m_thread->auto_delete);
|
||||||
|
|
||||||
m_thread->auto_delete = true;
|
m_thread->auto_delete = true;
|
||||||
#ifdef THREADS_SDL
|
|
||||||
SDL_DetachThread(m_thread->sdl);
|
|
||||||
#else
|
|
||||||
m_thread->m_thread.detach();
|
m_thread->m_thread.detach();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::Sleep(uint32_t millis)
|
void Thread::Sleep(uint32_t millis)
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
SDL_Delay(millis);
|
|
||||||
#else
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(millis));
|
std::this_thread::sleep_for(std::chrono::milliseconds(millis));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::SleepMicro(uint32_t micros)
|
void Thread::SleepMicro(uint32_t micros)
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
SDL_Delay(micros / 1000);
|
|
||||||
#else
|
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(micros));
|
std::this_thread::sleep_for(std::chrono::microseconds(micros));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::SleepNano(uint64_t nanos)
|
void Thread::SleepNano(uint64_t nanos)
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
SDL_Delay(nanos / 1000000);
|
|
||||||
#else
|
|
||||||
std::this_thread::sleep_for(std::chrono::nanoseconds(nanos));
|
std::this_thread::sleep_for(std::chrono::nanoseconds(nanos));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Thread::IsMainThread()
|
bool Thread::IsMainThread()
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
return g_main_thread == thread_id_t(SDL_ThreadID());
|
|
||||||
#else
|
|
||||||
return g_main_thread == std::this_thread::get_id();
|
return g_main_thread == std::this_thread::get_id();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String Thread::GetId() const
|
String Thread::GetId() const
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
return String::FromPrintf("%" PRIu64, (uint64_t)SDL_GetThreadID(m_thread->sdl));
|
|
||||||
#else
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << m_thread->m_thread.get_id();
|
ss << m_thread->m_thread.get_id();
|
||||||
return String::FromUtf8(ss.str().c_str());
|
return String::FromUtf8(ss.str().c_str());
|
||||||
#endif
|
}
|
||||||
|
|
||||||
|
int Thread::GetUniqueId() const
|
||||||
|
{
|
||||||
|
return m_thread->unique_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
String Thread::GetThreadId()
|
String Thread::GetThreadId()
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
return String::FromPrintf("%" PRIu64, (uint64_t)SDL_ThreadID());
|
|
||||||
#else
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << std::this_thread::get_id();
|
ss << std::this_thread::get_id();
|
||||||
return String::FromUtf8(ss.str().c_str());
|
return String::FromUtf8(ss.str().c_str());
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex::Mutex(): m_mutex(new MutexPrivate)
|
Mutex::Mutex(): m_mutex(new MutexPrivate) {}
|
||||||
{
|
|
||||||
#ifdef THREADS_SDL
|
|
||||||
m_mutex->sdl = SDL_CreateMutex();
|
|
||||||
EXIT_IF(!m_mutex->sdl);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
Mutex::~Mutex()
|
Mutex::~Mutex()
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
#ifdef KYTY_DEBUG_LOCKS
|
||||||
SDL_DestroyMutex(m_mutex->sdl);
|
if (g_wait_for_graph != nullptr)
|
||||||
|
{
|
||||||
|
g_wait_for_graph.load()->Delete(m_mutex);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
Delete(m_mutex);
|
Delete(m_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mutex::Lock()
|
void Mutex::Lock()
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
SDL_LockMutex(m_mutex->sdl);
|
|
||||||
#else
|
|
||||||
#ifdef KYTY_DEBUG_LOCKS
|
#ifdef KYTY_DEBUG_LOCKS
|
||||||
if (!m_mutex->m_mutex.try_lock_for(std::chrono::seconds(20)))
|
if (g_wait_for_graph != nullptr)
|
||||||
{
|
{
|
||||||
EXIT("lock timeout!");
|
int index = g_wait_for_graph.load()->Insert(Thread::GetThreadIdUnique(), m_mutex, WaitForGraph::Link::Wait);
|
||||||
|
bool locked = false;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
locked = m_mutex->m_mutex.try_lock_for(DBG_TRY_SECONDS);
|
||||||
|
|
||||||
|
if (!locked)
|
||||||
|
{
|
||||||
|
if (auto list = g_wait_for_graph.load()->DetectDeadlocks(); !list.IsEmpty())
|
||||||
|
{
|
||||||
|
g_wait_for_graph.load()->DbgDump(list);
|
||||||
|
EXIT("deadlock!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!locked);
|
||||||
|
g_wait_for_graph.load()->Update(index, WaitForGraph::Link::Own);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
m_mutex->m_mutex.lock();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
m_mutex->m_mutex.lock();
|
m_mutex->m_mutex.lock();
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mutex::Unlock()
|
void Mutex::Unlock()
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
#ifdef KYTY_DEBUG_LOCKS
|
||||||
SDL_UnlockMutex(m_mutex->sdl);
|
if (g_wait_for_graph != nullptr)
|
||||||
|
{
|
||||||
|
g_wait_for_graph.load()->Delete(Thread::GetThreadIdUnique(), m_mutex, WaitForGraph::Link::Own);
|
||||||
|
}
|
||||||
|
m_mutex->m_mutex.unlock();
|
||||||
#else
|
#else
|
||||||
m_mutex->m_mutex.unlock();
|
m_mutex->m_mutex.unlock();
|
||||||
#endif
|
#endif
|
||||||
@@ -241,10 +499,13 @@ void Mutex::Unlock()
|
|||||||
|
|
||||||
bool Mutex::TryLock()
|
bool Mutex::TryLock()
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
#ifdef KYTY_DEBUG_LOCKS
|
||||||
int status = SDL_TryLockMutex(m_mutex->sdl);
|
if (m_mutex->m_mutex.try_lock())
|
||||||
if (status == 0)
|
|
||||||
{
|
{
|
||||||
|
if (g_wait_for_graph != nullptr)
|
||||||
|
{
|
||||||
|
g_wait_for_graph.load()->Insert(Thread::GetThreadIdUnique(), m_mutex, WaitForGraph::Link::Own);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -253,42 +514,40 @@ bool Mutex::TryLock()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
CondVar::CondVar(): m_cond_var(new CondVarPrivate)
|
CondVar::CondVar(): m_cond_var(new CondVarPrivate) {}
|
||||||
{
|
|
||||||
#ifdef THREADS_SDL
|
|
||||||
m_cond_var->sdl = SDL_CreateCond();
|
|
||||||
EXIT_IF(!m_cond_var->sdl);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
CondVar::~CondVar()
|
CondVar::~CondVar()
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
SDL_DestroyCond(m_cond_var->sdl);
|
|
||||||
#endif
|
|
||||||
Delete(m_cond_var);
|
Delete(m_cond_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CondVar::Wait(Mutex* mutex)
|
void CondVar::Wait(Mutex* mutex)
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
SDL_CondWait(m_cond_var->sdl, mutex->m_mutex->sdl);
|
|
||||||
#else
|
|
||||||
#ifdef KYTY_DEBUG_LOCKS
|
#ifdef KYTY_DEBUG_LOCKS
|
||||||
std::unique_lock<std::recursive_timed_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
|
std::unique_lock<std::recursive_timed_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
|
||||||
#else
|
#else
|
||||||
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
|
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef KYTY_DEBUG_LOCKS
|
||||||
|
if (g_wait_for_graph != nullptr)
|
||||||
|
{
|
||||||
|
g_wait_for_graph.load()->Delete(Thread::GetThreadIdUnique(), mutex->m_mutex, WaitForGraph::Link::Own);
|
||||||
|
int index = g_wait_for_graph.load()->Insert(Thread::GetThreadIdUnique(), mutex->m_mutex, WaitForGraph::Link::CondVar);
|
||||||
|
m_cond_var->m_cv.wait(cpp_lock);
|
||||||
|
g_wait_for_graph.load()->DeleteByIndex(index);
|
||||||
|
g_wait_for_graph.load()->Insert(Thread::GetThreadIdUnique(), mutex->m_mutex, WaitForGraph::Link::Own);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
m_cond_var->m_cv.wait(cpp_lock);
|
||||||
|
}
|
||||||
|
#else
|
||||||
m_cond_var->m_cv.wait(cpp_lock);
|
m_cond_var->m_cv.wait(cpp_lock);
|
||||||
cpp_lock.release();
|
|
||||||
#endif
|
#endif
|
||||||
|
cpp_lock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CondVar::WaitFor(Mutex* mutex, uint32_t micros)
|
void CondVar::WaitFor(Mutex* mutex, uint32_t micros)
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
SDL_CondWaitTimeout(m_cond_var->sdl, mutex->m_mutex->sdl, (micros < 1000 ? 1 : micros / 1000));
|
|
||||||
#else
|
|
||||||
#ifdef KYTY_DEBUG_LOCKS
|
#ifdef KYTY_DEBUG_LOCKS
|
||||||
std::unique_lock<std::recursive_timed_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
|
std::unique_lock<std::recursive_timed_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
|
||||||
#else
|
#else
|
||||||
@@ -296,25 +555,16 @@ void CondVar::WaitFor(Mutex* mutex, uint32_t micros)
|
|||||||
#endif
|
#endif
|
||||||
m_cond_var->m_cv.wait_for(cpp_lock, std::chrono::microseconds(micros));
|
m_cond_var->m_cv.wait_for(cpp_lock, std::chrono::microseconds(micros));
|
||||||
cpp_lock.release();
|
cpp_lock.release();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CondVar::Signal()
|
void CondVar::Signal()
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
SDL_CondSignal(m_cond_var->sdl);
|
|
||||||
#else
|
|
||||||
m_cond_var->m_cv.notify_one();
|
m_cond_var->m_cv.notify_one();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CondVar::SignalAll()
|
void CondVar::SignalAll()
|
||||||
{
|
{
|
||||||
#ifdef THREADS_SDL
|
|
||||||
SDL_CondBroadcast(m_cond_var->sdl);
|
|
||||||
#else
|
|
||||||
m_cond_var->m_cv.notify_all();
|
m_cond_var->m_cv.notify_all();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Thread::GetThreadIdUnique()
|
int Thread::GetThreadIdUnique()
|
||||||
|
|||||||
Reference in New Issue
Block a user