Added QNX support

This commit is contained in:
mutouyun 2022-01-02 17:54:07 +08:00
parent 51828c2f7b
commit 2e35ab7685
14 changed files with 684 additions and 249 deletions

View File

@ -1,13 +1,8 @@
project(ipc) project(ipc)
if(UNIX)
file(GLOB SRC_FILES ${LIBIPC_PROJECT_DIR}/src/libipc/platform/linux/*.cpp
${LIBIPC_PROJECT_DIR}/src/libipc/platform/linux/a0/*.c)
else()
file(GLOB SRC_FILES ${LIBIPC_PROJECT_DIR}/src/libipc/platform/win/*.cpp)
endif()
aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libipc SRC_FILES) aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libipc SRC_FILES)
aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libipc/sync SRC_FILES) aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libipc/sync SRC_FILES)
aux_source_directory(${LIBIPC_PROJECT_DIR}/src/libipc/platform SRC_FILES)
file(GLOB HEAD_FILES file(GLOB HEAD_FILES
${LIBIPC_PROJECT_DIR}/include/libipc/*.h ${LIBIPC_PROJECT_DIR}/include/libipc/*.h
@ -39,7 +34,7 @@ set_target_properties(${PROJECT_NAME}
# set version # set version
set_target_properties(${PROJECT_NAME} set_target_properties(${PROJECT_NAME}
PROPERTIES PROPERTIES
VERSION 1.2.0 VERSION 1.1.2
SOVERSION 3) SOVERSION 3)
target_include_directories(${PROJECT_NAME} target_include_directories(${PROJECT_NAME}
@ -49,7 +44,7 @@ target_include_directories(${PROJECT_NAME}
if(NOT MSVC) if(NOT MSVC)
target_link_libraries(${PROJECT_NAME} PUBLIC target_link_libraries(${PROJECT_NAME} PUBLIC
pthread $<$<NOT:$<STREQUAL:${CMAKE_SYSTEM_NAME},QNX>>:pthread>
$<$<NOT:$<STREQUAL:${CMAKE_SYSTEM_NAME},Windows>>:rt>) $<$<NOT:$<STREQUAL:${CMAKE_SYSTEM_NAME},Windows>>:rt>)
endif() endif()

View File

@ -1,4 +1,22 @@
#pragma once #ifndef LIBIPC_SRC_PLATFORM_DETAIL_H_
#define LIBIPC_SRC_PLATFORM_DETAIL_H_
// detect platform
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \
defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \
defined(WINCE) || defined(_WIN32_WCE)
# define IPC_OS_WINDOWS_
#elif defined(__linux__) || defined(__linux)
# define IPC_OS_LINUX_
#elif defined(__QNX__)
# define IPC_OS_QNX_
#elif defined(__APPLE__)
#elif defined(__ANDROID__)
// TBD
#endif
#if defined(__cplusplus)
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@ -22,18 +40,6 @@
# error "IPC_CONSTEXPR_ has been defined." # error "IPC_CONSTEXPR_ has been defined."
#endif #endif
// detect platform
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || \
defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \
defined(WINCE) || defined(_WIN32_WCE)
# define IPC_OS_WINDOWS_
#endif/*WIN*/
#if defined(__linux__) || defined(__linux)
# define IPC_OS_LINUX_
#endif/*linux*/
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
#define IPC_UNUSED_ [[maybe_unused]] #define IPC_UNUSED_ [[maybe_unused]]
@ -123,17 +129,8 @@ constexpr const T& (min)(const T& a, const T& b) {
#endif/*__cplusplus < 201703L*/ #endif/*__cplusplus < 201703L*/
template <typename T, typename U>
auto horrible_cast(U rhs) noexcept
-> typename std::enable_if<std::is_trivially_copyable<T>::value
&& std::is_trivially_copyable<U>::value, T>::type {
union {
T t;
U u;
} r = {};
r.u = rhs;
return r.t;
}
} // namespace detail } // namespace detail
} // namespace ipc } // namespace ipc
#endif // defined(__cplusplus)
#endif // LIBIPC_SRC_PLATFORM_DETAIL_H_

View File

@ -0,0 +1,13 @@
#include "libipc/platform/detail.h"
#if defined(IPC_OS_WINDOWS_)
#elif defined(IPC_OS_LINUX_)
#include "libipc/platform/linux/a0/err.c"
#include "libipc/platform/linux/a0/mtx.c"
#include "libipc/platform/linux/a0/strconv.c"
#include "libipc/platform/linux/a0/tid.c"
#include "libipc/platform/linux/a0/time.c"
#elif defined(IPC_OS_QNX_)
#else/*IPC_OS*/
# error "Unsupported platform."
#endif

