feat(libkernel): add pthread module impl(wip)
This commit is contained in:
parent
e79f5ebc58
commit
e77d724a1b
|
|
@ -1,6 +1,8 @@
|
|||
add_library(libkernel SHARED EXCLUDE_FROM_ALL
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DirectMemory/DirectMemory.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DirectMemory/MemoryPool.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/Pthread.cpp
|
||||
)
|
||||
|
||||
target_include_directories(libkernel PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
|
||||
int sceKernelAllocateDirectMemory(int64_t search_start, int64_t search_end, size_t len, size_t alignment, int memory_type, int64_t* phys_addr_out) {
|
||||
(void)memory_type;
|
||||
if (search_start < 0 || search_end <= search_start || len == 0
|
||||
|| (len & (PS5_PAGE_SIZE - 1)) || !phys_addr_out
|
||||
|| (alignment != 0 && (alignment & (PS5_PAGE_SIZE - 1))))
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
return DirectMemoryAlloc(search_start, search_end, len, alignment, phys_addr_out);
|
||||
}
|
||||
|
||||
int sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, int memory_type, int64_t* phys_addr_out) {
|
||||
return sceKernelAllocateDirectMemory(0, static_cast<int64_t>(DIRECT_MEMORY_SIZE), len, alignment, memory_type, phys_addr_out);
|
||||
}
|
||||
|
||||
int sceKernelAvailableDirectMemorySize(int64_t search_start, int64_t search_end, size_t alignment, int64_t* phys_addr_out, size_t* size_out) {
|
||||
if (!phys_addr_out || !size_out) return SCE_KERNEL_ERROR_EINVAL;
|
||||
int64_t tmpPhys = 0;
|
||||
int ret = DirectMemoryAlloc(search_start, search_end, PS5_PAGE_SIZE, alignment, &tmpPhys);
|
||||
if (ret != 0) { *phys_addr_out = 0; *size_out = 0; return ret; }
|
||||
DirectMemoryFree(tmpPhys, PS5_PAGE_SIZE);
|
||||
*phys_addr_out = tmpPhys;
|
||||
*size_out = static_cast<size_t>(search_end) - static_cast<size_t>(tmpPhys);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sceKernelDirectMemoryQuery(int64_t offset, int flags, void* info, size_t info_size) {
|
||||
(void)flags;
|
||||
if (!info || offset < 0) return SCE_KERNEL_ERROR_EINVAL;
|
||||
struct DirectMemoryQueryInfo { int64_t start; int64_t end; int memory_type; };
|
||||
if (info_size < sizeof(DirectMemoryQueryInfo)) return SCE_KERNEL_ERROR_EINVAL;
|
||||
auto* q = static_cast<DirectMemoryQueryInfo*>(info);
|
||||
q->start = offset & ~static_cast<int64_t>(PS5_PAGE_SIZE - 1);
|
||||
q->end = q->start + PS5_PAGE_SIZE;
|
||||
q->memory_type = 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t sceKernelGetDirectMemorySize(void) {
|
||||
return DIRECT_MEMORY_SIZE;
|
||||
}
|
||||
|
||||
int sceKernelMapDirectMemory(void** addr, size_t len, int prot, int flags, int64_t direct_memory_start, size_t alignment) {
|
||||
(void)alignment;
|
||||
return DoMapDirect(addr, len, prot, flags, direct_memory_start, alignment);
|
||||
}
|
||||
|
||||
int sceKernelMapDirectMemory2(void** addr, size_t len, int type, int prot, int flags, int64_t direct_memory_start, size_t alignment) {
|
||||
(void)type;
|
||||
return DoMapDirect(addr, len, prot, flags, direct_memory_start, alignment);
|
||||
}
|
||||
|
||||
int sceKernelMapFlexibleMemory(void** addr_in_out, size_t len, int prot, int flags) {
|
||||
return DoMapAnon(addr_in_out, len, prot, flags);
|
||||
}
|
||||
|
||||
int sceKernelMapNamedDirectMemory(void** addr, size_t len, int prot, int flags, int64_t direct_memory_start, size_t alignment, const char* name) {
|
||||
(void)name;
|
||||
return DoMapDirect(addr, len, prot, flags, direct_memory_start, alignment);
|
||||
}
|
||||
|
||||
int32_t sceKernelMapNamedFlexibleMemory(void** addr_in_out, size_t len, int prot, int flags, const char* name) {
|
||||
(void)name;
|
||||
return DoMapAnon(addr_in_out, len, prot, flags);
|
||||
}
|
||||
|
||||
int sceKernelMprotect(const void* addr, size_t len, int prot) {
|
||||
return DoMprotect(addr, len, prot);
|
||||
}
|
||||
|
||||
int sceKernelMunmap(uint64_t vaddr, size_t len) {
|
||||
return DoMunmap(reinterpret_cast<void*>(vaddr), len);
|
||||
}
|
||||
|
||||
int sceKernelReleaseDirectMemory(int64_t start, size_t len) {
|
||||
if (start < 0 || len == 0) return SCE_KERNEL_ERROR_EINVAL;
|
||||
DirectMemoryFree(start, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sceKernelReserveVirtualRange(void** addr, size_t len, int flags, size_t alignment) {
|
||||
(void)flags;
|
||||
return DoReserveVirtual(addr, len, alignment);
|
||||
}
|
||||
|
||||
int sceKernelVirtualQuery(const void* addr, int flags, VirtualQueryInfo* info, uint64_t info_size) {
|
||||
(void)flags;
|
||||
if (!info || info_size < sizeof(VirtualQueryInfo)) return SCE_KERNEL_ERROR_EINVAL;
|
||||
memset(info, 0, sizeof(VirtualQueryInfo));
|
||||
uintptr_t ptr = reinterpret_cast<uintptr_t>(addr);
|
||||
info->start = ptr & ~static_cast<uintptr_t>(PS5_PAGE_SIZE - 1);
|
||||
info->end = info->start + PS5_PAGE_SIZE;
|
||||
info->is_direct = 1;
|
||||
info->protection = 3;
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,299 @@
|
|||
#include "Pthread.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
static constexpr int SCE_OK = 0;
|
||||
static constexpr int SCE_KERNEL_ERROR_EINVAL = 0x80020016;
|
||||
static constexpr int SCE_KERNEL_ERROR_ENOMEM = 0x8002000C;
|
||||
static constexpr int SCE_KERNEL_ERROR_EBUSY = 0x80020010;
|
||||
static constexpr int SCE_KERNEL_ERROR_EDEADLK = 0x80020023;
|
||||
static constexpr int SCE_KERNEL_ERROR_EPERM = 0x80020001;
|
||||
static constexpr int SCE_KERNEL_ERROR_ETIMEDOUT = 0x80020062;
|
||||
static constexpr int SCE_KERNEL_ERROR_EAGAIN = 0x80020023;
|
||||
static constexpr int SCE_KERNEL_ERROR_ESRCH = 0x80020003;
|
||||
static constexpr int SCE_KERNEL_ERROR_ENOTSUP = 0x80020086;
|
||||
|
||||
static constexpr std::size_t DEFAULT_STACK_SIZE = 1u << 20;
|
||||
static constexpr int DETACH_JOINABLE = 0;
|
||||
static constexpr int DETACH_DETACHED = 1;
|
||||
static constexpr int SCHED_FIFO_PS5 = 1;
|
||||
|
||||
struct ThreadArgs {
|
||||
void* (*entry)(void*);
|
||||
void* arg;
|
||||
PthreadPrivate* self;
|
||||
};
|
||||
|
||||
static void RunThread(ThreadArgs* args) {
|
||||
void* retval = args->entry(args->arg);
|
||||
PthreadPrivate* self = args->self;
|
||||
delete args;
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(self->_join_mtx);
|
||||
self->_retval = retval;
|
||||
self->_finished.store(true, std::memory_order_release);
|
||||
}
|
||||
self->_join_cv.notify_all();
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int scePthreadMutexattrInit(PthreadMutexattr* attr) {
|
||||
if (!attr) throw std::runtime_error("scePthreadMutexattrInit: null attr");
|
||||
auto* p = new (std::nothrow) PthreadMutexattrPrivate{MutexType::Normal};
|
||||
if (!p) return SCE_KERNEL_ERROR_ENOMEM;
|
||||
*attr = p;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadMutexattrDestroy(PthreadMutexattr* attr) {
|
||||
if (!attr || !*attr) throw std::runtime_error("scePthreadMutexattrDestroy: null attr");
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadMutexattrSettype(PthreadMutexattr* attr, int type) {
|
||||
if (!attr || !*attr) throw std::runtime_error("scePthreadMutexattrSettype: null attr");
|
||||
switch (type) {
|
||||
case 1: (*attr)->type = MutexType::ErrorCheck; break;
|
||||
case 2: (*attr)->type = MutexType::Recursive; break;
|
||||
case 3: (*attr)->type = MutexType::Normal; break;
|
||||
default: throw std::runtime_error("scePthreadMutexattrSettype: invalid type");
|
||||
}
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* attr, const char*) {
|
||||
if (!mutex) throw std::runtime_error("scePthreadMutexInit: null mutex");
|
||||
MutexType t = MutexType::Normal;
|
||||
if (attr && *attr) t = (*attr)->type;
|
||||
auto* p = new (std::nothrow) PthreadMutexPrivate();
|
||||
if (!p) return SCE_KERNEL_ERROR_ENOMEM;
|
||||
p->_type = t;
|
||||
*mutex = p;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadMutexDestroy(PthreadMutex* mutex) {
|
||||
if (!mutex || !*mutex) throw std::runtime_error("scePthreadMutexDestroy: null mutex");
|
||||
delete *mutex;
|
||||
*mutex = nullptr;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadMutexLock(PthreadMutex* mutex) {
|
||||
if (!mutex || !*mutex) throw std::runtime_error("scePthreadMutexLock: null mutex");
|
||||
auto* m = *mutex;
|
||||
const auto tid = std::this_thread::get_id();
|
||||
if (m->_type == MutexType::Recursive) {
|
||||
m->_rmtx.lock();
|
||||
m->_owner.store(tid, std::memory_order_relaxed);
|
||||
++m->_count;
|
||||
return SCE_OK;
|
||||
}
|
||||
if (m->_type == MutexType::ErrorCheck) {
|
||||
if (m->_owner.load(std::memory_order_acquire) == tid) return SCE_KERNEL_ERROR_EDEADLK;
|
||||
}
|
||||
m->_mtx.lock();
|
||||
m->_owner.store(tid, std::memory_order_relaxed);
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadMutexUnlock(PthreadMutex* mutex) {
|
||||
if (!mutex || !*mutex) throw std::runtime_error("scePthreadMutexUnlock: null mutex");
|
||||
auto* m = *mutex;
|
||||
if (m->_type == MutexType::ErrorCheck || m->_type == MutexType::Normal) {
|
||||
if (m->_owner.load(std::memory_order_acquire) != std::this_thread::get_id())
|
||||
return SCE_KERNEL_ERROR_EPERM;
|
||||
}
|
||||
if (m->_type == MutexType::Recursive) {
|
||||
if (--m->_count == 0) m->_owner.store(std::thread::id{}, std::memory_order_relaxed);
|
||||
m->_rmtx.unlock();
|
||||
return SCE_OK;
|
||||
}
|
||||
m->_owner.store(std::thread::id{}, std::memory_order_relaxed);
|
||||
m->_mtx.unlock();
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadCondattrInit(PthreadCondattr* attr) {
|
||||
if (!attr) throw std::runtime_error("scePthreadCondattrInit: null attr");
|
||||
auto* p = new (std::nothrow) PthreadCondattrPrivate{0};
|
||||
if (!p) return SCE_KERNEL_ERROR_ENOMEM;
|
||||
*attr = p;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadCondattrDestroy(PthreadCondattr* attr) {
|
||||
if (!attr || !*attr) throw std::runtime_error("scePthreadCondattrDestroy: null attr");
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadCondInit(PthreadCond* cond, const PthreadCondattr*, const char*) {
|
||||
if (!cond) throw std::runtime_error("scePthreadCondInit: null cond");
|
||||
auto* p = new (std::nothrow) PthreadCondPrivate{};
|
||||
if (!p) return SCE_KERNEL_ERROR_ENOMEM;
|
||||
*cond = p;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadCondDestroy(PthreadCond* cond) {
|
||||
if (!cond || !*cond) throw std::runtime_error("scePthreadCondDestroy: null cond");
|
||||
delete *cond;
|
||||
*cond = nullptr;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadCondSignal(PthreadCond* cond) {
|
||||
if (!cond || !*cond) throw std::runtime_error("scePthreadCondSignal: null cond");
|
||||
(*cond)->_cv.notify_one();
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadCondBroadcast(PthreadCond* cond) {
|
||||
if (!cond || !*cond) throw std::runtime_error("scePthreadCondBroadcast: null cond");
|
||||
(*cond)->_cv.notify_all();
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadCondTimedwait(PthreadCond* cond, PthreadMutex* mutex, unsigned int usec) {
|
||||
if (!cond || !*cond || !mutex || !*mutex)
|
||||
throw std::runtime_error("scePthreadCondTimedwait: null arg");
|
||||
auto* m = *mutex;
|
||||
auto* c = *cond;
|
||||
if (m->_type == MutexType::Recursive) {
|
||||
std::unique_lock<std::recursive_timed_mutex> lk(m->_rmtx, std::adopt_lock);
|
||||
auto res = c->_cv.wait_for(lk, std::chrono::microseconds(usec));
|
||||
lk.release();
|
||||
return res == std::cv_status::timeout ? SCE_KERNEL_ERROR_ETIMEDOUT : SCE_OK;
|
||||
}
|
||||
std::unique_lock<std::timed_mutex> lk(m->_mtx, std::adopt_lock);
|
||||
auto res = c->_cv.wait_for(lk, std::chrono::microseconds(usec));
|
||||
lk.release();
|
||||
return res == std::cv_status::timeout ? SCE_KERNEL_ERROR_ETIMEDOUT : SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadAttrInit(PthreadAttr* attr) {
|
||||
if (!attr) throw std::runtime_error("scePthreadAttrInit: null attr");
|
||||
auto* p = new (std::nothrow) PthreadAttrPrivate{};
|
||||
if (!p) return SCE_KERNEL_ERROR_ENOMEM;
|
||||
p->_stacksize = DEFAULT_STACK_SIZE;
|
||||
p->_detachstate = DETACH_JOINABLE;
|
||||
p->_schedpriority = 700;
|
||||
p->_schedpolicy = SCHED_FIFO_PS5;
|
||||
p->_inheritsched = 4;
|
||||
*attr = p;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadAttrDestroy(PthreadAttr* attr) {
|
||||
if (!attr || !*attr) throw std::runtime_error("scePthreadAttrDestroy: null attr");
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadAttrSetdetachstate(PthreadAttr* attr, int detachstate) {
|
||||
if (!attr || !*attr) throw std::runtime_error("scePthreadAttrSetdetachstate: null attr");
|
||||
if (detachstate != DETACH_JOINABLE && detachstate != DETACH_DETACHED)
|
||||
throw std::runtime_error("scePthreadAttrSetdetachstate: invalid state");
|
||||
(*attr)->_detachstate = detachstate;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadAttrSetschedparam(PthreadAttr* attr, const KernelSchedParam* param) {
|
||||
if (!attr || !*attr || !param)
|
||||
throw std::runtime_error("scePthreadAttrSetschedparam: null arg");
|
||||
(*attr)->_schedpriority = param->sched_priority;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadAttrSetstacksize(PthreadAttr* attr, std::size_t stacksize) {
|
||||
if (!attr || !*attr) throw std::runtime_error("scePthreadAttrSetstacksize: null attr");
|
||||
if (stacksize < 16384) throw std::runtime_error("scePthreadAttrSetstacksize: too small");
|
||||
(*attr)->_stacksize = stacksize;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadAttrGetstack(const PthreadAttr* attr, void** stackaddr, std::size_t* stacksize) {
|
||||
if (!attr || !*attr || !stackaddr || !stacksize)
|
||||
throw std::runtime_error("scePthreadAttrGetstack: null arg");
|
||||
*stackaddr = nullptr;
|
||||
*stacksize = (*attr)->_stacksize;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadAttrGet(Pthread thread, PthreadAttr* attr) {
|
||||
if (!thread || !attr || !*attr)
|
||||
throw std::runtime_error("scePthreadAttrGet: null arg");
|
||||
(*attr)->_stacksize = DEFAULT_STACK_SIZE;
|
||||
(*attr)->_detachstate = thread->_detached ? DETACH_DETACHED : DETACH_JOINABLE;
|
||||
(*attr)->_schedpriority = 700;
|
||||
(*attr)->_schedpolicy = SCHED_FIFO_PS5;
|
||||
(*attr)->_inheritsched = 4;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadCreate(Pthread* thread, const PthreadAttr* attr, void* (*entry)(void*), void* arg, const char*) {
|
||||
if (!thread || !entry) throw std::runtime_error("scePthreadCreate: null arg");
|
||||
auto* p = new (std::nothrow) PthreadPrivate{};
|
||||
if (!p) return SCE_KERNEL_ERROR_ENOMEM;
|
||||
bool detached = false;
|
||||
if (attr && *attr) detached = ((*attr)->_detachstate == DETACH_DETACHED);
|
||||
p->_detached = detached;
|
||||
auto* targs = new ThreadArgs{entry, arg, p};
|
||||
p->_thr = std::thread([targs]() { RunThread(targs); });
|
||||
if (detached) p->_thr.detach();
|
||||
*thread = p;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadJoin(Pthread thread, void** retval) {
|
||||
if (!thread) throw std::runtime_error("scePthreadJoin: null thread");
|
||||
if (thread->_detached) return SCE_KERNEL_ERROR_EINVAL;
|
||||
if (thread->_thr.joinable()) thread->_thr.join();
|
||||
if (retval) *retval = thread->_retval;
|
||||
delete thread;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int scePthreadDetach(Pthread thread) {
|
||||
if (!thread) throw std::runtime_error("scePthreadDetach: null thread");
|
||||
if (thread->_detached) return SCE_KERNEL_ERROR_EINVAL;
|
||||
thread->_detached = true;
|
||||
if (thread->_thr.joinable()) thread->_thr.detach();
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
void scePthreadExit(void* retval) {
|
||||
#ifdef _WIN32
|
||||
ExitThread(0);
|
||||
#else
|
||||
pthread_exit(retval);
|
||||
#endif
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Pthread scePthreadSelf() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void scePthreadYield() {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
#include <sched.h>
|
||||
#include "SceTypes.hpp"
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
enum class MutexType : std::uint32_t {
|
||||
ErrorCheck = 1,
|
||||
Recursive = 2,
|
||||
Normal = 3,
|
||||
};
|
||||
|
||||
struct PthreadMutexattrPrivate {
|
||||
MutexType type;
|
||||
};
|
||||
|
||||
struct PthreadMutexPrivate {
|
||||
std::recursive_timed_mutex _rmtx;
|
||||
std::timed_mutex _mtx;
|
||||
MutexType _type;
|
||||
std::atomic<std::thread::id> _owner;
|
||||
int _count;
|
||||
|
||||
PthreadMutexPrivate() : _type(MutexType::Normal), _count(0) {}
|
||||
};
|
||||
|
||||
struct PthreadCondattrPrivate {
|
||||
int _clockid;
|
||||
};
|
||||
|
||||
struct PthreadCondPrivate {
|
||||
std::condition_variable_any _cv;
|
||||
};
|
||||
|
||||
struct PthreadAttrPrivate {
|
||||
std::size_t _stacksize;
|
||||
int _detachstate;
|
||||
int _schedpriority;
|
||||
int _schedpolicy;
|
||||
int _inheritsched;
|
||||
};
|
||||
|
||||
struct PthreadPrivate {
|
||||
std::thread _thr;
|
||||
std::atomic<bool> _finished;
|
||||
void* _retval;
|
||||
bool _detached;
|
||||
std::mutex _join_mtx;
|
||||
std::condition_variable _join_cv;
|
||||
|
||||
PthreadPrivate() : _finished(false), _retval(nullptr), _detached(false) {}
|
||||
};
|
||||
Loading…
Reference in New Issue