feature add Mutex,ConditionVariable,Event,MutexGuard
Some checks failed
rpcrypto-build / build (Debug, hisiv510.toolchain.cmake) (push) Successful in 52s
linux-x64-gcc / linux-gcc (push) Failing after 56s
linux-mips64-gcc / linux-gcc-mips64el (push) Failing after 57s
rpcrypto-build / build (Debug, himix200.toolchain.cmake) (push) Successful in 1m18s
rpcrypto-build / build (Debug, mipsel-openwrt-linux-musl.toolchain.cmake) (push) Failing after 1m17s
rpcrypto-build / build (Release, mipsel-openwrt-linux.toolchain.cmake) (push) Failing after 1m15s
rpcrypto-build / build (Release, himix200.toolchain.cmake) (push) Successful in 1m25s
linux-hisiv500-gcc / linux-gcc-hisiv500 (push) Successful in 1m23s
rpcrypto-build / build (Release, hisiv510.toolchain.cmake) (push) Successful in 1m29s
rpcrypto-build / build (Debug, mipsel-openwrt-linux.toolchain.cmake) (push) Failing after 1m3s
rpcrypto-build / build (Release, mipsel-openwrt-linux-musl.toolchain.cmake) (push) Failing after 1m21s

This commit is contained in:
tqcq 2023-12-13 12:56:40 +08:00
parent 2a8b1d873e
commit 91e96b17b2
15 changed files with 475 additions and 14 deletions

View File

@ -2,18 +2,18 @@ name: linux-x64-gcc
on:
push:
paths:
- '.gitea/workflows/linux-x64-gcc.yml'
- 'src/**'
- 'tests/**'
- 'CMakeLists.txt'
- 'cmake/**'
- ".gitea/workflows/linux-x64-gcc.yml"
- "src/**"
- "tests/**"
- "CMakeLists.txt"
- "cmake/**"
pull_request:
paths:
- '.gitea/workflows/linux-x64-gcc.yml'
- 'src/**'
- 'tests/**'
- 'CMakeLists.txt'
- 'cmake/**'
- ".gitea/workflows/linux-x64-gcc.yml"
- "src/**"
- "tests/**"
- "CMakeLists.txt"
- "cmake/**"
concurrency:
group: linux-x64-gcc-${{ github.ref }}
cancel-in-progress: true
@ -51,3 +51,4 @@ jobs:
- name: test-shared
run: |
cd build-shared && ctest --output-on-failure -j `nproc`

View File