View File

@ -0,0 +1,9 @@
#include "libipc/platform/detail.h"
#if defined(IPC_OS_WINDOWS_)
#include "libipc/platform/win/shm_win.cpp"
#elif defined(IPC_OS_LINUX_) || defined(IPC_OS_QNX_)
#include "libipc/platform/linux/shm_posix.cpp"
#else/*IPC_OS*/
# error "Unsupported platform."
#endif

View File

@ -0,0 +1,140 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <pthread.h>
#include "libipc/platform/get_wait_time.h"
#include "libipc/utility/log.h"
#include "libipc/utility/scope_guard.h"
#include "libipc/mutex.h"
#include "libipc/shm.h"
namespace ipc {
namespace detail {
namespace sync {
class condition {
ipc::shm::handle shm_;
pthread_cond_t *cond_ = nullptr;
pthread_cond_t *acquire_cond(char const *name) {
if (!shm_.acquire(name, sizeof(pthread_cond_t))) {
ipc::error("[acquire_cond] fail shm.acquire: %s\n", name);
return nullptr;
}
return static_cast<pthread_cond_t *>(shm_.get());
}
public:
condition() = default;
~condition() = default;
pthread_cond_t const *native() const noexcept {
return cond_;
}
pthread_cond_t *native() noexcept {
return cond_;
}
bool valid() const noexcept {
static const char tmp[sizeof(pthread_cond_t)] {};
return (cond_ != nullptr)
&& (std::memcmp(tmp, cond_, sizeof(pthread_cond_t)) != 0);
}
bool open(char const *name) noexcept {
close();
if ((cond_ = acquire_cond(name)) == nullptr) {
return false;
}
if (shm_.ref() > 1) {
return valid();
}
::pthread_cond_destroy(cond_);
auto finally = ipc::guard([this] { close(); }); // close when failed
// init condition
int eno;
pthread_condattr_t cond_attr;
if ((eno = ::pthread_condattr_init(&cond_attr)) != 0) {
ipc::error("fail pthread_condattr_init[%d]\n", eno);
return false;
}
IPC_UNUSED_ auto guard_cond_attr = unique_ptr(&cond_attr, ::pthread_condattr_destroy);
if ((eno = ::pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED)) != 0) {
ipc::error("fail pthread_condattr_setpshared[%d]\n", eno);
return false;
}
*cond_ = PTHREAD_COND_INITIALIZER;
if ((eno = ::pthread_cond_init(cond_, &cond_attr)) != 0) {
ipc::error("fail pthread_cond_init[%d]\n", eno);
return false;
}
finally.dismiss();
return valid();
}
void close() noexcept {
if ((shm_.ref() <= 1) && cond_ != nullptr) {
int eno;
if ((eno = ::pthread_cond_destroy(cond_)) != 0) {
ipc::error("fail pthread_cond_destroy[%d]\n", eno);
}
}
shm_.release();
cond_ = nullptr;
}
bool wait(ipc::sync::mutex &mtx, std::uint64_t tm) noexcept {
if (!valid()) return false;
switch (tm) {
case invalid_value: {
int eno;
if ((eno = ::pthread_cond_wait(cond_, static_cast<pthread_mutex_t *>(mtx.native()))) != 0) {
ipc::error("fail pthread_cond_wait[%d]\n", eno);
return false;
}
}
break;
default: {
auto ts = detail::make_timespec(tm);
int eno;
if ((eno = ::pthread_cond_timedwait(cond_, static_cast<pthread_mutex_t *>(mtx.native()), &ts)) != 0) {
if (eno != ETIMEDOUT) {
ipc::error("fail pthread_cond_timedwait[%d]: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
eno, tm, ts.tv_sec, ts.tv_nsec);
}
return false;
}
}
break;
}
return true;
}
bool notify() noexcept {
if (!valid()) return false;
int eno;
if ((eno = ::pthread_cond_signal(cond_)) != 0) {
ipc::error("fail pthread_cond_signal[%d]\n", eno);
return false;
}
return true;
}
bool broadcast() noexcept {
if (!valid()) return false;
int eno;
if ((eno = ::pthread_cond_broadcast(cond_)) != 0) {
ipc::error("fail pthread_cond_broadcast[%d]\n", eno);
return false;
}
return true;
}
};
} // namespace sync
} // namespace detail
} // namespace ipc

