Fix FreeBSD-specific issues in POSIX implementation

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
This commit is contained in:
木头云 2025-12-06 06:05:53 +00:00
parent 006a46e09f
commit 543672b39d
2 changed files with 35 additions and 23 deletions

View File

@ -214,13 +214,13 @@ public:
ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_consistent[%d]\n", eno, eno2); ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_consistent[%d]\n", eno, eno2);
return false; return false;
} }
int eno3 = ::pthread_mutex_unlock(mutex_); // EOWNERDEAD means we have successfully acquired the lock,
if (eno3 != 0) { // but the previous owner died. After calling pthread_mutex_consistent(),
ipc::error("fail pthread_mutex_lock[%d], pthread_mutex_unlock[%d]\n", eno, eno3); // the mutex is now in a consistent state and we hold the lock.
return false; // We should return success here, not unlock and retry.
} // This avoids issues with FreeBSD's robust mutex list management.
return true;
} }
break; // loop again
default: default:
ipc::error("fail pthread_mutex_lock[%d]\n", eno); ipc::error("fail pthread_mutex_lock[%d]\n", eno);
return false; return false;
@ -244,15 +244,11 @@ public:
int eno2 = ::pthread_mutex_consistent(mutex_); int eno2 = ::pthread_mutex_consistent(mutex_);
if (eno2 != 0) { if (eno2 != 0) {
ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_consistent[%d]\n", eno, eno2); ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_consistent[%d]\n", eno, eno2);
break; throw std::system_error{eno2, std::system_category()};
}
int eno3 = ::pthread_mutex_unlock(mutex_);
if (eno3 != 0) {
ipc::error("fail pthread_mutex_timedlock[%d], pthread_mutex_unlock[%d]\n", eno, eno3);
break;
} }
// Successfully acquired the lock after making it consistent
return true;
} }
break;
default: default:
ipc::error("fail pthread_mutex_timedlock[%d]\n", eno); ipc::error("fail pthread_mutex_timedlock[%d]\n", eno);
break; break;

View File

@ -19,6 +19,7 @@ namespace sync {
class semaphore { class semaphore {
ipc::shm::handle shm_; ipc::shm::handle shm_;
sem_t *h_ = SEM_FAILED; sem_t *h_ = SEM_FAILED;
std::string sem_name_; // Store the actual semaphore name used
public: public:
semaphore() = default; semaphore() = default;
@ -38,9 +39,16 @@ public:
ipc::error("[open_semaphore] fail shm.acquire: %s\n", name); ipc::error("[open_semaphore] fail shm.acquire: %s\n", name);
return false; return false;
} }
h_ = ::sem_open(name, O_CREAT, 0666, static_cast<unsigned>(count)); // 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) { if (h_ == SEM_FAILED) {
ipc::error("fail sem_open[%d]: %s\n", errno, name); ipc::error("fail sem_open[%d]: %s\n", errno, sem_name_.c_str());
return false; return false;
} }
return true; return true;
@ -52,14 +60,14 @@ public:
ipc::error("fail sem_close[%d]: %s\n", errno); ipc::error("fail sem_close[%d]: %s\n", errno);
} }
h_ = SEM_FAILED; h_ = SEM_FAILED;
if (shm_.name() != nullptr) { if (!sem_name_.empty() && shm_.name() != nullptr) {
std::string name = shm_.name();
if (shm_.release() <= 1) { if (shm_.release() <= 1) {
if (::sem_unlink(name.c_str()) != 0) { if (::sem_unlink(sem_name_.c_str()) != 0) {
ipc::error("fail sem_unlink[%d]: %s, name: %s\n", errno, name.c_str()); ipc::error("fail sem_unlink[%d]: %s, name: %s\n", errno, sem_name_.c_str());
} }
} }
} }
sem_name_.clear();
} }
void clear() noexcept { void clear() noexcept {
@ -69,14 +77,22 @@ public:
} }
h_ = SEM_FAILED; h_ = SEM_FAILED;
} }
char const *name = shm_.name(); if (!sem_name_.empty()) {
if (name == nullptr) return; ::sem_unlink(sem_name_.c_str());
::sem_unlink(name); sem_name_.clear();
}
shm_.clear(); // Make sure the storage is cleaned up. shm_.clear(); // Make sure the storage is cleaned up.
} }
static void clear_storage(char const *name) noexcept { static void clear_storage(char const *name) noexcept {
::sem_unlink(name); // 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); ipc::shm::handle::clear_storage(name);
} }