mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-07 01:06:45 +08:00
This commit addresses the test failures reported in issue #156 on FreeBSD platform. 1. Fix POSIX semaphore naming for FreeBSD compatibility - FreeBSD requires semaphore names to start with "/" - Add "_sem" suffix to avoid namespace conflicts with shm - Store semaphore name separately for proper cleanup - This fixes all 24 semaphore test failures 2. Fix robust mutex EOWNERDEAD handling - When pthread_mutex_lock returns EOWNERDEAD, the lock is already acquired by the calling thread - After calling pthread_mutex_consistent(), we should return success immediately, not unlock and retry - Previous behavior caused issues with FreeBSD's libthr robust mutex list management, leading to fatal errors - This fixes the random crashes in MutexTest Technical details: - EOWNERDEAD indicates the previous lock owner died while holding the lock, but the current thread has successfully acquired it - pthread_mutex_consistent() restores the mutex to a consistent state - The Linux implementation worked differently, but the new approach is more correct according to POSIX semantics and works on both Linux and FreeBSD Fixes #156
134 lines
3.8 KiB
C++
134 lines
3.8 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
|
|
#include <fcntl.h> /* For O_* constants */
|
|
#include <sys/stat.h> /* For mode constants */
|
|
#include <semaphore.h>
|
|
#include <errno.h>
|
|
|
|
#include "libipc/utility/log.h"
|
|
#include "libipc/shm.h"
|
|
|
|
#include "get_wait_time.h"
|
|
|
|
namespace ipc {
|
|
namespace detail {
|
|
namespace sync {
|
|
|
|
class semaphore {
|
|
ipc::shm::handle shm_;
|
|
sem_t *h_ = SEM_FAILED;
|
|
std::string sem_name_; // Store the actual semaphore name used
|
|
|
|
public:
|
|
semaphore() = default;
|
|
~semaphore() noexcept = default;
|
|
|
|
sem_t *native() const noexcept {
|
|
return h_;
|
|
}
|
|
|
|
bool valid() const noexcept {
|
|
return h_ != SEM_FAILED;
|
|
}
|
|
|
|
bool open(char const *name, std::uint32_t count) noexcept {
|
|
close();
|
|
if (!shm_.acquire(name, 1)) {
|
|
ipc::error("[open_semaphore] fail shm.acquire: %s\n", name);
|
|
return false;
|
|
}
|
|
// POSIX semaphore names must start with "/" on some platforms (e.g., FreeBSD)
|
|
// Use a separate namespace for semaphores to avoid conflicts with shm
|
|
if (name[0] == '/') {
|
|
sem_name_ = std::string(name) + "_sem";
|
|
} else {
|
|
sem_name_ = std::string("/") + name + "_sem";
|
|
}
|
|
h_ = ::sem_open(sem_name_.c_str(), O_CREAT, 0666, static_cast<unsigned>(count));
|
|
if (h_ == SEM_FAILED) {
|
|
ipc::error("fail sem_open[%d]: %s\n", errno, sem_name_.c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void close() noexcept {
|
|
if (!valid()) return;
|
|
if (::sem_close(h_) != 0) {
|
|
ipc::error("fail sem_close[%d]: %s\n", errno);
|
|
}
|
|
h_ = SEM_FAILED;
|
|
if (!sem_name_.empty() && shm_.name() != nullptr) {
|
|
if (shm_.release() <= 1) {
|
|
if (::sem_unlink(sem_name_.c_str()) != 0) {
|
|
ipc::error("fail sem_unlink[%d]: %s, name: %s\n", errno, sem_name_.c_str());
|
|
}
|
|
}
|
|
}
|
|
sem_name_.clear();
|
|
}
|
|
|
|
void clear() noexcept {
|
|
if (valid()) {
|
|
if (::sem_close(h_) != 0) {
|
|
ipc::error("fail sem_close[%d]: %s\n", errno);
|
|
}
|
|
h_ = SEM_FAILED;
|
|
}
|
|
if (!sem_name_.empty()) {
|
|
::sem_unlink(sem_name_.c_str());
|
|
sem_name_.clear();
|
|
}
|
|
shm_.clear(); // Make sure the storage is cleaned up.
|
|
}
|
|
|
|
static void clear_storage(char const *name) noexcept {
|
|
// Construct the semaphore name same way as open() does
|
|
std::string sem_name;
|
|
if (name[0] == '/') {
|
|
sem_name = std::string(name) + "_sem";
|
|
} else {
|
|
sem_name = std::string("/") + name + "_sem";
|
|
}
|
|
::sem_unlink(sem_name.c_str());
|
|
ipc::shm::handle::clear_storage(name);
|
|
}
|
|
|
|
bool wait(std::uint64_t tm) noexcept {
|
|
if (!valid()) return false;
|
|
if (tm == invalid_value) {
|
|
if (::sem_wait(h_) != 0) {
|
|
ipc::error("fail sem_wait[%d]: %s\n", errno);
|
|
return false;
|
|
}
|
|
} else {
|
|
auto ts = posix_::detail::make_timespec(tm);
|
|
if (::sem_timedwait(h_, &ts) != 0) {
|
|
if (errno != ETIMEDOUT) {
|
|
ipc::error("fail sem_timedwait[%d]: tm = %zd, tv_sec = %ld, tv_nsec = %ld\n",
|
|
errno, tm, ts.tv_sec, ts.tv_nsec);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool post(std::uint32_t count) noexcept {
|
|
if (!valid()) return false;
|
|
for (std::uint32_t i = 0; i < count; ++i) {
|
|
if (::sem_post(h_) != 0) {
|
|
ipc::error("fail sem_post[%d]: %s\n", errno);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
} // namespace sync
|
|
} // namespace detail
|
|
} // namespace ipc
|