View File

@ -0,0 +1,39 @@
#pragma once
#include <cstdint>
#include <system_error>
#include <sys/time.h>
#include <time.h>
#include <errno.h>
#include "libipc/utility/log.h"
namespace ipc {
namespace detail {
inline bool calc_wait_time(timespec &ts, std::uint64_t tm /*ms*/) noexcept {
timeval now;
int eno = ::gettimeofday(&now, NULL);
if (eno != 0) {
ipc::error("fail gettimeofday [%d]\n", eno);
return false;
}
ts.tv_nsec = (now.tv_usec + (tm % 1000) * 1000) * 1000;
ts.tv_sec = now.tv_sec + (tm / 1000) + (ts.tv_nsec / 1000000000l);
ts.tv_nsec %= 1000000000l;
return true;
}
inline timespec make_timespec(std::uint64_t tm /*ms*/) noexcept(false) {
timespec ts {};
if (!calc_wait_time(ts, tm)) {
ipc::error("fail calc_wait_time: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
tm, ts.tv_sec, ts.tv_nsec);
throw std::system_error{static_cast<int>(errno), std::system_category()};
}
return ts;
}
} // namespace detail
} // namespace ipc

View File

@ -0,0 +1,236 @@
#pragma once
#include <cstring>
#include <cassert>
#include <cstdint>
#include <system_error>
#include <mutex>
#include <atomic>
#include <pthread.h>
#include "libipc/platform/get_wait_time.h"
#include "libipc/platform/detail.h"
#include "libipc/utility/log.h"
#include "libipc/utility/scope_guard.h"
#include "libipc/memory/resource.h"
#include "libipc/shm.h"
namespace ipc {
namespace detail {
namespace sync {
class mutex {
ipc::shm::handle *shm_ = nullptr;
std::atomic<std::int32_t> *ref_ = nullptr;
pthread_mutex_t *mutex_ = nullptr;
struct curr_prog {
struct shm_data {
ipc::shm::handle shm;
std::atomic<std::int32_t> ref;
struct init {
char const *name;
std::size_t size;
};
shm_data(init arg)
: shm{arg.name, arg.size}, ref{0} {}
};
ipc::map<ipc::string, shm_data> mutex_handles;
std::mutex lock;
static curr_prog &get() {
static curr_prog info;
return info;
}
};
pthread_mutex_t *acquire_mutex(char const *name) {
if (name == nullptr) {
return nullptr;
}
auto &info = curr_prog::get();
IPC_UNUSED_ std::lock_guard<std::mutex> guard {info.lock};
auto it = info.mutex_handles.find(name);
if (it == info.mutex_handles.end()) {
it = curr_prog::get().mutex_handles.emplace(name,
curr_prog::shm_data::init{name, sizeof(pthread_mutex_t)}).first;
}
shm_ = &it->second.shm;
ref_ = &it->second.ref;
if (shm_ == nullptr) {
return nullptr;
}
return static_cast<pthread_mutex_t *>(shm_->get());
}
template <typename F>
void release_mutex(ipc::string const &name, F &&clear) {
if (name.empty()) return;
IPC_UNUSED_ std::lock_guard<std::mutex> guard {curr_prog::get().lock};
auto it = curr_prog::get().mutex_handles.find(name);
if (it == curr_prog::get().mutex_handles.end()) {
return;
}
if (clear()) {
curr_prog::get().mutex_handles.erase(it);
}
}
public:
mutex() = default;
~mutex() = default;
pthread_mutex_t const *native() const noexcept {
return mutex_;
}
pthread_mutex_t *native() noexcept {
return mutex_;
}
bool valid() const noexcept {
static const char tmp[sizeof(pthread_mutex_t)] {};
return (shm_ != nullptr) && (ref_ != nullptr) && (mutex_ != nullptr)
&& (std::memcmp(tmp, mutex_, sizeof(pthread_mutex_t)) != 0);
}
bool open(char const *name) noexcept {
close();
if ((mutex_ = acquire_mutex(name)) == nullptr) {
return false;
}
auto self_ref = ref_->fetch_add(1, std::memory_order_relaxed);
if (shm_->ref() > 1 || self_ref > 0) {
return valid();
}
::pthread_mutex_destroy(mutex_);
auto finally = ipc::guard([this] { close(); }); // close when failed
// init mutex
int eno;
pthread_mutexattr_t mutex_attr;
if ((eno = ::pthread_mutexattr_init(&mutex_attr)) != 0) {
ipc::error("fail pthread_mutexattr_init[%d]\n", eno);
return false;
}
IPC_UNUSED_ auto guard_mutex_attr = unique_ptr(&mutex_attr, ::pthread_mutexattr_destroy);
if ((eno = ::pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED)) != 0) {
ipc::error("fail pthread_mutexattr_setpshared[%d]\n", eno);
return false;
}
if ((eno = ::pthread_mutexattr_setrobust(&mutex_attr, PTHREAD_MUTEX_ROBUST)) != 0) {
ipc::error("fail pthread_mutexattr_setrobust[%d]\n", eno);
return false;
}
*mutex_ = PTHREAD_MUTEX_INITIALIZER;
if ((eno = ::pthread_mutex_init(mutex_, &mutex_attr)) != 0) {
ipc::error("fail pthread_mutex_init[%d]\n", eno);
return false;
}
finally.dismiss();
return valid();
}
void close() noexcept {
if ((ref_ != nullptr) && (shm_ != nullptr) && (mutex_ != nullptr)) {
if (shm_->name() != nullptr) {
release_mutex(shm_->name(), [this] {
auto self_ref = ref_->fetch_sub(1, std::memory_order_relaxed);
if ((shm_->ref() <= 1) && (self_ref <= 1)) {
int eno;
if ((eno = ::pthread_mutex_destroy(mutex_)) != 0) {
ipc::error("fail pthread_mutex_destroy[%d]\n", eno);
}
return true;
}
return false;
});
} else shm_->release();
}
shm_ = nullptr;
ref_ = nullptr;
mutex_ = nullptr;
}
bool lock(std::uint64_t tm) noexcept {
if (!valid()) return false;
for (;;) {
auto ts = detail::make_timespec(tm);
int eno = (tm == invalid_value)
? ::pthread_mutex_lock(mutex_)
: ::pthread_mutex_timedlock(mutex_, &ts);
switch (eno) {
case 0:
return true;
case ETIMEDOUT:
return false;
case EOWNERDEAD: {
if (shm_->ref() > 1) {
shm_->sub_ref();
}
int eno2 = ::pthread_mutex_consistent(mutex_);
if (eno2 != 0) {
ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_consistent[%d]\n", eno, eno2);
return false;
}
int eno3 = ::pthread_mutex_unlock(mutex_);
if (eno3 != 0) {
ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_unlock[%d]\n", eno, eno3);
return false;
}
}
break; // loop again
default:
ipc::error("fail pthread_mutex_lock[%d]\n", eno);
return false;
}
}
}
bool try_lock() noexcept(false) {
if (!valid()) return false;
auto ts = detail::make_timespec(0);
int eno = ::pthread_mutex_timedlock(mutex_, &ts);
switch (eno) {
case 0:
return true;
case ETIMEDOUT:
return false;
case EOWNERDEAD: {
if (shm_->ref() > 1) {
shm_->sub_ref();
}
int eno2 = ::pthread_mutex_consistent(mutex_);
if (eno2 != 0) {
ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_consistent[%d]\n", eno, eno2);
break;
}
int eno3 = ::pthread_mutex_unlock(mutex_);
if (eno3 != 0) {
ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_unlock[%d]\n", eno, eno3);
break;
}
}
break;
default:
ipc::error("fail pthread_mutex_timedlock[%d]\n", eno);
break;
}
throw std::system_error{eno, std::system_category()};
}
bool unlock() noexcept {
if (!valid()) return false;
int eno;
if ((eno = ::pthread_mutex_unlock(mutex_)) != 0) {
ipc::error("fail pthread_mutex_unlock[%d]\n", eno);
return false;
}
return true;
}
};
} // namespace sync
} // namespace detail
} // namespace ipc

