#pragma once #include #include /* For O_* constants */ #include /* For mode constants */ #include #include #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(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