mirror of
https://github.com/SysRay/psOff_public.git
synced 2024-11-23 06:19:41 +00:00
fix semaphore
This commit is contained in:
parent
9e3dab85c3
commit
3007e81a1e
@ -4,7 +4,7 @@ add_library(kernel OBJECT
|
||||
errors.cpp
|
||||
filesystem.cpp
|
||||
pthread.cpp
|
||||
semaphore.cpp
|
||||
semaphore_fifo.cpp
|
||||
)
|
||||
|
||||
add_dependencies(kernel third_party boost)
|
@ -1,199 +0,0 @@
|
||||
#include "semaphore.h"
|
||||
|
||||
#include "logging.h"
|
||||
#include "modules_include/common.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include <boost/chrono.hpp>
|
||||
#include <boost/thread/condition.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
LOG_DEFINE_MODULE(Semaphore)
|
||||
|
||||
namespace {
|
||||
size_t getUniqueId() {
|
||||
static size_t count = 0;
|
||||
return count++;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class Semaphore: public ISemaphore {
|
||||
|
||||
enum class Status { Set, Canceled, Deleted };
|
||||
|
||||
boost::mutex m_mutex;
|
||||
boost::condition_variable m_condState;
|
||||
|
||||
std::queue<std::shared_ptr<boost::condition_variable>> m_condQueue; // todo: optimze
|
||||
|
||||
std::string m_name;
|
||||
size_t m_waitCounter = 0;
|
||||
size_t const m_id = getUniqueId();
|
||||
int m_countThreads = 0;
|
||||
|
||||
Status m_state = Status::Set;
|
||||
bool m_fifo;
|
||||
int m_count;
|
||||
int m_max_count;
|
||||
|
||||
public:
|
||||
Semaphore(const std::string& name, bool fifo, int init_count, int max_count): m_name(name), m_fifo(fifo), m_count(init_count), m_max_count(max_count) {
|
||||
if (!fifo) m_condQueue.push(std::make_shared<boost::condition_variable>());
|
||||
};
|
||||
|
||||
virtual ~Semaphore();
|
||||
|
||||
int cancel(int setCount, int* numWaitingThreads) final;
|
||||
int signal(int signalCount) final;
|
||||
int wait(int needcount, uint32_t* pMicros) final;
|
||||
int try_wait(int needcount, uint32_t* pMicros) final;
|
||||
|
||||
std::string_view const getName() const final { return m_name; }
|
||||
|
||||
size_t getId() const final { return m_id; }
|
||||
|
||||
private:
|
||||
int wait_internal(int needCount, uint32_t* pMicros, boost::unique_lock<boost::mutex>& lock);
|
||||
};
|
||||
|
||||
std::unique_ptr<ISemaphore> createSemaphore(const char* name, bool fifo, int initCount, int maxCount) {
|
||||
return std::make_unique<Semaphore>(name == nullptr ? "" : name, fifo, initCount, maxCount);
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore() {
|
||||
boost::unique_lock lock(m_mutex);
|
||||
m_state = Status::Deleted;
|
||||
|
||||
while (!m_condQueue.empty()) {
|
||||
m_condQueue.front()->notify_all();
|
||||
m_condQueue.pop();
|
||||
}
|
||||
|
||||
// Wait for Threads to leave wait
|
||||
m_condState.wait(lock, [this] { return m_countThreads == 0; });
|
||||
}
|
||||
|
||||
int Semaphore::cancel(int setCount, int* numWaitingThreads) {
|
||||
boost::unique_lock lock(m_mutex);
|
||||
|
||||
m_state = Status::Canceled;
|
||||
if (numWaitingThreads != nullptr) *numWaitingThreads = m_countThreads;
|
||||
|
||||
if (m_fifo) {
|
||||
while (!m_condQueue.empty()) {
|
||||
m_condQueue.front()->notify_one();
|
||||
m_condQueue.pop();
|
||||
}
|
||||
} else {
|
||||
m_condQueue.front()->notify_all();
|
||||
}
|
||||
|
||||
// Wait for Threads to leave wait
|
||||
m_condState.wait(lock, [this] { return m_countThreads == 0; });
|
||||
m_state = Status::Set;
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int Semaphore::signal(int signalCount) {
|
||||
LOG_USE_MODULE(Semaphore);
|
||||
|
||||
boost::unique_lock lock(m_mutex);
|
||||
LOG_TRACE(L"KernelSema(%llu) name:%S signal:%d count:%d", m_id, m_name.c_str(), signalCount, m_count);
|
||||
|
||||
m_condState.wait(lock, [this] { return m_state != Status::Canceled; });
|
||||
if (m_state == Status::Deleted) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
|
||||
if (m_count + signalCount > m_max_count) {
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
if (signalCount > 0) {
|
||||
m_count += signalCount;
|
||||
if (!m_condQueue.empty()) m_condQueue.front()->notify_one();
|
||||
}
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int Semaphore::wait_internal(int needCount, uint32_t* pMicros, boost::unique_lock<boost::mutex>& lock) {
|
||||
LOG_USE_MODULE(Semaphore);
|
||||
std::chrono::time_point<std::chrono::system_clock> start = std::chrono::system_clock::now();
|
||||
uint32_t const micros = pMicros != nullptr ? *pMicros : 0;
|
||||
size_t waitCount = 0;
|
||||
{
|
||||
|
||||
if (m_state == Status::Deleted) return getErr(ErrCode::_EACCES);
|
||||
|
||||
std::shared_ptr<boost::condition_variable> condVar;
|
||||
if (m_fifo) {
|
||||
condVar = std::make_shared<boost::condition_variable>();
|
||||
m_condQueue.push(condVar);
|
||||
} else {
|
||||
condVar = m_condQueue.front();
|
||||
}
|
||||
|
||||
waitCount = m_waitCounter++;
|
||||
LOG_TRACE(L"-> KernelSema(%llu) name:%S waitCount:%llu need:%d count:%d time:%u us", m_id, m_name.c_str(), waitCount, needCount, m_count, micros);
|
||||
|
||||
m_countThreads++;
|
||||
if (pMicros != nullptr) {
|
||||
if (*pMicros == 0) {
|
||||
if (m_count < needCount) {
|
||||
*pMicros = 0;
|
||||
m_countThreads--;
|
||||
m_condState.notify_all();
|
||||
return getErr(ErrCode::_ETIMEDOUT);
|
||||
}
|
||||
} else {
|
||||
if (!condVar->wait_for(lock, boost::chrono::microseconds(micros),
|
||||
[this, condVar, needCount] { return m_state != Status::Set || (m_condQueue.front() == condVar && m_count >= needCount); })) {
|
||||
LOG_WARN(L"<- KernelSema(%llu) name:%S waitCount:%llu timeout", m_id, m_name.c_str(), waitCount);
|
||||
*pMicros = 0;
|
||||
m_countThreads--;
|
||||
m_condState.notify_all();
|
||||
return getErr(ErrCode::_ETIMEDOUT);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
condVar->wait(lock, [this, condVar, needCount] { return m_state != Status::Set || (m_condQueue.front() == condVar && m_count >= needCount); });
|
||||
}
|
||||
|
||||
if (m_fifo) m_condQueue.pop();
|
||||
m_count -= needCount;
|
||||
m_countThreads--;
|
||||
|
||||
m_condState.notify_all();
|
||||
|
||||
if (m_count >= 0 && !m_condQueue.empty()) m_condQueue.front()->notify_one(); // notify next in queue if count is != 0
|
||||
}
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - start).count();
|
||||
LOG_TRACE(L"<- KernelSema(%llu) name:%S waitCount:%llu count:%d waiting:%llu", m_id, m_name.c_str(), waitCount, m_count, m_condQueue.size());
|
||||
|
||||
if (pMicros != nullptr) {
|
||||
*pMicros = (elapsed >= micros ? 0 : micros - elapsed);
|
||||
}
|
||||
|
||||
if (m_state == Status::Canceled) {
|
||||
return getErr(ErrCode::_ECANCELED);
|
||||
}
|
||||
|
||||
if (m_state == Status::Deleted) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int Semaphore::wait(int needCount, uint32_t* pMicros) {
|
||||
|
||||
boost::unique_lock lock(m_mutex);
|
||||
return wait_internal(needCount, pMicros, lock);
|
||||
}
|
||||
|
||||
int Semaphore::try_wait(int needCount, uint32_t* pMicros) {
|
||||
boost::unique_lock lock(m_mutex);
|
||||
if (m_countThreads > 0) return getErr(ErrCode::_EAGAIN);
|
||||
|
||||
return wait_internal(needCount, pMicros, lock);
|
||||
}
|
@ -13,19 +13,17 @@ class ISemaphore {
|
||||
public:
|
||||
virtual ~ISemaphore() = default;
|
||||
|
||||
virtual int cancel(int setCount, int* numWaitingThreads) = 0;
|
||||
virtual int signal(int signalCount) = 0;
|
||||
virtual int wait(int needcount, uint32_t* pMicros) = 0;
|
||||
virtual int try_wait(int needcount, uint32_t* pMicros) = 0;
|
||||
|
||||
int poll(int needCount) {
|
||||
uint32_t micros = 0;
|
||||
return wait(needCount, µs);
|
||||
}
|
||||
virtual int cancel(int setCount, int* numCanceled) = 0;
|
||||
virtual int signal(int signalCount) = 0;
|
||||
virtual int wait(int needcount, uint32_t* pMicros) = 0;
|
||||
virtual int try_wait(int needcount, uint32_t* pMicros) = 0;
|
||||
virtual int poll(int needCount) = 0;
|
||||
|
||||
virtual std::string_view const getName() const = 0;
|
||||
|
||||
virtual size_t getId() const = 0;
|
||||
|
||||
virtual size_t getSignalCounter() const = 0;
|
||||
};
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
@ -36,6 +34,7 @@ class ISemaphore {
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
__APICALL std::unique_ptr<ISemaphore> createSemaphore(const char* name, bool fifo, int initCount, int maxCount);
|
||||
__APICALL std::unique_ptr<ISemaphore> createSemaphore_fifo(const char* name, int initCount, int maxCount);
|
||||
__APICALL std::unique_ptr<ISemaphore> createSemaphore_prio(const char* name, int initCount, int maxCount);
|
||||
|
||||
#undef __APICALL
|
309
core/kernel/semaphore_fifo.cpp
Normal file
309
core/kernel/semaphore_fifo.cpp
Normal file
@ -0,0 +1,309 @@
|
||||
#include "logging.h"
|
||||
#include "modules_include/common.h"
|
||||
#include "pthread.h"
|
||||
#include "semaphore.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include <boost/chrono.hpp>
|
||||
#include <boost/thread/condition.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <map>
|
||||
|
||||
LOG_DEFINE_MODULE(Semaphore)
|
||||
|
||||
namespace {
|
||||
enum class SemState { idle, signaled, waiting, canceled };
|
||||
|
||||
struct SemData {
|
||||
size_t index = 0;
|
||||
|
||||
uint32_t needs = 0;
|
||||
|
||||
SemState state = SemState::idle;
|
||||
|
||||
boost::condition_variable m_condVar;
|
||||
|
||||
SemData* parent = nullptr;
|
||||
SemData* child = nullptr;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class Semaphore: public ISemaphore {
|
||||
boost::mutex m_mutexInt;
|
||||
|
||||
boost::condition_variable m_condState;
|
||||
|
||||
size_t const m_id = [] {
|
||||
static size_t count = 0;
|
||||
return count++;
|
||||
}();
|
||||
|
||||
std::string const m_name;
|
||||
int const m_maxCount;
|
||||
|
||||
uint32_t m_signalValue = 0;
|
||||
|
||||
bool m_isStop = false;
|
||||
|
||||
std::map<size_t, std::shared_ptr<SemData>> m_semData; // todo use pthread specific
|
||||
|
||||
SemData* m_curBack = nullptr;
|
||||
SemData* m_curStart = nullptr;
|
||||
|
||||
size_t m_numWaiters = 0;
|
||||
|
||||
size_t m_countWaits = 0;
|
||||
|
||||
public:
|
||||
Semaphore(const std::string& name, int initCount, int maxCount): m_name(name), m_maxCount(maxCount) { m_signalValue = initCount; };
|
||||
|
||||
virtual ~Semaphore() {
|
||||
m_isStop = true;
|
||||
|
||||
boost::unique_lock lock(m_mutexInt);
|
||||
|
||||
while (m_curStart != nullptr) {
|
||||
m_curStart->m_condVar.notify_one();
|
||||
m_curStart = m_curStart->child;
|
||||
}
|
||||
|
||||
m_condState.wait(lock, [this] { return m_numWaiters == 0; });
|
||||
}
|
||||
|
||||
// ### Interface
|
||||
std::string_view const getName() const final { return m_name; }
|
||||
|
||||
size_t getSignalCounter() const final { return m_signalValue; }
|
||||
|
||||
size_t getId() const final { return m_id; }
|
||||
|
||||
int cancel(int setCount, int* numCanceled) final;
|
||||
int signal(int signalCount) final;
|
||||
int wait(int needcount, uint32_t* pMicros) final;
|
||||
int try_wait(int needcount, uint32_t* pMicros) final;
|
||||
int poll(int needCount) final;
|
||||
|
||||
private:
|
||||
int wait_internal(int needCount, uint32_t* pMicros, boost::unique_lock<boost::mutex>& lock);
|
||||
|
||||
int poll_internal(int needCount, boost::unique_lock<boost::mutex>& lock);
|
||||
};
|
||||
|
||||
std::unique_ptr<ISemaphore> createSemaphore_fifo(const char* name, int initCount, int maxCount) {
|
||||
return std::make_unique<Semaphore>(name == nullptr ? "" : name, initCount, maxCount);
|
||||
}
|
||||
|
||||
int Semaphore::cancel(int setCount, int* numCanceled) {
|
||||
LOG_USE_MODULE(Semaphore);
|
||||
|
||||
if (setCount <= 0 || setCount >= m_maxCount) return getErr(ErrCode::_EINVAL);
|
||||
|
||||
boost::unique_lock lock(m_mutexInt);
|
||||
|
||||
*numCanceled = 0;
|
||||
auto start = m_curStart;
|
||||
while (start != nullptr) {
|
||||
if (start->needs > setCount) break;
|
||||
|
||||
++*numCanceled;
|
||||
setCount -= start->needs;
|
||||
start->state = SemState::canceled;
|
||||
|
||||
start = start->child; // next
|
||||
}
|
||||
|
||||
m_signalValue = 0;
|
||||
|
||||
// Notify back
|
||||
if (*numCanceled > 0) {
|
||||
lock.unlock();
|
||||
start->m_condVar.notify_one();
|
||||
}
|
||||
// -
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int Semaphore::signal(int signalCount) {
|
||||
LOG_USE_MODULE(Semaphore);
|
||||
|
||||
boost::unique_lock lock(m_mutexInt);
|
||||
|
||||
m_signalValue += signalCount;
|
||||
|
||||
// Notify back
|
||||
if (m_curStart != nullptr && m_curStart->needs <= m_signalValue) {
|
||||
m_curStart->state = SemState::signaled;
|
||||
LOG_TRACE(L"KernelSema(%llu) name:%S notify| count:%d index:%llu", m_id, m_name.c_str(), m_signalValue, m_curStart->index);
|
||||
|
||||
auto& cond = m_curStart->m_condVar;
|
||||
lock.unlock();
|
||||
cond.notify_one(); // race condition if m_curStart is used
|
||||
} else {
|
||||
LOG_TRACE(L"KernelSema(%llu) name:%S signal| count:%d", m_id, m_name.c_str(), m_signalValue);
|
||||
}
|
||||
// -
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int Semaphore::wait_internal(int needCount, uint32_t* pMicros, boost::unique_lock<boost::mutex>& lock) {
|
||||
LOG_USE_MODULE(Semaphore);
|
||||
|
||||
if (poll_internal(needCount, lock) == Ok) {
|
||||
return Ok;
|
||||
}
|
||||
|
||||
auto itThread = m_semData.find(pthread::getThreadId());
|
||||
if (itThread == m_semData.end()) {
|
||||
itThread = m_semData.emplace(std::make_pair(pthread::getThreadId(), std::make_shared<SemData>())).first;
|
||||
}
|
||||
|
||||
++m_numWaiters;
|
||||
|
||||
auto ownData = itThread->second;
|
||||
ownData->index = ++m_countWaits;
|
||||
ownData->needs = needCount;
|
||||
|
||||
// enque in list
|
||||
if (m_curBack != nullptr) {
|
||||
m_curBack->child = ownData.get();
|
||||
}
|
||||
if (m_curStart == nullptr) {
|
||||
m_curStart = ownData.get();
|
||||
}
|
||||
ownData->parent = m_curBack;
|
||||
m_curBack = ownData.get();
|
||||
// - list
|
||||
|
||||
int ret = Ok;
|
||||
ownData->state = SemState::waiting;
|
||||
|
||||
LOG_TRACE(L"-> KernelSema(%llu) name:%S wait| count:%d needs:%d index:%llu state:%d", m_id, m_name.c_str(), m_signalValue, needCount, ownData->index,
|
||||
ownData->state);
|
||||
|
||||
if (pMicros == nullptr) {
|
||||
ownData->m_condVar.wait(lock, [this, ownData] { return m_isStop || ownData->state != SemState::waiting; });
|
||||
} else {
|
||||
std::chrono::time_point<std::chrono::system_clock> startTime = std::chrono::system_clock::now();
|
||||
|
||||
if (!ownData->m_condVar.wait_for(lock, boost::chrono::microseconds(*pMicros),
|
||||
[this, ownData] { return m_isStop || ownData->state != SemState::waiting; })) {
|
||||
// timeout
|
||||
ret = getErr(ErrCode::_ETIMEDOUT);
|
||||
|
||||
// remove from list
|
||||
if (ownData->parent != nullptr) {
|
||||
ownData->parent->child = ownData->child;
|
||||
}
|
||||
if (ownData->child != nullptr) {
|
||||
ownData->child->parent = ownData->parent;
|
||||
}
|
||||
|
||||
LOG_TRACE(L"<- KernelSema(%llu) name:%S timeout| count:%d needs:%d index:%llu", m_id, m_name.c_str(), m_signalValue, needCount, ownData->index);
|
||||
|
||||
// Special: first in list -> notify next
|
||||
if (ownData->parent == nullptr) {
|
||||
m_curStart = ownData->child;
|
||||
if (m_curStart != nullptr && (m_isStop || m_curStart->needs <= m_signalValue)) {
|
||||
m_curStart->state = SemState::signaled;
|
||||
LOG_TRACE(L"KernelSema(%llu) name:%S timeout notify| count:%d index:%llu", m_id, m_name.c_str(), m_signalValue, m_curStart->index);
|
||||
|
||||
auto& cond = m_curStart->m_condVar;
|
||||
lock.unlock();
|
||||
cond.notify_one(); // race condition if m_curStart is used
|
||||
}
|
||||
}
|
||||
// - special
|
||||
|
||||
// Reset ownData
|
||||
ownData->child = nullptr;
|
||||
ownData->parent = nullptr;
|
||||
ownData->state = SemState::idle;
|
||||
|
||||
*pMicros = 0;
|
||||
|
||||
if (--m_numWaiters == 0 && m_isStop) {
|
||||
lock.unlock();
|
||||
m_condState.notify_one();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - startTime).count();
|
||||
|
||||
if (pMicros != nullptr) {
|
||||
*pMicros = (elapsed >= *pMicros ? 0 : *pMicros - elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_isStop || ownData->state == SemState::canceled) {
|
||||
ret = getErr(ErrCode::_ECANCELED);
|
||||
}
|
||||
|
||||
if (ret == Ok) {
|
||||
m_signalValue -= ownData->needs;
|
||||
}
|
||||
|
||||
// Set list start/end
|
||||
if (ownData->child == nullptr) {
|
||||
// Reached end -> reset back
|
||||
m_curBack = nullptr;
|
||||
m_countWaits = 0;
|
||||
}
|
||||
m_curStart = ownData->child;
|
||||
// -
|
||||
LOG_TRACE(L"<- KernelSema(%llu) name:%S wait| count:%d needs:%d index:%llu state:%d", m_id, m_name.c_str(), m_signalValue, needCount, ownData->index,
|
||||
ownData->state);
|
||||
|
||||
// Reset ownData
|
||||
ownData->child = nullptr;
|
||||
ownData->parent = nullptr;
|
||||
ownData->state = SemState::idle;
|
||||
// -
|
||||
|
||||
// notify next
|
||||
if (m_curStart != nullptr && (m_isStop || m_curStart->needs <= m_signalValue)) {
|
||||
m_curStart->state = SemState::signaled;
|
||||
LOG_TRACE(L"KernelSema(%llu) name:%S notify| count:%d index:%llu", m_id, m_name.c_str(), m_signalValue, m_curStart->index);
|
||||
|
||||
auto& cond = m_curStart->m_condVar;
|
||||
lock.unlock();
|
||||
cond.notify_one(); // race condition if m_curStart is used
|
||||
}
|
||||
//
|
||||
|
||||
if (--m_numWaiters == 0 && m_isStop) {
|
||||
lock.unlock();
|
||||
m_condState.notify_one();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Semaphore::wait(int needcount, uint32_t* pMicros) {
|
||||
boost::unique_lock lock(m_mutexInt);
|
||||
return wait_internal(needcount, pMicros, lock);
|
||||
}
|
||||
|
||||
int Semaphore::try_wait(int needcount, uint32_t* pMicros) {
|
||||
boost::unique_lock lock(m_mutexInt);
|
||||
|
||||
if (m_curBack != nullptr) {
|
||||
return getErr(ErrCode::_EBUSY);
|
||||
}
|
||||
|
||||
return wait_internal(needcount, pMicros, lock);
|
||||
}
|
||||
|
||||
int Semaphore::poll_internal(int needCount, boost::unique_lock<boost::mutex>& lock) {
|
||||
if (m_curBack == 0 && (needCount <= m_signalValue)) {
|
||||
m_signalValue -= needCount;
|
||||
return Ok;
|
||||
}
|
||||
|
||||
return getErr(ErrCode::_EAGAIN); // Waiters in queue, todo need enqueue?
|
||||
}
|
||||
|
||||
int Semaphore::poll(int needCount) {
|
||||
boost::unique_lock lock(m_mutexInt);
|
||||
return poll_internal(needCount, lock);
|
||||
}
|
@ -434,6 +434,10 @@ void* SYSV_ABI sceGnmGetTheTessellationFactorRingBufferBaseAddress() {
|
||||
return (void*)0xff0000000; // Maps/allocates it afterwards anyway
|
||||
}
|
||||
|
||||
int SYSV_ABI sceGnmValidateCommandBuffers() {
|
||||
return Err::VALIDATION_NOT_ENABLED;
|
||||
}
|
||||
|
||||
// #### Only used for tracing ########
|
||||
// #####################################
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include "boost/date_time/posix_time/posix_time_types.hpp"
|
||||
#include "boost/interprocess/sync/interprocess_semaphore.hpp"
|
||||
#include "common.h"
|
||||
#include "core/kernel/errors.h"
|
||||
#include "core/kernel/semaphore.h"
|
||||
@ -7,6 +9,7 @@
|
||||
|
||||
#include <boost/chrono.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
LOG_DEFINE_MODULE(libScePosix);
|
||||
|
||||
using ScePthreadSem_t = ISemaphore*;
|
||||
@ -15,12 +18,12 @@ extern "C" {
|
||||
|
||||
EXPORT const char* MODULE_NAME = "libkernel";
|
||||
|
||||
EXPORT SYSV_ABI int __NID(sem_init)(ScePthreadSem_t* sem, int pshared, unsigned int value) {
|
||||
(*sem) = createSemaphore(nullptr, false, 0, std::numeric_limits<int>::max()).release();
|
||||
EXPORT SYSV_ABI int __NID(sem_init)(boost::interprocess::interprocess_semaphore** sem, int pshared, unsigned int value) {
|
||||
*sem = new boost::interprocess::interprocess_semaphore(value);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
EXPORT SYSV_ABI int __NID(sem_destroy)(ScePthreadSem_t* sem) {
|
||||
EXPORT SYSV_ABI int __NID(sem_destroy)(boost::interprocess::interprocess_semaphore** sem) {
|
||||
if (sem == nullptr) {
|
||||
return POSIX_SET(ErrCode::_ESRCH);
|
||||
}
|
||||
@ -29,27 +32,29 @@ EXPORT SYSV_ABI int __NID(sem_destroy)(ScePthreadSem_t* sem) {
|
||||
return Ok;
|
||||
}
|
||||
|
||||
EXPORT SYSV_ABI int __NID(sem_post)(ScePthreadSem_t* sem) {
|
||||
EXPORT SYSV_ABI int __NID(sem_post)(boost::interprocess::interprocess_semaphore** sem) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
return POSIX_SET(ErrCode::_ESRCH);
|
||||
}
|
||||
return POSIX_CALL((*sem)->signal(1));
|
||||
(*sem)->post();
|
||||
return Ok;
|
||||
}
|
||||
|
||||
// EXPORT SYSV_ABI int sem_reltimedwait_np(ScePthreadSem_t*sem, useconds_t);
|
||||
EXPORT SYSV_ABI int __NID(sem_trywait)(ScePthreadSem_t* sem) {
|
||||
EXPORT SYSV_ABI int __NID(sem_trywait)(boost::interprocess::interprocess_semaphore** sem) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
return POSIX_SET(ErrCode::_ESRCH);
|
||||
}
|
||||
return POSIX_CALL((*sem)->try_wait(1, nullptr));
|
||||
return (*sem)->try_wait() ? Ok : POSIX_SET(ErrCode::_EBUSY);
|
||||
}
|
||||
|
||||
// EXPORT SYSV_ABI int sem_unlink(const char* semName){}
|
||||
EXPORT SYSV_ABI int __NID(sem_wait)(ScePthreadSem_t* sem) {
|
||||
EXPORT SYSV_ABI int __NID(sem_wait)(boost::interprocess::interprocess_semaphore** sem) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
return POSIX_SET(ErrCode::_ESRCH);
|
||||
}
|
||||
return POSIX_CALL((*sem)->wait(1, nullptr));
|
||||
(*sem)->wait();
|
||||
return Ok;
|
||||
}
|
||||
|
||||
EXPORT SYSV_ABI unsigned int __NID(sleep)(unsigned int seconds) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "core/kernel/semaphore.h"
|
||||
|
||||
#include "boost/date_time/posix_time/posix_time_types.hpp"
|
||||
#include "boost/interprocess/sync/interprocess_semaphore.hpp"
|
||||
#include "common.h"
|
||||
#include "core/kernel/errors.h"
|
||||
#include "logging.h"
|
||||
@ -7,8 +9,8 @@
|
||||
|
||||
using ScePthreadSem_t = ISemaphore*;
|
||||
extern "C" {
|
||||
EXPORT SYSV_ABI int scePthreadSemInit(ScePthreadSem_t* sem, int pshared, unsigned int initCount, const char* name) {
|
||||
(*sem) = createSemaphore(name, false, 0, std::numeric_limits<int>::max()).release();
|
||||
EXPORT SYSV_ABI int scePthreadSemInit(boost::interprocess::interprocess_semaphore** sem, int pshared, unsigned int initCount, const char* name) {
|
||||
*sem = new boost::interprocess::interprocess_semaphore(initCount);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
@ -21,44 +23,47 @@ EXPORT SYSV_ABI int scePthreadSemDestroy(ScePthreadSem_t* sem) {
|
||||
return Ok;
|
||||
}
|
||||
|
||||
EXPORT SYSV_ABI int scePthreadSemTimedwait(ScePthreadSem_t* sem, SceKernelUseconds usec) {
|
||||
EXPORT SYSV_ABI int scePthreadSemTimedwait(boost::interprocess::interprocess_semaphore** sem, SceKernelUseconds usec) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
return POSIX_SET(ErrCode::_ESRCH);
|
||||
}
|
||||
|
||||
return POSIX_CALL((*sem)->wait(1, &usec));
|
||||
auto deadline = boost::posix_time::microsec_clock::universal_time() + boost::posix_time::microseconds(usec);
|
||||
return (*sem)->timed_wait(deadline) ? Ok : POSIX_SET(ErrCode::_ETIMEDOUT);
|
||||
}
|
||||
|
||||
EXPORT SYSV_ABI int scePthreadSemPost(ScePthreadSem_t* sem) {
|
||||
EXPORT SYSV_ABI int scePthreadSemPost(boost::interprocess::interprocess_semaphore** sem) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
return POSIX_SET(ErrCode::_ESRCH);
|
||||
}
|
||||
return POSIX_CALL((*sem)->signal(1));
|
||||
(*sem)->post();
|
||||
return Ok;
|
||||
}
|
||||
|
||||
EXPORT SYSV_ABI int scePthreadSemTrywait(ScePthreadSem_t* sem) {
|
||||
EXPORT SYSV_ABI int scePthreadSemTrywait(boost::interprocess::interprocess_semaphore** sem) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
return POSIX_SET(ErrCode::_ESRCH);
|
||||
}
|
||||
return POSIX_CALL((*sem)->try_wait(1, nullptr));
|
||||
|
||||
return (*sem)->try_wait() ? Ok : POSIX_SET(ErrCode::_EBUSY);
|
||||
}
|
||||
|
||||
EXPORT SYSV_ABI int scePthreadSemWait(ScePthreadSem_t* sem) {
|
||||
EXPORT SYSV_ABI int scePthreadSemWait(boost::interprocess::interprocess_semaphore** sem) {
|
||||
if (sem == nullptr || *sem == nullptr) {
|
||||
return POSIX_SET(ErrCode::_ESRCH);
|
||||
}
|
||||
return POSIX_CALL((*sem)->wait(1, nullptr));
|
||||
|
||||
(*sem)->wait();
|
||||
return Ok;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT SYSV_ABI int sceKernelCreateSema(ScePthreadSem_t* sem, const char* name, uint32_t attr, int init, int max, const void* opt) {
|
||||
if (name == nullptr || init < 0 || init > max) {
|
||||
if (sem == nullptr) {
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
|
||||
bool fifo = attr == 1 ? true : false;
|
||||
|
||||
(*sem) = createSemaphore(name, fifo, init, max).release();
|
||||
(*sem) = createSemaphore_fifo(name, init, max).release();
|
||||
return Ok;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
enable_testing()
|
||||
|
||||
add_compile_definitions(
|
||||
BOOST_ALL_NO_LIB
|
||||
)
|
||||
|
||||
link_libraries(gtest_main gmock gmock_main)
|
||||
add_link_options(/DEBUG)
|
||||
link_directories(
|
||||
@ -13,9 +17,8 @@ include_directories(
|
||||
${CMAKE_BINARY_DIR}/third_party/install/include
|
||||
|
||||
${Vulkan_INCLUDE_DIRS}
|
||||
${PRJ_SRC_DIR}/public_repo
|
||||
${PRJ_SRC_DIR}/public_repo/third_party
|
||||
${PRJ_SRC_DIR}/public_repo/tools/logging
|
||||
${PRJ_SRC_DIR}/third_party
|
||||
${PRJ_SRC_DIR}/tools/logging
|
||||
)
|
||||
|
||||
add_subdirectory(core)
|
@ -1,17 +1 @@
|
||||
enable_testing()
|
||||
|
||||
add_executable(core_test
|
||||
semaphore/entry.cpp
|
||||
)
|
||||
|
||||
add_test(NAME core_test COMMAND core_test)
|
||||
|
||||
add_dependencies(core_test third_party psOff_utility logging_stub)
|
||||
|
||||
set_target_properties(core_test
|
||||
PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/."
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/."
|
||||
PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/."
|
||||
)
|
||||
add_subdirectory(semaphore)
|
25
tests/core/semaphore/CMakeLists.txt
Normal file
25
tests/core/semaphore/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
||||
enable_testing()
|
||||
|
||||
add_executable(semaphore_test
|
||||
entry.cpp
|
||||
|
||||
${PRJ_SRC_DIR}/core/kernel/semaphore_fifo.cpp
|
||||
)
|
||||
|
||||
add_test(NAME semaphore_test COMMAND semaphore_test)
|
||||
|
||||
add_dependencies(semaphore_test third_party psOff_utility logging_stub boost)
|
||||
|
||||
target_link_libraries(semaphore_test PRIVATE
|
||||
logging_stub.lib
|
||||
libboost_thread
|
||||
libboost_chrono
|
||||
)
|
||||
|
||||
set_target_properties(semaphore_test
|
||||
PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/."
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/."
|
||||
PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/."
|
||||
)
|
@ -1,4 +1,275 @@
|
||||
|
||||
#include "core/kernel/semaphore.h"
|
||||
#include "modules_include/common.h"
|
||||
|
||||
#include <fff/fff.h>
|
||||
#include <future>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(core_semaphore, init) {}
|
||||
DEFINE_FFF_GLOBALS;
|
||||
|
||||
namespace pthread {
|
||||
FAKE_VALUE_FUNC(int, getThreadId);
|
||||
}
|
||||
|
||||
TEST(core_semaphore, init) {
|
||||
RESET_FAKE(pthread::getThreadId);
|
||||
pthread::getThreadId_fake.return_val = 4;
|
||||
|
||||
constexpr int initCount = 1;
|
||||
constexpr int maxCount = 10;
|
||||
|
||||
{
|
||||
auto sem = createSemaphore_fifo("test", initCount, maxCount);
|
||||
EXPECT_TRUE(sem);
|
||||
|
||||
EXPECT_EQ(sem->getSignalCounter(), initCount);
|
||||
}
|
||||
|
||||
{
|
||||
auto sem = createSemaphore_fifo(nullptr, initCount, maxCount);
|
||||
EXPECT_TRUE(sem);
|
||||
|
||||
EXPECT_EQ(sem->getSignalCounter(), initCount);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(core_semaphore, polling_1) {
|
||||
RESET_FAKE(pthread::getThreadId);
|
||||
pthread::getThreadId_fake.return_val = 4;
|
||||
|
||||
constexpr int initCount = 1;
|
||||
constexpr int maxCount = 10;
|
||||
|
||||
auto sem = createSemaphore_fifo("test", initCount, maxCount);
|
||||
|
||||
auto fut = std::async(std::launch::async, [&sem] {
|
||||
{
|
||||
auto res = sem->poll(2);
|
||||
EXPECT_EQ(res, getErr(ErrCode::_EAGAIN));
|
||||
}
|
||||
{
|
||||
auto res = sem->poll(1);
|
||||
EXPECT_EQ(res, Ok);
|
||||
|
||||
EXPECT_EQ(sem->getSignalCounter(), 0);
|
||||
}
|
||||
{
|
||||
auto res = sem->poll(2);
|
||||
EXPECT_EQ(res, getErr(ErrCode::_EAGAIN));
|
||||
}
|
||||
{
|
||||
auto resSignal = sem->signal(2);
|
||||
EXPECT_EQ(resSignal, Ok);
|
||||
|
||||
auto res = sem->poll(2);
|
||||
EXPECT_EQ(res, Ok);
|
||||
|
||||
EXPECT_EQ(sem->getSignalCounter(), 0);
|
||||
}
|
||||
|
||||
{
|
||||
auto resSignal = sem->signal(4);
|
||||
EXPECT_EQ(resSignal, Ok);
|
||||
|
||||
auto res1 = sem->poll(2);
|
||||
EXPECT_EQ(res1, Ok);
|
||||
EXPECT_EQ(sem->getSignalCounter(), 2);
|
||||
|
||||
auto res2 = sem->poll(2);
|
||||
EXPECT_EQ(res2, Ok);
|
||||
EXPECT_EQ(sem->getSignalCounter(), 0);
|
||||
}
|
||||
});
|
||||
|
||||
auto res = fut.wait_for(std::chrono::milliseconds(10));
|
||||
EXPECT_NE(res, std::future_status::timeout);
|
||||
}
|
||||
|
||||
TEST(core_semaphore, signal_1) {
|
||||
RESET_FAKE(pthread::getThreadId);
|
||||
pthread::getThreadId_fake.return_val = 4;
|
||||
|
||||
constexpr int initCount = 1;
|
||||
constexpr int maxCount = 10;
|
||||
|
||||
auto sem = createSemaphore_fifo("test", initCount, maxCount);
|
||||
|
||||
{ // Check wait instant release
|
||||
auto resSignal = sem->signal(1);
|
||||
EXPECT_EQ(resSignal, Ok);
|
||||
EXPECT_EQ(sem->getSignalCounter(), 2);
|
||||
|
||||
auto fut = std::async(std::launch::async, [&sem] {
|
||||
{
|
||||
auto res = sem->wait(2, nullptr); // wait forever
|
||||
EXPECT_EQ(res, Ok);
|
||||
}
|
||||
});
|
||||
|
||||
auto res = fut.wait_for(std::chrono::milliseconds(10));
|
||||
EXPECT_NE(res, std::future_status::timeout);
|
||||
}
|
||||
|
||||
EXPECT_EQ(sem->getSignalCounter(), 0);
|
||||
|
||||
{ // Check timeout
|
||||
auto resSignal = sem->signal(1);
|
||||
EXPECT_EQ(resSignal, Ok);
|
||||
EXPECT_EQ(sem->getSignalCounter(), 1);
|
||||
|
||||
auto fut = std::async(std::launch::async, [&sem] {
|
||||
{
|
||||
uint32_t micros = 1;
|
||||
auto res = sem->wait(2, µs); // wait timeout
|
||||
EXPECT_EQ(res, getErr(ErrCode::_ETIMEDOUT));
|
||||
}
|
||||
});
|
||||
|
||||
auto res = fut.wait_for(std::chrono::milliseconds(10));
|
||||
EXPECT_NE(res, std::future_status::timeout);
|
||||
}
|
||||
EXPECT_EQ(sem->getSignalCounter(), 1);
|
||||
|
||||
{ // Check wait signal afterwards
|
||||
auto fut = std::async(std::launch::async, [&sem] {
|
||||
{
|
||||
auto res = sem->wait(2, nullptr); // wait forever
|
||||
EXPECT_EQ(res, Ok);
|
||||
}
|
||||
});
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(100)); // this or mock condition var
|
||||
|
||||
auto resSignal = sem->signal(1);
|
||||
EXPECT_EQ(resSignal, Ok);
|
||||
EXPECT_EQ(sem->getSignalCounter(), 2);
|
||||
|
||||
auto res = fut.wait_for(std::chrono::milliseconds(10));
|
||||
EXPECT_NE(res, std::future_status::timeout);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(core_semaphore, signal_2) {
|
||||
RESET_FAKE(pthread::getThreadId);
|
||||
|
||||
pthread::getThreadId_fake.custom_fake = []() {
|
||||
static int counter = 0;
|
||||
return ++counter;
|
||||
};
|
||||
|
||||
constexpr int initCount = 0;
|
||||
constexpr int maxCount = 10;
|
||||
|
||||
auto sem = createSemaphore_fifo("test", initCount, maxCount);
|
||||
EXPECT_EQ(sem->getSignalCounter(), 0);
|
||||
|
||||
size_t countItems = 0;
|
||||
{ // Check wait signal afterwards (sequenze release)
|
||||
auto fut1 = std::async(std::launch::async, [&sem, &countItems] {
|
||||
{
|
||||
auto res = sem->wait(2, nullptr); // wait forever
|
||||
++countItems;
|
||||
EXPECT_EQ(res, Ok);
|
||||
}
|
||||
});
|
||||
auto fut2 = std::async(std::launch::async, [&sem, &countItems] {
|
||||
{
|
||||
auto res = sem->wait(2, nullptr); // wait forever
|
||||
++countItems;
|
||||
EXPECT_EQ(res, Ok);
|
||||
}
|
||||
});
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(100)); // this or mock condition var
|
||||
|
||||
{
|
||||
auto resSignal = sem->signal(2);
|
||||
EXPECT_EQ(resSignal, Ok);
|
||||
}
|
||||
|
||||
auto res1 = fut1.wait_for(std::chrono::microseconds(100));
|
||||
EXPECT_NE(res1, std::future_status::timeout);
|
||||
EXPECT_EQ(sem->getSignalCounter(), 0);
|
||||
EXPECT_EQ(countItems, 1);
|
||||
|
||||
{
|
||||
auto resSignal = sem->signal(2);
|
||||
EXPECT_EQ(resSignal, Ok);
|
||||
}
|
||||
|
||||
auto res2 = fut2.wait_for(std::chrono::microseconds(100));
|
||||
EXPECT_NE(res2, std::future_status::timeout);
|
||||
EXPECT_EQ(countItems, 2);
|
||||
}
|
||||
|
||||
{ // Check wait signal afterwards (direct release)
|
||||
auto fut1 = std::async(std::launch::async, [&sem] {
|
||||
{
|
||||
auto res = sem->wait(2, nullptr); // wait forever
|
||||
EXPECT_EQ(res, Ok);
|
||||
}
|
||||
});
|
||||
auto fut2 = std::async(std::launch::async, [&sem] {
|
||||
{
|
||||
auto res = sem->wait(2, nullptr); // wait forever
|
||||
EXPECT_EQ(res, Ok);
|
||||
}
|
||||
});
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(100)); // this or mock condition var
|
||||
|
||||
{
|
||||
auto resSignal = sem->signal(4);
|
||||
EXPECT_EQ(resSignal, Ok);
|
||||
}
|
||||
|
||||
auto res1 = fut1.wait_for(std::chrono::microseconds(100));
|
||||
EXPECT_NE(res1, std::future_status::timeout);
|
||||
|
||||
auto res2 = fut2.wait_for(std::chrono::microseconds(100));
|
||||
EXPECT_NE(res2, std::future_status::timeout);
|
||||
EXPECT_EQ(sem->getSignalCounter(), 0);
|
||||
}
|
||||
|
||||
sem.reset();
|
||||
}
|
||||
|
||||
TEST(core_semaphore, signal_exit) {
|
||||
RESET_FAKE(pthread::getThreadId);
|
||||
|
||||
pthread::getThreadId_fake.custom_fake = []() {
|
||||
static int counter = 0;
|
||||
return ++counter;
|
||||
};
|
||||
|
||||
constexpr int initCount = 0;
|
||||
constexpr int maxCount = 10;
|
||||
|
||||
auto sem = createSemaphore_fifo("test", initCount, maxCount);
|
||||
EXPECT_EQ(sem->getSignalCounter(), 0);
|
||||
|
||||
{ // Check wait signal afterwards and exit release
|
||||
auto fut1 = std::async(std::launch::async, [&sem] {
|
||||
{
|
||||
auto res = sem->wait(2, nullptr); // wait forever
|
||||
EXPECT_EQ(res, Ok);
|
||||
}
|
||||
});
|
||||
auto fut2 = std::async(std::launch::async, [&sem] {
|
||||
{
|
||||
auto res = sem->wait(2, nullptr); // wait forever
|
||||
EXPECT_EQ(res, getErr(ErrCode::_ECANCELED));
|
||||
}
|
||||
});
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(100)); // this or mock condition var
|
||||
|
||||
auto resSignal = sem->signal(2);
|
||||
EXPECT_EQ(resSignal, Ok);
|
||||
|
||||
auto res1 = fut1.wait_for(std::chrono::microseconds(100));
|
||||
EXPECT_NE(res1, std::future_status::timeout);
|
||||
EXPECT_EQ(sem->getSignalCounter(), 0);
|
||||
|
||||
auto res2 = fut2.wait_for(std::chrono::microseconds(100));
|
||||
EXPECT_EQ(res2, std::future_status::timeout);
|
||||
sem.reset();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user