View File

@ -1,5 +1,4 @@
#include <sys/shm.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/types.h> #include <sys/types.h>

View File

@ -8,7 +8,9 @@
#include "libipc/platform/win/condition.h" #include "libipc/platform/win/condition.h"
#elif defined(IPC_OS_LINUX_) #elif defined(IPC_OS_LINUX_)
#include "libipc/platform/linux/condition.h" #include "libipc/platform/linux/condition.h"
#else/*linux*/ #elif defined(IPC_OS_QNX_)
#include "libipc/platform/posix/condition.h"
#else/*IPC_OS*/
# error "Unsupported platform." # error "Unsupported platform."
#endif #endif

View File

@ -8,7 +8,9 @@
#include "libipc/platform/win/mutex.h" #include "libipc/platform/win/mutex.h"
#elif defined(IPC_OS_LINUX_) #elif defined(IPC_OS_LINUX_)
#include "libipc/platform/linux/mutex.h" #include "libipc/platform/linux/mutex.h"
#else/*linux*/ #elif defined(IPC_OS_QNX_)
#include "libipc/platform/posix/mutex.h"
#else/*IPC_OS*/
# error "Unsupported platform." # error "Unsupported platform."
#endif #endif

View File

@ -6,9 +6,9 @@
#include "libipc/platform/detail.h" #include "libipc/platform/detail.h"
#if defined(IPC_OS_WINDOWS_) #if defined(IPC_OS_WINDOWS_)
#include "libipc/platform/win/semaphore.h" #include "libipc/platform/win/semaphore.h"
#elif defined(IPC_OS_LINUX_) #elif defined(IPC_OS_LINUX_) || defined(IPC_OS_QNX_)
#include "libipc/platform/linux/semaphore_impl.h" #include "libipc/platform/posix/semaphore_impl.h"
#else/*linux*/ #else/*IPC_OS*/
# error "Unsupported platform." # error "Unsupported platform."
#endif #endif