@ -8,11 +8,21 @@ set(CMAKE_CXX_EXTENSIONS OFF)
option(ULIB_BUILD_TESTS "Build tests" OFF)
option(ULIB_SHARED_LIB "Build shared library" OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if (ULIB_SHARED_LIB)
add_library(${PROJECT_NAME} SHARED "")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
else()
add_library(${PROJECT_NAME} STATIC "")
add_library(${PROJECT_NAME} STATIC ""
src/ulib/concorrency/mutex.cpp
src/ulib/concorrency/mutex.h
src/ulib/concorrency/condition_variable.cpp
src/ulib/concorrency/condition_variable.h
src/ulib/concorrency/internal/mutex_impl.cpp
src/ulib/concorrency/internal/mutex_impl.h
src/ulib/concorrency/internal/condition_variable_impl.cpp
src/ulib/concorrency/internal/condition_variable_impl.h
src/ulib/concorrency/event.cpp
src/ulib/concorrency/event.h)
endif()
find_package(Threads REQUIRED)
@ -31,7 +41,6 @@ set(FMT_TEST OFF CACHE BOOL "Build tests" FORCE)
set(FMT_USE_CPP11 OFF CACHE BOOL "Use C++11" FORCE)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3party/fmt)
target_sources(${PROJECT_NAME} PRIVATE
src/ulib/empty.cpp
src/ulib/log/logger.cpp

View File

@ -0,0 +1,38 @@
//
// Created by Feng Zhang on 2023/12/12.
//
#include "condition_variable.h"
#include "internal/condition_variable_impl.h"
namespace ulib {
ConditionVariable::ConditionVariable() : impl_(new detail::ConditionVariableImpl) {}
ConditionVariable::~ConditionVariable() { delete impl_; }
void
ConditionVariable::NotifyOne()
{
impl_->NotifyOne();
}
void
ConditionVariable::NotifyAll()
{
impl_->NotifyAll();
}
void
ConditionVariable::Wait(MutexGuard &guard)
{
impl_->Wait(*guard.mutex_.impl_);
}
bool
ConditionVariable::WaitForMilliseconds(MutexGuard &guard, uint32_t wait_time)
{
return impl_->WaitForMilliseconds(*guard.mutex_.impl_, wait_time);
}
}

View File

@ -0,0 +1,47 @@
//
// Created by Feng Zhang on 2023/12/12.
//
#ifndef ULIB_SRC_ULIB_CONCORRENCY_CONDITION_VARIABLE_H_
#define ULIB_SRC_ULIB_CONCORRENCY_CONDITION_VARIABLE_H_
#include "mutex.h"
#include "ulib/base/types.h"
namespace ulib {
namespace detail {
class ConditionVariableImpl;
}
class ConditionVariable {
public:
ConditionVariable();
~ConditionVariable();
void NotifyOne();
void NotifyAll();
void Wait(MutexGuard &guard);
bool WaitForMilliseconds(MutexGuard &guard, uint32_t wait_time);
template<class Predicate>
void Wait(MutexGuard &guard, Predicate p)
{
while (!p()) { Wait(guard); }
}
template<class Predicate>
bool WaitForMilliseconds(MutexGuard &guard, uint32_t wait_time, Predicate p)
{
if (!p()) { WaitForMilliseconds(guard, wait_time); }
return p();
}
private:
detail::ConditionVariableImpl *impl_;
};
}// namespace ulib
#endif//ULIB_SRC_ULIB_CONCORRENCY_CONDITION_VARIABLE_H_

View File

@ -0,0 +1,64 @@
//
// Created by Feng Zhang on 2023/12/13.
//
#include "event.h"
ulib::Event::Event() : manual_reset_(false), event_status_(false) {}
ulib::Event::Event(bool manual_reset, bool initially_signaled)
: manual_reset_(manual_reset),
event_status_(initially_signaled)
{}
ulib::Event::~Event() {}
void
ulib::Event::Set()
{
MutexGuard guard(mutex_);
event_status_ = true;
cond_.NotifyAll();
}
void
ulib::Event::Reset()
{
MutexGuard guard(mutex_);
event_status_ = false;
}
bool
ulib::Event::Wait(int give_up_after_ms)
{
MutexGuard guard(mutex_);
if (event_status_) { return true; }
IsEventSetChecker checker(*this);
if (give_up_after_ms <= 0) {
cond_.Wait(guard, checker);
} else {
cond_.WaitForMilliseconds(guard, give_up_after_ms, checker);
}
if (event_status_) {
if (manual_reset_) { event_status_ = false; }
return true;
} else {
return false;
}
}
bool
ulib::Event::operator()() const
{
return event_status_;
}
ulib::Event::IsEventSetChecker::IsEventSetChecker(const ulib::Event &event) : event_(event) {}
bool
ulib::Event::IsEventSetChecker::operator()()
{
return event_.event_status_;
}

View File

@ -0,0 +1,50 @@
//
// Created by Feng Zhang on 2023/12/13.
//
#ifndef ULIB_SRC_ULIB_CONCORRENCY_EVENT_H_
#define ULIB_SRC_ULIB_CONCORRENCY_EVENT_H_
#include "mutex.h"
#include "condition_variable.h"
namespace ulib {
class Event {
public:
Event();
Event(bool manual_reset, bool initially_signaled);
~Event();
void Set();
void Reset();
/**
* @brief
* @param give_up_after_ms -1, always wait, > 0 set timeout
* @return
*/
bool Wait(int give_up_after_ms = -1);
bool operator()() const;
private:
class IsEventSetChecker {
public:
IsEventSetChecker(const Event &);
bool operator()();
private:
const Event &event_;
};
Event(const Event &);
Event &operator=(const Event &);
const bool manual_reset_;
Mutex mutex_;
ConditionVariable cond_;
bool event_status_;
friend class IsEventSetChecker;
};
}// namespace ulib
#endif//ULIB_SRC_ULIB_CONCORRENCY_EVENT_H_

View File

@ -0,0 +1,5 @@
//
// Created by Feng Zhang on 2023/12/12.
//
#include "condition_variable_impl.h"

View File

@ -0,0 +1,58 @@
//
// Created by Feng Zhang on 2023/12/12.
//
#ifndef ULIB_SRC_ULIB_CONCORRENCY_INTERNAL_CONDITION_VARIABLE_IMPL_H_
#define ULIB_SRC_ULIB_CONCORRENCY_INTERNAL_CONDITION_VARIABLE_IMPL_H_
#include "ulib/base/types.h"
#include "mutex_impl.h"
#include "ulib/concorrency/mutex.h"
#include <algorithm>
#include <pthread.h>
#include <errno.h>
namespace ulib {
namespace detail {
class ConditionVariableImpl {
public:
ConditionVariableImpl() { pthread_cond_init(&cond_, NULL); }
~ConditionVariableImpl() { pthread_cond_destroy(&cond_); }
void NotifyOne() { pthread_cond_signal(&cond_); }
void NotifyAll() { pthread_cond_broadcast(&cond_); }
void Wait(MutexImpl &mutex_impl) { pthread_cond_wait(&cond_, &mutex_impl.mutex_); }
template<class Predicate>
void Wait(MutexImpl &mutex_impl, Predicate p)
{
while (!p()) { Wait(mutex_impl); }
}
bool WaitForMilliseconds(MutexImpl &mutex_impl, uint32_t wait_time)
{
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = wait_time * 1000000;
return ETIMEDOUT != pthread_cond_timedwait(&cond_, &mutex_impl.mutex_, &ts);
}
template<class Predicate>
bool WaitForMilliseconds(MutexImpl &mutex_impl, uint32_t wait_time, Predicate p)
{
if (!p()) { WaitForMilliseconds(mutex_impl, wait_time); }
return p();
}
private:
pthread_cond_t cond_;
};
} // namespace detail
}// namespace ulib
#endif//ULIB_SRC_ULIB_CONCORRENCY_INTERNAL_CONDITION_VARIABLE_IMPL_H_

View File

@ -0,0 +1,5 @@
//
// Created by Feng Zhang on 2023/12/12.
//
#include "mutex_impl.h"

View File

@ -0,0 +1,31 @@
//
// Created by Feng Zhang on 2023/12/12.
//
#ifndef ULIB_SRC_ULIB_CONCORRENCY_INTERNAL_MUTEX_IMPL_H_
#define ULIB_SRC_ULIB_CONCORRENCY_INTERNAL_MUTEX_IMPL_H_
#include <pthread.h>
namespace ulib {
namespace detail {
class MutexImpl {
public:
MutexImpl() { pthread_mutex_init(&mutex_, NULL); }
~MutexImpl() { pthread_mutex_destroy(&mutex_); }
void Lock() { pthread_mutex_lock(&mutex_); }
void Unlock() { pthread_mutex_unlock(&mutex_); }
bool TryLock() { return pthread_mutex_trylock(&mutex_) == 0; }
private:
friend class ConditionVariableImpl;
pthread_mutex_t mutex_;
};
} // namespace detail
} // namespace ulib
#endif//ULIB_SRC_ULIB_CONCORRENCY_INTERNAL_MUTEX_IMPL_H_

View File

@ -0,0 +1,36 @@
//
// Created by Feng Zhang on 2023/12/12.
//
#include "mutex.h"
#include "internal/mutex_impl.h"
namespace ulib {
Mutex::Mutex() : impl_(new detail::MutexImpl) {}
Mutex::~Mutex() { delete impl_; }
void
Mutex::Lock()
{
impl_->Lock();
}
void
Mutex::Unlock()
{
impl_->Unlock();
}
bool
Mutex::TryLock()
{
return impl_->TryLock();
}
MutexGuard::MutexGuard(Mutex &mutex) : mutex_(mutex) { mutex_.Lock(); }
MutexGuard::~MutexGuard() { mutex_.Unlock(); }
}// namespace ulib

View File

@ -0,0 +1,43 @@
//
// Created by Feng Zhang on 2023/12/12.
//
#ifndef ULIB_SRC_ULIB_CONCORRENCY_MUTEX_H_
#define ULIB_SRC_ULIB_CONCORRENCY_MUTEX_H_
namespace ulib {
namespace detail {
class MutexImpl;
}
class Mutex {
public:
Mutex();
~Mutex();
void Lock();
void Unlock();
bool TryLock();
private:
Mutex(const Mutex &);
Mutex &operator=(const Mutex &);
friend class ConditionVariable;
detail::MutexImpl *impl_;
};
class MutexGuard {
public:
MutexGuard(Mutex& );
~MutexGuard();
private:
MutexGuard(const MutexGuard&);
MutexGuard& operator=(const MutexGuard&);
friend class ConditionVariable;
Mutex& mutex_;
};
} // namespace ulib
#endif//ULIB_SRC_ULIB_CONCORRENCY_MUTEX_H_

View File

@ -4,6 +4,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(ulib_test
ulib/base/types_test.cpp
ulib/log/log_test.cpp
ulib/concorrency/mutex_test.cpp
ulib/concorrency/event_test.cpp
)
target_link_libraries(ulib_test PRIVATE
ulib

View File

@ -0,0 +1,62 @@
#include <ulib/concorrency/condition_variable.h>
#include <ulib/concorrency/mutex.h>
#include <ulib/log/log.h>
#include <gtest/gtest.h>
#include <ulib/concorrency/event.h>
#include <pthread.h>
#include <vector>
class EventTest : public ::testing::Test {
public:
void SetUp() override
{
EXPECT_FALSE(event_());
EXPECT_FALSE(event_.Wait(100));
consumer_count_ = 0;
}
ulib::Event event_;
ulib::Mutex mutex_;
ulib::ConditionVariable cond_;
int32_t consumer_count_;
};
void *
Consumer(void *args)
{
EventTest *test = (EventTest *) args;
{
ulib::MutexGuard guard(test->mutex_);
++test->consumer_count_;
test->cond_.NotifyAll();
}
EXPECT_TRUE(test != NULL);
test->event_.Wait();
{
ulib::MutexGuard guard(test->mutex_);
--test->consumer_count_;
test->cond_.NotifyAll();
}
return NULL;
}
TEST_F(EventTest, multi_thread_wait_event)
{
std::vector<pthread_t> threads(10);
for (int i = 0; i < threads.size(); i++) { pthread_create(&threads[i], NULL, Consumer, this); }
ulib::MutexGuard guard(mutex_);
while (consumer_count_ < threads.size()) { cond_.Wait(guard); }
EXPECT_EQ(consumer_count_, threads.size());
{
event_.Set();
while (consumer_count_ > 0) { cond_.Wait(guard); }
}
EXPECT_EQ(consumer_count_, 0);
for (int i = 0; i < threads.size(); i++) { pthread_join(threads[i], NULL); }
}

View File

@ -0,0 +1,10 @@
#include <gtest/gtest.h>
#include <ulib/concorrency/mutex.h>
TEST(MutexTest, MutexTest)
{
ulib::Mutex mutex;
EXPECT_TRUE(mutex.TryLock());
EXPECT_FALSE(mutex.TryLock());
mutex.Unlock();
}