Skip to content

Commit

Permalink
Sem: replace sem_getvalue
Browse files Browse the repository at this point in the history
  • Loading branch information
fchn289 committed Jul 4, 2024
1 parent fbf752e commit 23a61bd
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 20 deletions.
18 changes: 9 additions & 9 deletions src/thread/MT_Semaphore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,17 @@ namespace RLib
// ***********************************************************************************************
void MT_Semaphore::mt_notify()
{
int count = 0;
sem_getvalue(&mt_sem_, &count); // impossible failed since MT_Semaphore's constructore
if (count > 0) // >0 to avoid count overflow; timeout is deadline
return;
sem_post(&mt_sem_); // impossible failed since MT_Semaphore's constructor
// - can't sem_getvalue() as NOT mt-safe
// - mt_notified_ is to avoid sem counter overflow; & not rouse main-thread repeatedly
if (!mt_notified_.test_and_set()) // memory_order_seq_cst to ensure other thread(s) see the flag
sem_post(&mt_sem_); // impossible failed since MT_Semaphore's constructor
}

// ***********************************************************************************************
void MT_Semaphore::timedwait(const size_t aSec, const size_t aRestNsec)
{
timespec ts{0, 0};
clock_gettime(CLOCK_REALTIME, &ts); // impossible failed since MT_Semaphore's constructor
clock_gettime(CLOCK_REALTIME, &ts);

const auto ns = ts.tv_nsec + aRestNsec; // usr's duty for reasonable aRestNsec; here's duty for no crash
ts.tv_sec += (aSec + ns / 1000'000'000);
Expand All @@ -35,10 +34,11 @@ void MT_Semaphore::timedwait(const size_t aSec, const size_t aRestNsec)
for (;;)
{
const auto ret = sem_timedwait(&mt_sem_, &ts);
if (ret == 0) // notified
return;
else if (errno == ETIMEDOUT)
if (errno == ETIMEDOUT || ret == 0) // timeout or notified -> wakeup to handle sth
{
mt_notified_.clear(); // memory_order_seq_cst to ensure other thread(s) see the flag
return;
}

// impossible since MT_Semaphore's constructor
// else if (errno == EINVAL) // avoid dead loop
Expand Down
35 changes: 27 additions & 8 deletions src/thread/MT_Semaphore.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
// ***********************************************************************************************
#pragma once

#include <atomic>
#include <semaphore.h>

using namespace std;
Expand All @@ -30,20 +31,30 @@ namespace RLib
class MT_Semaphore
{
public:
MT_Semaphore() { sem_init(&mt_sem_, 0, 0); } // 2nd para: intra-process; 3rd: init value
~MT_Semaphore() { sem_destroy(&mt_sem_); }
MT_Semaphore(const MT_Semaphore&) = delete; // then compiler will not auto-gen mv(), =() etc
MT_Semaphore() noexcept { sem_init(&mt_sem_, 0, 0); } // 2nd para: intra-process; 3rd: init value
~MT_Semaphore() noexcept { sem_destroy(&mt_sem_); }
MT_Semaphore(const MT_Semaphore&) = delete; // then compiler NOT auto-gen mv(), =() etc

void mt_notify();
void mt_notify() noexcept;

// - no mt_ prefix since main thread use only
// - no mt_ prefix since main-thread use ONLY
// - if more threads call it, not guarantee to wakeup all threads but only 1
// . so mt_timedwait() is more complex, will impl if real req appears
void timedwait(const size_t aSec = 0, const size_t aRestNsec = 100'000'000);
void timedwait(const size_t aSec = 0, const size_t aRestNsec = 100'000'000) noexcept;

// -------------------------------------------------------------------------------------------
private:
sem_t mt_sem_;
atomic_flag mt_notified_ = ATOMIC_FLAG_INIT; // = false

// -------------------------------------------------------------------------------------------
#ifdef RLIB_UT
public:
void reset() noexcept // not mt safe
{
sem_destroy(&mt_sem_);
sem_init(&mt_sem_, 0, 0);
mt_notified_.clear();
}
#endif
};

} // namespace
Expand All @@ -53,4 +64,12 @@ class MT_Semaphore
// 2023-09-20 CSZ 1)create
// 2023-09-21 CSZ - timer based on ThreadBack
// 2023-10-26 CSZ - timer based on sem_timedwait()
// 2024-07-04 CSZ - sem_getvalue() is not MT safe, replaced
// ***********************************************************************************************
// Q&A:
// - why not check sem_*() failure?
// . ENOMEM: sem exceeds max# - impossible normally/mostly
// . EINVAL: invalid para - impossible
// . EPERM : no right - impossible normally/mostly
// . ENOSYS: not support sem - impossible normally/mostly
// . EBUSY : re-init sem - impossible
11 changes: 8 additions & 3 deletions ut/thread/MT_SemaphoreTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
#include <unordered_map>

#include "MsgSelf.hpp"
#include "MT_PingMainTH.hpp"
#include "MtInQueue.hpp"
#include "ThreadBackViaMsgSelf.hpp"
#include "UniLog.hpp"

#define RLIB_UT
#include "MT_PingMainTH.hpp"
#include "MtInQueue.hpp"
#undef RLIB_UT

using namespace std::chrono;
using namespace testing;

Expand All @@ -27,7 +30,9 @@ struct MT_SemaphoreTest : public Test, public UniLog
{
MT_SemaphoreTest()
: UniLog(UnitTest::GetInstance()->current_test_info()->name())
{}
{
g_sem.reset(); // clear prev test's counter
}
void SetUp() override
{
mt_getQ().mt_clear(); // avoid other case interfere
Expand Down

0 comments on commit 23a61bd

Please sign in to comment.