mirror of
https://github.com/SysRay/psOff_public.git
synced 2024-11-27 00:20:54 +00:00
fix semaphore
This commit is contained in:
parent
9e3dab85c3
commit
3007e81a1e
@ -4,7 +4,7 @@ add_library(kernel OBJECT
|
|||||||
errors.cpp
|
errors.cpp
|
||||||
filesystem.cpp
|
filesystem.cpp
|
||||||
pthread.cpp
|
pthread.cpp
|
||||||
semaphore.cpp
|
semaphore_fifo.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_dependencies(kernel third_party boost)
|
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:
|
public:
|
||||||
virtual ~ISemaphore() = default;
|
virtual ~ISemaphore() = default;
|
||||||
|
|
||||||
virtual int cancel(int setCount, int* numWaitingThreads) = 0;
|
virtual int cancel(int setCount, int* numCanceled) = 0;
|
||||||
virtual int signal(int signalCount) = 0;
|
virtual int signal(int signalCount) = 0;
|
||||||
virtual int wait(int needcount, uint32_t* pMicros) = 0;
|
virtual int wait(int needcount, uint32_t* pMicros) = 0;
|
||||||
virtual int try_wait(int needcount, uint32_t* pMicros) = 0;
|
virtual int try_wait(int needcount, uint32_t* pMicros) = 0;
|
||||||
|
virtual int poll(int needCount) = 0;
|
||||||
int poll(int needCount) {
|
|
||||||
uint32_t micros = 0;
|
|
||||||
return wait(needCount, µs);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual std::string_view const getName() const = 0;
|
virtual std::string_view const getName() const = 0;
|
||||||
|
|
||||||
virtual size_t getId() const = 0;
|
virtual size_t getId() const = 0;
|
||||||
|
|
||||||
|
virtual size_t getSignalCounter() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(__APICALL_EXTERN)
|
#if defined(__APICALL_EXTERN)
|
||||||
@ -36,6 +34,7 @@ class ISemaphore {
|
|||||||
#define __APICALL
|
#define __APICALL
|
||||||
#endif
|
#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
|
#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
|
return (void*)0xff0000000; // Maps/allocates it afterwards anyway
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SYSV_ABI sceGnmValidateCommandBuffers() {
|
||||||
|
return Err::VALIDATION_NOT_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
// #### Only used for tracing ########
|
// #### 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 "common.h"
|
||||||
#include "core/kernel/errors.h"
|
#include "core/kernel/errors.h"
|
||||||
#include "core/kernel/semaphore.h"
|
#include "core/kernel/semaphore.h"
|
||||||
@ -7,6 +9,7 @@
|
|||||||
|
|
||||||
#include <boost/chrono.hpp>
|
#include <boost/chrono.hpp>
|
||||||
#include <boost/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
|
|
||||||
LOG_DEFINE_MODULE(libScePosix);
|
LOG_DEFINE_MODULE(libScePosix);
|
||||||
|
|
||||||
using ScePthreadSem_t = ISemaphore*;
|
using ScePthreadSem_t = ISemaphore*;
|
||||||
@ -15,12 +18,12 @@ extern "C" {
|
|||||||
|
|
||||||
EXPORT const char* MODULE_NAME = "libkernel";
|
EXPORT const char* MODULE_NAME = "libkernel";
|
||||||
|
|
||||||
EXPORT SYSV_ABI int __NID(sem_init)(ScePthreadSem_t* sem, int pshared, unsigned int value) {
|
EXPORT SYSV_ABI int __NID(sem_init)(boost::interprocess::interprocess_semaphore** sem, int pshared, unsigned int value) {
|
||||||
(*sem) = createSemaphore(nullptr, false, 0, std::numeric_limits<int>::max()).release();
|
*sem = new boost::interprocess::interprocess_semaphore(value);
|
||||||
return Ok;
|
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) {
|
if (sem == nullptr) {
|
||||||
return POSIX_SET(ErrCode::_ESRCH);
|
return POSIX_SET(ErrCode::_ESRCH);
|
||||||
}
|
}
|
||||||
@ -29,27 +32,29 @@ EXPORT SYSV_ABI int __NID(sem_destroy)(ScePthreadSem_t* sem) {
|
|||||||
return Ok;
|
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) {
|
if (sem == nullptr || *sem == nullptr) {
|
||||||
return POSIX_SET(ErrCode::_ESRCH);
|
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 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) {
|
if (sem == nullptr || *sem == nullptr) {
|
||||||
return POSIX_SET(ErrCode::_ESRCH);
|
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 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) {
|
if (sem == nullptr || *sem == nullptr) {
|
||||||
return POSIX_SET(ErrCode::_ESRCH);
|
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) {
|
EXPORT SYSV_ABI unsigned int __NID(sleep)(unsigned int seconds) {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "core/kernel/semaphore.h"
|
#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 "common.h"
|
||||||
#include "core/kernel/errors.h"
|
#include "core/kernel/errors.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
@ -7,8 +9,8 @@
|
|||||||
|
|
||||||
using ScePthreadSem_t = ISemaphore*;
|
using ScePthreadSem_t = ISemaphore*;
|
||||||
extern "C" {
|
extern "C" {
|
||||||
EXPORT SYSV_ABI int scePthreadSemInit(ScePthreadSem_t* sem, int pshared, unsigned int initCount, const char* name) {
|
EXPORT SYSV_ABI int scePthreadSemInit(boost::interprocess::interprocess_semaphore** sem, int pshared, unsigned int initCount, const char* name) {
|
||||||
(*sem) = createSemaphore(name, false, 0, std::numeric_limits<int>::max()).release();
|
*sem = new boost::interprocess::interprocess_semaphore(initCount);
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,44 +23,47 @@ EXPORT SYSV_ABI int scePthreadSemDestroy(ScePthreadSem_t* sem) {
|
|||||||
return Ok;
|
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) {
|
if (sem == nullptr || *sem == nullptr) {
|
||||||
return POSIX_SET(ErrCode::_ESRCH);
|
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) {
|
if (sem == nullptr || *sem == nullptr) {
|
||||||
return POSIX_SET(ErrCode::_ESRCH);
|
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) {
|
if (sem == nullptr || *sem == nullptr) {
|
||||||
return POSIX_SET(ErrCode::_ESRCH);
|
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) {
|
if (sem == nullptr || *sem == nullptr) {
|
||||||
return POSIX_SET(ErrCode::_ESRCH);
|
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) {
|
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);
|
return getErr(ErrCode::_EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fifo = attr == 1 ? true : false;
|
(*sem) = createSemaphore_fifo(name, init, max).release();
|
||||||
|
|
||||||
(*sem) = createSemaphore(name, fifo, init, max).release();
|
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
|
add_compile_definitions(
|
||||||
|
BOOST_ALL_NO_LIB
|
||||||
|
)
|
||||||
|
|
||||||
link_libraries(gtest_main gmock gmock_main)
|
link_libraries(gtest_main gmock gmock_main)
|
||||||
add_link_options(/DEBUG)
|
add_link_options(/DEBUG)
|
||||||
link_directories(
|
link_directories(
|
||||||
@ -13,9 +17,8 @@ include_directories(
|
|||||||
${CMAKE_BINARY_DIR}/third_party/install/include
|
${CMAKE_BINARY_DIR}/third_party/install/include
|
||||||
|
|
||||||
${Vulkan_INCLUDE_DIRS}
|
${Vulkan_INCLUDE_DIRS}
|
||||||
${PRJ_SRC_DIR}/public_repo
|
${PRJ_SRC_DIR}/third_party
|
||||||
${PRJ_SRC_DIR}/public_repo/third_party
|
${PRJ_SRC_DIR}/tools/logging
|
||||||
${PRJ_SRC_DIR}/public_repo/tools/logging
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
@ -1,17 +1 @@
|
|||||||
enable_testing()
|
add_subdirectory(semaphore)
|
||||||
|
|
||||||
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}/."
|
|
||||||
)
|
|
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>
|
#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