feat(thread): add basic thread functions

This commit is contained in:
fallenoak 2023-03-04 10:50:12 -06:00 committed by GitHub
parent 0242759cbe
commit 39f4bd35a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 296 additions and 0 deletions

161
bc/System_Thread.cpp Normal file
View file

@ -0,0 +1,161 @@
#include "bc/System_Thread.hpp"
#include "bc/Debug.hpp"
#include "bc/Memory.hpp"
#include "bc/String.hpp"
#if defined(WHOA_SYSTEM_WIN)
#include <windows.h>
#endif
#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
#include <pthread.h>
#endif
bool Blizzard::System_Thread::s_initialized;
Blizzard::Thread::ThreadRecord* Blizzard::System_Thread::s_mainThread;
Blizzard::Lock::Mutex Blizzard::System_Thread::s_mutex;
Blizzard::Lock::Mutex Blizzard::System_Thread::s_registryMutex;
Blizzard::Thread::TLSSlot Blizzard::System_Thread::s_stackTraceEntryPointTLS;
Blizzard::Thread::TLSSlot Blizzard::System_Thread::s_threadRecordTLS;
std::map<Blizzard::Thread::ThreadRecord*, Blizzard::Thread::ThreadRecord*>* Blizzard::System_Thread::s_threadRegistry;
Blizzard::Thread::TLSSlot* Blizzard::System_Thread::s_slotList[128];
int32_t Blizzard::System_Thread::s_slotListUsed;
void Blizzard::System_Thread::AddToRegistry(Thread::ThreadRecord* thread) {
Blizzard::Lock::MutexEnter(System_Thread::s_registryMutex);
if (!System_Thread::s_threadRegistry) {
auto m = Blizzard::Memory::Allocate(sizeof(std::map<Thread::ThreadRecord*, Thread::ThreadRecord*>), 0, __FILE__, __LINE__, nullptr);
System_Thread::s_threadRegistry = new (m) std::map<Thread::ThreadRecord*, Thread::ThreadRecord*>();
}
System_Thread::s_threadRegistry->insert(std::pair<Thread::ThreadRecord*, Thread::ThreadRecord*>(thread, thread));
Blizzard::Lock::MutexLeave(System_Thread::s_registryMutex);
}
bool Blizzard::System_Thread::AllocateLocalStorage(Thread::TLSSlot* slot, void (*destructor)(void*)) {
System_Thread::InitThreadSystem();
BLIZZARD_ASSERT(!System_Thread::TLSSlotIsAllocated(slot));
if (!System_Thread::InternalAllocateLocalStorage(slot, destructor)) {
BLIZZARD_ASSERT(!"failed to allocate TLS");
return false;
}
slot->destructor = destructor;
Blizzard::Lock::MutexEnter(System_Thread::s_mutex);
System_Thread::s_slotList[System_Thread::s_slotListUsed] = slot;
System_Thread::s_slotListUsed++;
Blizzard::Lock::MutexLeave(System_Thread::s_mutex);
return true;
}
bool Blizzard::System_Thread::AllocateTLSSlot(Thread::TLSSlot* slot, void (*destructor)(void*)) {
System_Thread::InitThreadSystem();
Blizzard::Lock::MutexEnter(System_Thread::s_mutex);
auto result = false;
if (System_Thread::TLSSlotIsAllocated(slot) || System_Thread::AllocateLocalStorage(slot, destructor)) {
result = true;
}
Blizzard::Lock::MutexLeave(System_Thread::s_mutex);
return result;
}
void Blizzard::System_Thread::InitThreadSystem() {
if (System_Thread::s_initialized) {
return;
}
System_Thread::s_initialized = true;
Blizzard::Lock::MutexCreate(System_Thread::s_mutex);
Blizzard::Lock::MutexCreate(System_Thread::s_registryMutex);
Blizzard::Thread::AllocateLocalStorage(&System_Thread::s_threadRecordTLS);
Blizzard::Thread::AllocateLocalStorage(&System_Thread::s_stackTraceEntryPointTLS);
System_Thread::s_mainThread = System_Thread::NewThread(nullptr, nullptr, nullptr);
Blizzard::Thread::SetLocalStorage(&System_Thread::s_threadRecordTLS, System_Thread::s_mainThread);
System_Thread::s_mainThread->unkC = 1;
System_Thread::s_mainThread->unk10 = 1;
}
bool Blizzard::System_Thread::InternalAllocateLocalStorage(Thread::TLSSlot* slot, void (*destructor)(void*)) {
#if defined(WHOA_SYSTEM_WIN)
auto index = TlsAlloc();
if (index == TLS_OUT_OF_INDEXES) {
return false;
}
slot->key = index;
slot->allocated = true;
return true;
#elif defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
if (pthread_key_create(&slot->key, destructor)) {
return false;
}
slot->allocated = true;
return true;
#endif
}
void* Blizzard::System_Thread::InternalGetLocalStorage(const Thread::TLSSlot* slot) {
#if defined(WHOA_SYSTEM_WIN)
return TlsGetValue(slot->key);
#elif defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
return pthread_getspecific(slot->key);
#endif
}
void Blizzard::System_Thread::InternalSetLocalStorage(const Thread::TLSSlot* slot, const void* value) {
#if defined(WHOA_SYSTEM_WIN)
auto result = TlsSetValue(slot->key, const_cast<void*>(value));
BLIZZARD_ASSERT(result);
#elif defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
auto err = pthread_setspecific(slot->key, value);
BLIZZARD_ASSERT(err == 0);
#endif
}
Blizzard::Thread::ThreadRecord* Blizzard::System_Thread::NewThread(uint32_t (*a1)(void*), void* a2, const char* name) {
if (!name) {
name = "";
}
auto nameLen = Blizzard::String::Length(name);
auto thread = static_cast<Blizzard::Thread::ThreadRecord*>(
Blizzard::Memory::Allocate(sizeof(Blizzard::Thread::ThreadRecord) + nameLen + 1, 0, __FILE__, __LINE__, nullptr)
);
Blizzard::String::MemFill(thread, sizeof(Blizzard::Thread::ThreadRecord), 0);
thread->unk4 = a2;
thread->unk8 = a1;
thread->unkC = 0;
thread->unk10 = 2;
Blizzard::String::Copy(&thread->name, name, nameLen + 1);
System_Thread::AddToRegistry(thread);
return thread;
}
bool Blizzard::System_Thread::TLSSlotIsAllocated(const Thread::TLSSlot* slot) {
return slot->allocated;
}

