mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 08:46:45 +08:00
fix(test): correct test logic and semantics in multiple test cases
1. ChannelTest::MultipleSendersReceivers - Add C++14-compatible latch implementation (similar to C++20 std::latch) - Ensure receivers are ready before senders start sending messages - This prevents race condition where senders might send before receivers are listening 2. RWLockTest::ReadWriteReadPattern - Fix test logic: lock_shared allows multiple concurrent readers - Previous test had race condition where both threads could read same value - New test: each thread writes based on thread id (1 or 2), then reads - Expected result: 1*20 + 2*20 = 60 3. ShmTest::ReleaseMemory - Correct return value semantics: release() returns ref count before decrement, or -1 on error - Must call get_mem() to map memory and set ref count to 1 before release - Expected: release() returns 1 (ref count before decrement) 4. ShmTest::ReferenceCount - Correct semantics: ref count is 0 after acquire (memory not mapped) - get_mem() maps memory and sets ref count to 1 - Second acquire+get_mem increases ref count to 2 - Test now properly validates reference counting behavior 5. ShmTest::SubtractReference - sub_ref() only works after get_mem() has mapped the memory - Must call get_mem() first to initialize ref count to 1 - sub_ref() then decrements it to 0 - Test now follows correct API usage pattern
This commit is contained in:
parent
d5f787596a
commit
78be284668
@ -22,6 +22,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
#include "libipc/ipc.h"
|
#include "libipc/ipc.h"
|
||||||
#include "libipc/buffer.h"
|
#include "libipc/buffer.h"
|
||||||
|
|
||||||
@ -29,6 +31,29 @@ using namespace ipc;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// Simple latch implementation for C++14 (similar to C++20 std::latch)
|
||||||
|
class latch {
|
||||||
|
public:
|
||||||
|
explicit latch(std::ptrdiff_t count) : count_(count) {}
|
||||||
|
|
||||||
|
void count_down() {
|
||||||
|
std::unique_lock<std::mutex> lock(mutex_);
|
||||||
|
if (--count_ <= 0) {
|
||||||
|
cv_.notify_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait() {
|
||||||
|
std::unique_lock<std::mutex> lock(mutex_);
|
||||||
|
cv_.wait(lock, [this] { return count_ <= 0; });
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ptrdiff_t count_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::condition_variable cv_;
|
||||||
|
};
|
||||||
|
|
||||||
std::string generate_unique_ipc_name(const char* prefix) {
|
std::string generate_unique_ipc_name(const char* prefix) {
|
||||||
static int counter = 0;
|
static int counter = 0;
|
||||||
return std::string(prefix) + "_ipc_" + std::to_string(++counter);
|
return std::string(prefix) + "_ipc_" + std::to_string(++counter);
|
||||||
@ -516,6 +541,27 @@ TEST_F(ChannelTest, MultipleSendersReceivers) {
|
|||||||
std::atomic<int> sent_count{0};
|
std::atomic<int> sent_count{0};
|
||||||
std::atomic<int> received_count{0};
|
std::atomic<int> received_count{0};
|
||||||
|
|
||||||
|
// Use latch to ensure receivers are ready before senders start
|
||||||
|
latch receivers_ready(num_receivers);
|
||||||
|
|
||||||
|
std::vector<std::thread> receivers;
|
||||||
|
for (int i = 0; i < num_receivers; ++i) {
|
||||||
|
receivers.emplace_back([&, i]() {
|
||||||
|
channel ch(name.c_str(), receiver);
|
||||||
|
receivers_ready.count_down(); // Signal this receiver is ready
|
||||||
|
|
||||||
|
for (int j = 0; j < messages_per_sender; ++j) {
|
||||||
|
buffer buf = ch.recv(2000);
|
||||||
|
if (!buf.empty()) {
|
||||||
|
++received_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all receivers to be ready
|
||||||
|
receivers_ready.wait();
|
||||||
|
|
||||||
std::vector<std::thread> senders;
|
std::vector<std::thread> senders;
|
||||||
for (int i = 0; i < num_senders; ++i) {
|
for (int i = 0; i < num_senders; ++i) {
|
||||||
senders.emplace_back([&, i]() {
|
senders.emplace_back([&, i]() {
|
||||||
@ -530,19 +576,6 @@ TEST_F(ChannelTest, MultipleSendersReceivers) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::thread> receivers;
|
|
||||||
for (int i = 0; i < num_receivers; ++i) {
|
|
||||||
receivers.emplace_back([&, i]() {
|
|
||||||
channel ch(name.c_str(), receiver);
|
|
||||||
for (int j = 0; j < messages_per_sender; ++j) {
|
|
||||||
buffer buf = ch.recv(2000);
|
|
||||||
if (!buf.empty()) {
|
|
||||||
++received_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& t : senders) {
|
for (auto& t : senders) {
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -383,20 +383,23 @@ TEST_F(RWLockTest, ReadersWritersNoOverlap) {
|
|||||||
TEST_F(RWLockTest, ReadWriteReadPattern) {
|
TEST_F(RWLockTest, ReadWriteReadPattern) {
|
||||||
rw_lock lock;
|
rw_lock lock;
|
||||||
int data = 0;
|
int data = 0;
|
||||||
|
std::atomic<int> iterations{0};
|
||||||
|
|
||||||
auto pattern_task = [&](int id) {
|
auto pattern_task = [&](int id) {
|
||||||
for (int i = 0; i < 20; ++i) {
|
for (int i = 0; i < 20; ++i) {
|
||||||
// Read
|
// Write: increment based on thread id
|
||||||
lock.lock_shared();
|
lock.lock();
|
||||||
int read_val = data;
|
data += id;
|
||||||
lock.unlock_shared();
|
lock.unlock();
|
||||||
|
|
||||||
|
iterations.fetch_add(1);
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
|
|
||||||
// Write
|
// Read: verify data is consistent
|
||||||
lock.lock();
|
lock.lock_shared();
|
||||||
data = read_val + 1;
|
int read_val = data;
|
||||||
lock.unlock();
|
EXPECT_GE(read_val, 0); // Data should be non-negative
|
||||||
|
lock.unlock_shared();
|
||||||
|
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
@ -408,7 +411,10 @@ TEST_F(RWLockTest, ReadWriteReadPattern) {
|
|||||||
t1.join();
|
t1.join();
|
||||||
t2.join();
|
t2.join();
|
||||||
|
|
||||||
EXPECT_EQ(data, 40);
|
// Each thread increments by its id (1 or 2), 20 times each
|
||||||
|
// Total = 1*20 + 2*20 = 20 + 40 = 60
|
||||||
|
EXPECT_EQ(data, 60);
|
||||||
|
EXPECT_EQ(iterations.load(), 40);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test many readers, one writer
|
// Test many readers, one writer
|
||||||
|
|||||||
@ -126,8 +126,13 @@ TEST_F(ShmTest, ReleaseMemory) {
|
|||||||
shm::id_t id = shm::acquire(name.c_str(), 128, shm::create);
|
shm::id_t id = shm::acquire(name.c_str(), 128, shm::create);
|
||||||
ASSERT_NE(id, nullptr);
|
ASSERT_NE(id, nullptr);
|
||||||
|
|
||||||
|
// Must call get_mem to map memory and set reference count
|
||||||
|
void* mem = shm::get_mem(id, nullptr);
|
||||||
|
ASSERT_NE(mem, nullptr);
|
||||||
|
|
||||||
|
// release returns the reference count before decrement, or -1 on error
|
||||||
std::int32_t ref_count = shm::release(id);
|
std::int32_t ref_count = shm::release(id);
|
||||||
EXPECT_GE(ref_count, 0);
|
EXPECT_EQ(ref_count, 1); // Should be 1 (set by get_mem, before decrement)
|
||||||
|
|
||||||
shm::remove(name.c_str());
|
shm::remove(name.c_str());
|
||||||
}
|
}
|
||||||
@ -161,14 +166,25 @@ TEST_F(ShmTest, ReferenceCount) {
|
|||||||
shm::id_t id1 = shm::acquire(name.c_str(), 512, shm::create);
|
shm::id_t id1 = shm::acquire(name.c_str(), 512, shm::create);
|
||||||
ASSERT_NE(id1, nullptr);
|
ASSERT_NE(id1, nullptr);
|
||||||
|
|
||||||
std::int32_t ref1 = shm::get_ref(id1);
|
// Reference count is 0 after acquire (memory not mapped yet)
|
||||||
EXPECT_GT(ref1, 0);
|
std::int32_t ref_before_get_mem = shm::get_ref(id1);
|
||||||
|
EXPECT_EQ(ref_before_get_mem, 0);
|
||||||
|
|
||||||
// Acquire again (should increase reference count)
|
// get_mem maps memory and sets reference count to 1
|
||||||
|
void* mem1 = shm::get_mem(id1, nullptr);
|
||||||
|
ASSERT_NE(mem1, nullptr);
|
||||||
|
|
||||||
|
std::int32_t ref1 = shm::get_ref(id1);
|
||||||
|
EXPECT_EQ(ref1, 1);
|
||||||
|
|
||||||
|
// Acquire again and get_mem (should increase reference count)
|
||||||
shm::id_t id2 = shm::acquire(name.c_str(), 512, shm::open);
|
shm::id_t id2 = shm::acquire(name.c_str(), 512, shm::open);
|
||||||
if (id2 != nullptr) {
|
if (id2 != nullptr) {
|
||||||
|
void* mem2 = shm::get_mem(id2, nullptr);
|
||||||
|
ASSERT_NE(mem2, nullptr);
|
||||||
|
|
||||||
std::int32_t ref2 = shm::get_ref(id2);
|
std::int32_t ref2 = shm::get_ref(id2);
|
||||||
EXPECT_GE(ref2, ref1);
|
EXPECT_EQ(ref2, 2); // Should be 2 now
|
||||||
|
|
||||||
shm::release(id2);
|
shm::release(id2);
|
||||||
}
|
}
|
||||||
@ -184,11 +200,17 @@ TEST_F(ShmTest, SubtractReference) {
|
|||||||
shm::id_t id = shm::acquire(name.c_str(), 256, shm::create);
|
shm::id_t id = shm::acquire(name.c_str(), 256, shm::create);
|
||||||
ASSERT_NE(id, nullptr);
|
ASSERT_NE(id, nullptr);
|
||||||
|
|
||||||
std::int32_t ref_before = shm::get_ref(id);
|
// Must call get_mem first to map memory and initialize reference count
|
||||||
shm::sub_ref(id);
|
void* mem = shm::get_mem(id, nullptr);
|
||||||
std::int32_t ref_after = shm::get_ref(id);
|
ASSERT_NE(mem, nullptr);
|
||||||
|
|
||||||
EXPECT_EQ(ref_after, ref_before - 1);
|
std::int32_t ref_before = shm::get_ref(id);
|
||||||
|
EXPECT_EQ(ref_before, 1); // Should be 1 after get_mem
|
||||||
|
|
||||||
|
shm::sub_ref(id);
|
||||||
|
|
||||||
|
std::int32_t ref_after = shm::get_ref(id);
|
||||||
|
EXPECT_EQ(ref_after, 0); // Should be 0 after sub_ref
|
||||||
|
|
||||||
// Use remove(id) to clean up - it internally calls release()
|
// Use remove(id) to clean up - it internally calls release()
|
||||||
shm::remove(id);
|
shm::remove(id);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user