Check if name already starts with '/' before adding prefix, consistent
with the pattern used in semaphore_impl.h. This avoids duplicate prefix
when users provide names in the correct format.
Problem: Tests fail on the second run on FreeBSD with ShmTest.RemoveByName failing.
After the first test run completes, subsequent runs fail because shared memory
objects are not properly removed.
Root cause: FreeBSD's shm_unlink() is stricter than Linux about POSIX compliance.
The remove(char const * name) function was calling shm_unlink() without the '/'
prefix, while acquire() was using '/'+name format. This inconsistency caused:
- Linux: Silently tolerates both /name and name formats
- FreeBSD: Strictly requires /name format, shm_unlink("name") fails
When shm_unlink() fails to remove the shared memory object:
1. First test run creates /remove_by_name_test_1
2. Test calls shm::remove("remove_by_name_test_1")
3. shm_unlink("remove_by_name_test_1") fails on FreeBSD (missing '/')
4. Shared memory object remains in the system
5. Second test run tries to reuse the same name -> conflict -> test fails
Solution:
1. Fix remove(char const * name) to prepend '/' to the name for consistency
with acquire() function, ensuring POSIX compliance
2. Add error checking for all shm_unlink() calls to log failures with errno
This ensures proper cleanup on FreeBSD and maintains compatibility with Linux.
Changes:
- Modified remove(char const * name) to use '/'+name format
- Added error logging for all three shm_unlink() calls
- Now consistent with POSIX requirement: shared memory names must be /somename
Tested on FreeBSD 15: Multiple consecutive test runs now pass without failures.
Problem: The previous fix unconditionally called pthread_mutex_unlock() at the
beginning of close(), which could interfere with other threads/processes that
still had valid references to the mutex. This caused test failures on FreeBSD
when running tests multiple times (ShmTest.RemoveByName would fail on the second run).
Root cause: Calling unlock() too early could affect the mutex state for other
references that are still using it, leading to unexpected behavior.
Solution: Move pthread_mutex_unlock() to only be called when we're about to
destroy the mutex (i.e., when we're the last reference: shm_->ref() <= 1 &&
self_ref <= 1). This ensures:
1. We don't interfere with other threads/processes using the mutex
2. We still unlock before destroying to avoid FreeBSD robust list issues
3. The unlock happens at the correct time - right before pthread_mutex_destroy()
This is the correct approach because:
- Only the last reference holder should clean up the mutex
- Unlocking should be paired with destroying for the final cleanup
- Other references should not be affected by one reference closing
Fixes the second-run test failure on FreeBSD while maintaining the segfault fix.
Root cause: FreeBSD's robust mutex implementation (libthr) maintains a per-thread
robust list of mutexes. The error 'rb error 14' (EFAULT - Bad address) indicates
that the robust list contained a dangling pointer to a destroyed mutex.
When a mutex object is destroyed (via close() or clear()), if the mutex is still
in the current thread's robust list, FreeBSD's libthr may try to access it later
and encounter an invalid pointer, causing a segmentation fault.
This happened in MutexTest.TryLockExceptionSafety because:
1. The test called try_lock() which successfully acquired the lock
2. The test ended without calling unlock()
3. The mutex destructor called close()
4. close() called pthread_mutex_destroy() on a mutex that was:
- Still locked by the current thread, OR
- Still in the thread's robust list
Solution:
Call pthread_mutex_unlock() before pthread_mutex_destroy() in both close()
and clear() methods. This ensures:
1. The mutex is unlocked if we hold the lock
2. The mutex is removed from the thread's robust list
3. Subsequent pthread_mutex_destroy() is safe
We ignore errors from pthread_mutex_unlock() because:
- If we don't hold the lock, EPERM is expected and harmless
- If the mutex is already unlocked, this is a no-op
- Even if there's an error, we still want to proceed with cleanup
This fix is platform-agnostic and should not affect Linux/QNX behavior,
as both also use pthread robust mutexes with similar semantics.
Fixes the segfault in MutexTest.TryLockExceptionSafety on FreeBSD 15.
Root cause: The previous code incorrectly called shm_->sub_ref() when handling
EOWNERDEAD, which could cause the shared memory to be freed prematurely while
the mutex pointer was still in use, leading to segmentation fault.
Fix: Remove the shm_->sub_ref() call. When EOWNERDEAD is returned, it means
we have successfully acquired the lock. We only need to call pthread_mutex_consistent()
to make the mutex usable again, then return success. The shared memory reference
count should not be modified in this path.
This fixes the segfault in MutexTest.TryLockExceptionSafety on FreeBSD 15.
Remove custom deduction guides for std::unique_ptr in C++17 mode.
Issue: FreeBSD GCC 13.3 correctly rejects adding deduction guides
to namespace std, which violates C++ standard [namespace.std]:
"The behavior of a C++ program is undefined if it adds declarations
or definitions to namespace std or to a namespace within namespace std."
Root cause: The code attempted to add custom deduction guides for
std::unique_ptr in namespace std when compiling in C++17 mode.
This is not allowed by the C++ standard.
Solution: Remove the custom deduction guides for C++17 and later,
as the C++17 standard library already provides deduction guides
for std::unique_ptr (added in C++17 via P0433R2).
The custom deduction guide wrappers in the else branch (for C++14
and earlier) are kept as they provide helper functions, not actual
deduction guides in namespace std.
Tested-on: FreeBSD 15 with GCC 13.3
Fixes: Compilation error 'deduction guide must be declared in the
same scope as template std::unique_ptr'
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
- Remove useless 'ii->size_ = ii->size_;' statement at line 140
- The user-requested size is already set in acquire() function
- Simplify else branch to just a comment for clarity
- No functional change, just code cleanup
Problem:
- Reference counting tests fail on Windows (ReleaseMemory, ReferenceCount,
SubtractReference, HandleRef, HandleSubRef)
- get_ref() and sub_ref() were stub implementations returning 0/doing nothing
- CreateFileMapping HANDLE lacks built-in reference counting mechanism
Solution:
- Implement reference counting using std::atomic<std::int32_t> stored at
the end of shared memory (same strategy as POSIX version)
- Add calc_size() helper to allocate extra space for atomic counter
- Add acc_of() helper to access the atomic counter at the end of memory
- Modify acquire() to allocate calc_size(size) instead of size
- Modify get_mem() to initialize counter to 1 on first mapping
- Modify release() to decrement counter and return ref count before decrement
- Implement get_ref() to return current reference count
- Implement sub_ref() to atomically decrement reference count
- Convert file from Windows (CRLF) to Unix (LF) line endings for consistency
Key Implementation Details:
1. Reference counter stored at end of shared memory (aligned to info_t)
2. First get_mem() call: fetch_add(1) initializes counter to 1
3. release() returns ref count before decrement (for semantics compatibility)
4. Memory layout: [user data][padding][atomic<int32_t> counter]
5. Uses memory_order_acquire/release/acq_rel for proper synchronization
This makes Windows implementation match POSIX behavior and ensures all
reference counting tests pass on Windows platform.
Problem:
- Both linux/get_wait_time.h and posix/get_wait_time.h define inline functions
make_timespec() and calc_wait_time() in namespace ipc::detail
- On Linux, semaphore uses posix implementation, but may include both headers
- This causes ODR (One Definition Rule) violation - undefined behavior
- Different inline function definitions with same name violates C++ standard
- Manifested as test failures in SemaphoreTest::WaitTimeout
Solution:
- Add platform-specific namespace layer between ipc and detail:
- linux/get_wait_time.h: ipc::linux::detail::make_timespec()
- posix/get_wait_time.h: ipc::posix::detail::make_timespec()
- Update all call sites to use fully qualified names:
- linux/condition.h: linux::detail::make_timespec()
- linux/mutex.h: linux::detail::make_timespec() (2 places)
- posix/condition.h: posix::detail::make_timespec()
- posix/mutex.h: posix::detail::make_timespec() (2 places)
- posix/semaphore_impl.h: posix::detail::make_timespec()
This ensures each platform's implementation is uniquely named, preventing
ODR violations and ensuring correct function resolution at compile time.
Header changes (include/libipc/buffer.h):
- Rename: additional → mem_to_free (better semantic name)
- Add documentation comments explaining the parameter's purpose
- Clarifies that mem_to_free is passed to destructor instead of p
- Use case: when data pointer is offset into a larger allocation
Implementation changes (src/libipc/buffer.cpp):
- Update parameter name in constructor implementation
- No logic changes, just naming improvement
Test changes (test/test_buffer.cpp):
- Fix TEST_F(BufferTest, ConstructorWithMemToFree)
- Previous test caused crash: passed stack variable address to destructor
- New test correctly demonstrates the parameter's purpose:
* Allocate 100-byte block
* Use offset portion (bytes 25-75) as data
* Destructor receives original block pointer for proper cleanup
- Prevents double-free and invalid free errors
Semantic explanation:
buffer(data_ptr, size, destructor, mem_to_free)
On destruction:
destructor(mem_to_free ? mem_to_free : data_ptr, size)
This allows:
char* block = new char[100];
char* data = block + 25;
buffer buf(data, 50, my_free, block); // Frees 'block', not 'data'
- Add IPC_OS_FREEBSD_ platform detection macro
- Enable FreeBSD to use POSIX pthread implementation (shared with QNX)
- Update all conditional compilation directives to include FreeBSD
- Update README to reflect FreeBSD platform support
FreeBSD uses the existing POSIX implementation which supports:
- Process-shared mutexes (PTHREAD_PROCESS_SHARED)
- Robust mutexes (PTHREAD_MUTEX_ROBUST)
- Timed lock operations
- POSIX shared memory
This is a minimal change that reuses the mature POSIX implementation
already proven by QNX platform support.