37
bc/System_Thread.hpp Normal file
View file

@ -0,0 +1,37 @@
#ifndef BC_SYSTEM_THREAD_HPP
#define BC_SYSTEM_THREAD_HPP
#include "bc/Lock.hpp"
#include "bc/Thread.hpp"
#include <cstdint>
#include <map>
namespace Blizzard {
namespace System_Thread {
// Variables
extern bool s_initialized;
extern Thread::ThreadRecord* s_mainThread;
extern Lock::Mutex s_mutex;
extern Lock::Mutex s_registryMutex;
extern Thread::TLSSlot s_stackTraceEntryPointTLS;
extern Thread::TLSSlot s_threadRecordTLS;
extern std::map<Thread::ThreadRecord*, Thread::ThreadRecord*>* s_threadRegistry;
extern Thread::TLSSlot* s_slotList[128];
extern int32_t s_slotListUsed;
// Functions
void AddToRegistry(Thread::ThreadRecord* thread);
bool AllocateLocalStorage(Thread::TLSSlot* slot, void (*destructor)(void*));
bool AllocateTLSSlot(Thread::TLSSlot* slot, void (*destructor)(void*));
void InitThreadSystem();
bool InternalAllocateLocalStorage(Thread::TLSSlot* slot, void (*destructor)(void*));
void* InternalGetLocalStorage(const Thread::TLSSlot* slot);
void InternalSetLocalStorage(const Thread::TLSSlot* slot, const void* value);
Thread::ThreadRecord* NewThread(uint32_t (*a1)(void*), void* a2, const char* name);
bool TLSSlotIsAllocated(const Thread::TLSSlot* slot);
} // namespace System_Thread
} // namespace Blizzard
#endif

