Add CodeCov test coverage configuration:
- Add LIBIPC_CODECOV option to CMakeLists.txt with coverage compilation flags
- Add CodeCov badge to README.md for coverage status display
Note: The codecov.yml workflow file needs to be added manually
or requires workflows permission to push.
Fixed two critical issues from the rebase:
1. Added LIBIPC_OS_FREEBSD macro definition in detect_plat.h to enable
FreeBSD platform detection alongside other OS checks
2. Added missing #include "libipc/imp/detect_plat.h" in detail.h to
properly include platform detection macros
These fixes ensure FreeBSD compilation will work correctly with the
unified platform detection system.
The acquire() function allocates id_info_t using mem::$new<id_info_t>(),
so the release() function must use mem::$delete(ii) to deallocate it,
not mem::free(ii). This ensures proper allocation/deallocation pairing.
Issue: Memory allocated with mem::$new must be freed with mem::$delete
to maintain consistent memory management semantics.
After rebasing onto master, test.h was moved to test/archive/.
Updated include paths in test subdirectories:
- test/imp/*.cpp: "test.h" -> "../archive/test.h"
- test/mem/*.cpp: "test.h" -> "../archive/test.h"
- test/concur/*.cpp: "test.h" -> "../archive/test.h"
This ensures all test files can properly find the test header
after the directory reorganization in master branch.
ISSUE:
MSVC compiler reports warning C4138: '*/' found outside of comment
for patterns like 'void */*p*/' where the pointer asterisk is immediately
followed by a comment start.
AFFECTED FILES:
- include/libipc/mem/new.h (line 30)
- src/libipc/platform/win/mutex.h (line 54)
- src/libipc/platform/win/semaphore.h (line 53)
CHANGES:
Changed 'type */*param*/' to 'type * /*param*/' (added space before comment)
Examples:
- void */*p*/ → void * /*p*/
- char const */*name*/ → char const * /*name*/
This resolves the MSVC warning while maintaining code functionality
and keeping the commented-out parameter names for documentation.
ROOT CAUSE:
Array placement new (::new(buffer) T[N]) adds a hidden cookie (array size)
before the array elements in some compiler implementations (particularly MSVC).
The cookie is used for proper array destruction. However, the data_set buffer
was sized only for sizeof(T[N]), not accounting for the cookie overhead.
ISSUE:
- Buffer allocated: sizeof(rand_buf[LoopCount])
- Actual space needed: sizeof(cookie) + sizeof(rand_buf[LoopCount])
- Result: Cookie and part of array written beyond buffer boundary
- Consequence: Memory corruption, leading to invalid pointers in buffer objects
SYMPTOM:
In IPC.1v1 test, memcpy(buf, data, size) crashed because 'data' pointer
(from buffer::data()) pointed to corrupted/invalid memory address.
SOLUTION:
Replace array placement new with individual element placement new:
- Cast buffer to array pointer directly (no cookie needed)
- Construct each element individually with placement new
- Manually destroy each element in destructor
This approach:
- Eliminates cookie overhead
- Provides precise control over object lifetime
- Works consistently across all compilers
Fixes crash in IPC.1v1 test case on MSVC.
IMPROVEMENTS:
1. Add explicit zero-argument overload to avoid SFINAE ambiguity
2. Require at least one argument (A1) for parameterized overloads
3. Better separation between direct initialization and aggregate initialization
BENEFITS:
- Clearer intent: zero-argument construction is explicitly handled
- Avoids potential SFINAE ambiguity when empty parameter pack is used
- More maintainable: easier to understand which overload is selected
- Consistent with modern C++ best practices for variadic templates
TECHNICAL DETAILS:
- Zero-arg overload: Always uses T() for value initialization
- One-or-more-arg overload: Uses SFINAE to choose between:
* T(args...) for types with matching constructor
* T{args...} for aggregate types or types with initializer_list ctor
This is a code quality improvement and does not fix any compilation issues,
but provides better template overload resolution.
ROOT CAUSE:
The allocate() function was incorrectly constructing objects during memory
allocation, violating C++ allocator requirements. MSVC's std::_Tree_node has
a deleted default constructor, causing compilation failure.
CHANGES:
- container_allocator::allocate() now only allocates raw memory without
constructing objects (removed mem::$new and ipc::construct calls)
- container_allocator::deallocate() now only frees memory without
destroying objects (removed mem::$delete and ipc::destroy_n calls)
WHY THIS FIXES THE ISSUE:
C++ allocator semantics require strict separation:
* allocate() -> raw memory allocation only
* construct() -> object construction with proper arguments
* destroy() -> object destruction
* deallocate() -> memory deallocation only
Standard containers (like std::map) call construct() with proper arguments
(key, value) to initialize nodes, not allocate(). Since std::_Tree_node in
MSVC has no default constructor (= delete), attempting to construct it
without arguments always fails.
Fixes MSVC 2017 compilation error:
error C2280: 'std::_Tree_node<...>::_Tree_node(void)':
attempting to reference a deleted function
Fixes#165
- Update project version in root CMakeLists.txt to 1.4.1
- Update PACKAGE_VERSION in src/CMakeLists.txt to 1.4.1
- Ensures compiled library has correct version number for the upcoming 1.4.1 release
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.