View File

@ -1,8 +1,9 @@
#pragma once #pragma once
#include <utility> // std::forward, std::integer_sequence #include <utility> // std::forward, std::integer_sequence
#include <cstddef> // std::size_t #include <cstddef> // std::size_t
#include <new> // std::hardware_destructive_interference_size #include <new> // std::hardware_destructive_interference_size
#include <type_traits> // std::is_trivially_copyable
#include "libipc/platform/detail.h" #include "libipc/platform/detail.h"
@ -44,13 +45,15 @@ enum {
}; };
template <typename T, typename U> template <typename T, typename U>
T horrible_cast(U val) { auto horrible_cast(U rhs) noexcept
-> typename std::enable_if<std::is_trivially_copyable<T>::value
&& std::is_trivially_copyable<U>::value, T>::type {
union { union {
T out; T t;
U in; U u;
} u; } r = {};
u.in = val; r.u = rhs;
return u.out; return r.t;
} }
IPC_CONSTEXPR_ std::size_t make_align(std::size_t align, std::size_t size) { IPC_CONSTEXPR_ std::size_t make_align(std::size_t align, std::size_t size) {

View File

@ -153,16 +153,16 @@ void test_sr(char const * name, int s_cnt, int r_cnt) {
TEST(IPC, basic) { TEST(IPC, basic) {
test_basic<relat::single, relat::single, trans::unicast >("ssu"); test_basic<relat::single, relat::single, trans::unicast >("ssu");
test_basic<relat::single, relat::multi , trans::unicast >("smu"); //test_basic<relat::single, relat::multi , trans::unicast >("smu");
test_basic<relat::multi , relat::multi , trans::unicast >("mmu"); //test_basic<relat::multi , relat::multi , trans::unicast >("mmu");
test_basic<relat::single, relat::multi , trans::broadcast>("smb"); test_basic<relat::single, relat::multi , trans::broadcast>("smb");
test_basic<relat::multi , relat::multi , trans::broadcast>("mmb"); test_basic<relat::multi , relat::multi , trans::broadcast>("mmb");
} }
TEST(IPC, 1v1) { TEST(IPC, 1v1) {
test_sr<relat::single, relat::single, trans::unicast >("ssu", 1, 1); test_sr<relat::single, relat::single, trans::unicast >("ssu", 1, 1);
test_sr<relat::single, relat::multi , trans::unicast >("smu", 1, 1); //test_sr<relat::single, relat::multi , trans::unicast >("smu", 1, 1);
test_sr<relat::multi , relat::multi , trans::unicast >("mmu", 1, 1); //test_sr<relat::multi , relat::multi , trans::unicast >("mmu", 1, 1);
test_sr<relat::single, relat::multi , trans::broadcast>("smb", 1, 1); test_sr<relat::single, relat::multi , trans::broadcast>("smb", 1, 1);
test_sr<relat::multi , relat::multi , trans::broadcast>("mmb", 1, 1); test_sr<relat::multi , relat::multi , trans::broadcast>("mmb", 1, 1);
} }