33
bc/Thread.cpp Normal file
View file

@ -0,0 +1,33 @@
#include "bc/Thread.hpp"
#include "bc/Debug.hpp"
#include "bc/System_Thread.hpp"
void Blizzard::Thread::AllocateLocalStorage(TLSSlot* slot) {
System_Thread::AllocateLocalStorage(slot, nullptr);
}
void* Blizzard::Thread::RegisterLocalStorage(TLSSlot* slot, void* (*constructor)(void*), void* userData, void (*destructor)(void*)) {
if (!System_Thread::TLSSlotIsAllocated(slot) && !System_Thread::AllocateTLSSlot(slot, destructor)) {
BLIZZARD_ASSERT(!"Unable to allocate thread-local storage");
}
auto value = System_Thread::InternalGetLocalStorage(slot);
if (value) {
return value;
}
value = constructor(userData);
System_Thread::InternalSetLocalStorage(slot, value);
return value;
}
void Blizzard::Thread::SetLocalStorage(const TLSSlot* slot, const void* value) {
BLIZZARD_ASSERT(Blizzard::Thread::TLSSlotIsAllocated(slot));
System_Thread::InternalSetLocalStorage(slot, value);
}
bool Blizzard::Thread::TLSSlotIsAllocated(const TLSSlot* slot) {
return System_Thread::TLSSlotIsAllocated(slot);
}

44
bc/Thread.hpp Normal file
View file

@ -0,0 +1,44 @@
#ifndef BC_THREAD_HPP
#define BC_THREAD_HPP
#include <cstdint>
#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
#include <pthread.h>
#endif
namespace Blizzard {
namespace Thread {
// Types
struct ThreadRecord {
#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
pthread_t unk0;
#endif
void* unk4;
uint32_t (*unk8)(void*);
int32_t unkC;
int32_t unk10;
char name;
};
struct TLSSlot {
#if defined(WHOA_SYSTEM_WIN)
uint32_t key;
#elif defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX)
pthread_key_t key;
#endif
void (*destructor)(void*);
bool allocated;
};
// Functions
void AllocateLocalStorage(TLSSlot* slot);
void* RegisterLocalStorage(TLSSlot* slot, void* (*constructor)(void*), void* userData, void (*destructor)(void*));
void SetLocalStorage(const TLSSlot* slot, const void* value);
bool TLSSlotIsAllocated(const TLSSlot* slot);
} // namespace Thread
} // namespace Blizzard
#endif

21
test/Thread.cpp Normal file
View file

@ -0,0 +1,21 @@
#include "bc/Thread.hpp"
#include "bc/Memory.hpp"
#include "test/Test.hpp"
void* ConstructInteger(void* value) {
auto ptr = reinterpret_cast<int32_t*>(Blizzard::Memory::Allocate(sizeof(int32_t)));
*ptr = *reinterpret_cast<int32_t*>(value);
return ptr;
}
TEST_CASE("Blizzard::Thread::RegisterLocalStorage", "[thread]") {
SECTION("constructs value and stores it in available TLS slot") {
Blizzard::Thread::TLSSlot slot {};
Blizzard::Thread::AllocateLocalStorage(&slot);
int32_t value = 12345;
auto ptr = Blizzard::Thread::RegisterLocalStorage(&slot, ConstructInteger, &value, nullptr);
REQUIRE(*reinterpret_cast<int32_t*>(ptr) == value);
}
}