Added a cleanup interface for shared memory handles

This commit is contained in:
mutouyun 2024-11-17 17:35:29 +08:00
parent 805490605e
commit 5071fb5db6
8 changed files with 273 additions and 201 deletions

View File

@ -11,8 +11,8 @@ namespace mem {
class IPC_EXPORT pool_alloc { class IPC_EXPORT pool_alloc {
public: public:
static void* alloc(std::size_t size); static void* alloc(std::size_t size) noexcept;
static void free (void* p, std::size_t size); static void free (void* p, std::size_t size) noexcept;
}; };
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////

View File

@ -17,9 +17,9 @@ enum : unsigned {
IPC_EXPORT id_t acquire(char const * name, std::size_t size, unsigned mode = create | open); IPC_EXPORT id_t acquire(char const * name, std::size_t size, unsigned mode = create | open);
IPC_EXPORT void * get_mem(id_t id, std::size_t * size); IPC_EXPORT void * get_mem(id_t id, std::size_t * size);
IPC_EXPORT std::int32_t release(id_t id); IPC_EXPORT std::int32_t release(id_t id) noexcept;
IPC_EXPORT void remove (id_t id); IPC_EXPORT void remove (id_t id) noexcept;
IPC_EXPORT void remove (char const * name); IPC_EXPORT void remove (char const * name) noexcept;
IPC_EXPORT std::int32_t get_ref(id_t id); IPC_EXPORT std::int32_t get_ref(id_t id);
IPC_EXPORT void sub_ref(id_t id); IPC_EXPORT void sub_ref(id_t id);
@ -45,6 +45,10 @@ public:
bool acquire(char const * name, std::size_t size, unsigned mode = create | open); bool acquire(char const * name, std::size_t size, unsigned mode = create | open);
std::int32_t release(); std::int32_t release();
// Clean the handle file.
void clear() noexcept;
static void clear_storage(char const * name) noexcept;
void* get() const; void* get() const;
void attach(id_t); void attach(id_t);

View File

@ -19,17 +19,17 @@ namespace mem {
class static_alloc { class static_alloc {
public: public:
static void swap(static_alloc&) {} static void swap(static_alloc&) noexcept {}
static void* alloc(std::size_t size) { static void* alloc(std::size_t size) noexcept {
return size ? std::malloc(size) : nullptr; return size ? std::malloc(size) : nullptr;
} }
static void free(void* p) { static void free(void* p) noexcept {
std::free(p); std::free(p);
} }
static void free(void* p, std::size_t /*size*/) { static void free(void* p, std::size_t /*size*/) noexcept {
free(p); free(p);
} }
}; };

View File

@ -153,7 +153,7 @@ void * get_mem(id_t id, std::size_t * size) {
return mem; return mem;
} }
std::int32_t release(id_t id) { std::int32_t release(id_t id) noexcept {
if (id == nullptr) { if (id == nullptr) {
ipc::error("fail release: invalid id (null)\n"); ipc::error("fail release: invalid id (null)\n");
return -1; return -1;
@ -175,7 +175,7 @@ std::int32_t release(id_t id) {
return ret; return ret;
} }
void remove(id_t id) { void remove(id_t id) noexcept {
if (id == nullptr) { if (id == nullptr) {
ipc::error("fail remove: invalid id (null)\n"); ipc::error("fail remove: invalid id (null)\n");
return; return;

View File

@ -5,11 +5,11 @@
namespace ipc { namespace ipc {
namespace mem { namespace mem {
void* pool_alloc::alloc(std::size_t size) { void* pool_alloc::alloc(std::size_t size) noexcept {
return async_pool_alloc::alloc(size); return async_pool_alloc::alloc(size);
} }
void pool_alloc::free(void* p, std::size_t size) { void pool_alloc::free(void* p, std::size_t size) noexcept {
async_pool_alloc::free(p, size); async_pool_alloc::free(p, size);
} }

View File

@ -89,6 +89,18 @@ std::int32_t handle::release() {
return shm::release(detach()); return shm::release(detach());
} }
void handle::clear() noexcept {
if (impl(p_)->id_ == nullptr) return;
shm::remove(detach());
}
void handle::clear_storage(char const * name) noexcept {
if (name == nullptr) {
return;
}
shm::remove(name);
}
void* handle::get() const { void* handle::get() const {
return impl(p_)->m_; return impl(p_)->m_;
} }

View File

@ -1,86 +1,110 @@
#pragma once #pragma once
#include <iostream> #include <iostream>
#include <atomic> #include <atomic>
#include <thread> #include <thread>
#include <string> #include <string>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <utility> #include <utility>
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "capo/stopwatch.hpp" #include "capo/stopwatch.hpp"
#include "thread_pool.h" #include "thread_pool.h"
namespace ipc_ut { #include "libipc/platform/detail.h"
#ifdef IPC_OS_LINUX_
template <typename Dur> #include <fcntl.h> // ::open
struct unit; #endif
template <> struct unit<std::chrono::nanoseconds> { namespace ipc_ut {
constexpr static char const * str() noexcept {
return "ns"; template <typename Dur>
} struct unit;
};
template <> struct unit<std::chrono::nanoseconds> {
template <> struct unit<std::chrono::microseconds> { constexpr static char const * str() noexcept {
constexpr static char const * str() noexcept { return "ns";
return "us"; }
} };
};
template <> struct unit<std::chrono::microseconds> {
template <> struct unit<std::chrono::milliseconds> { constexpr static char const * str() noexcept {
constexpr static char const * str() noexcept { return "us";
return "ms"; }
} };
};
template <> struct unit<std::chrono::milliseconds> {
template <> struct unit<std::chrono::seconds> { constexpr static char const * str() noexcept {
constexpr static char const * str() noexcept { return "ms";
return "sec"; }
} };
};
template <> struct unit<std::chrono::seconds> {
struct test_stopwatch { constexpr static char const * str() noexcept {
capo::stopwatch<> sw_; return "sec";
std::atomic_flag started_ = ATOMIC_FLAG_INIT; }
};
void start() {
if (!started_.test_and_set()) { struct test_stopwatch {
sw_.start(); capo::stopwatch<> sw_;
} std::atomic_flag started_ = ATOMIC_FLAG_INIT;
}
void start() {
template <typename ToDur = std::chrono::nanoseconds> if (!started_.test_and_set()) {
void print_elapsed(int N, int Loops, char const * message = "") { sw_.start();
auto ts = sw_.elapsed<ToDur>(); }
std::cout << "[" << N << ", \t" << Loops << "] " << message << "\t" }
<< (double(ts) / double(Loops)) << " " << unit<ToDur>::str() << std::endl;
} template <typename ToDur = std::chrono::nanoseconds>
void print_elapsed(int N, int Loops, char const * message = "") {
template <int Factor, typename ToDur = std::chrono::nanoseconds> auto ts = sw_.elapsed<ToDur>();
void print_elapsed(int N, int M, int Loops, char const * message = "") { std::cout << "[" << N << ", \t" << Loops << "] " << message << "\t"
auto ts = sw_.elapsed<ToDur>(); << (double(ts) / double(Loops)) << " " << unit<ToDur>::str() << std::endl;
std::cout << "[" << N << "-" << M << ", \t" << Loops << "] " << message << "\t" }
<< (double(ts) / double(Factor ? (Loops * Factor) : (Loops * N))) << " " << unit<ToDur>::str() << std::endl;
} template <int Factor, typename ToDur = std::chrono::nanoseconds>
void print_elapsed(int N, int M, int Loops, char const * message = "") {
template <typename ToDur = std::chrono::nanoseconds> auto ts = sw_.elapsed<ToDur>();
void print_elapsed(int N, int M, int Loops, char const * message = "") { std::cout << "[" << N << "-" << M << ", \t" << Loops << "] " << message << "\t"
print_elapsed<0, ToDur>(N, M, Loops, message); << (double(ts) / double(Factor ? (Loops * Factor) : (Loops * N))) << " " << unit<ToDur>::str() << std::endl;
} }
};
template <typename ToDur = std::chrono::nanoseconds>
inline static thread_pool & sender() { void print_elapsed(int N, int M, int Loops, char const * message = "") {
static thread_pool pool; print_elapsed<0, ToDur>(N, M, Loops, message);
return pool; }
} };
inline static thread_pool & reader() { inline static thread_pool & sender() {
static thread_pool pool; static thread_pool pool;
return pool; return pool;
} }
} // namespace ipc_ut inline static thread_pool & reader() {
static thread_pool pool;
return pool;
}
#ifdef IPC_OS_LINUX_
inline bool check_exist(char const *name) noexcept {
int fd = ::open((std::string{"/dev/shm/__IPC_SHM__"} + name).c_str(), O_RDONLY);
if (fd == -1) {
return false;
}
::close(fd);
return true;
}
#endif
inline bool expect_exist(char const *name, bool expected) noexcept {
#ifdef IPC_OS_LINUX_
return ipc_ut::check_exist(name) == expected;
#else
return true;
#endif
}
} // namespace ipc_ut

View File

@ -1,102 +1,134 @@
#include <cstring> #include <cstring>
#include <cstdint> #include <cstdint>
#include <thread> #include <thread>
#include "libipc/shm.h" #include "libipc/shm.h"
#include "test.h"
#include "test.h"
using namespace ipc::shm;
using namespace ipc::shm;
namespace {
namespace {
TEST(SHM, acquire) {
handle shm_hd; TEST(SHM, acquire) {
EXPECT_FALSE(shm_hd.valid()); handle shm_hd;
EXPECT_FALSE(shm_hd.valid());
EXPECT_TRUE(shm_hd.acquire("my-test-1", 1024));
EXPECT_TRUE(shm_hd.valid()); EXPECT_TRUE(shm_hd.acquire("my-test-1", 1024));
EXPECT_STREQ(shm_hd.name(), "my-test-1"); EXPECT_TRUE(shm_hd.valid());
EXPECT_STREQ(shm_hd.name(), "my-test-1");
EXPECT_TRUE(shm_hd.acquire("my-test-2", 2048));
EXPECT_TRUE(shm_hd.valid()); EXPECT_TRUE(shm_hd.acquire("my-test-2", 2048));
EXPECT_STREQ(shm_hd.name(), "my-test-2"); EXPECT_TRUE(shm_hd.valid());
EXPECT_STREQ(shm_hd.name(), "my-test-2");
EXPECT_TRUE(shm_hd.acquire("my-test-3", 4096));
EXPECT_TRUE(shm_hd.valid()); EXPECT_TRUE(shm_hd.acquire("my-test-3", 4096));
EXPECT_STREQ(shm_hd.name(), "my-test-3"); EXPECT_TRUE(shm_hd.valid());
} EXPECT_STREQ(shm_hd.name(), "my-test-3");
}
TEST(SHM, release) {
handle shm_hd; TEST(SHM, release) {
EXPECT_FALSE(shm_hd.valid()); handle shm_hd;
shm_hd.release(); EXPECT_FALSE(shm_hd.valid());
EXPECT_FALSE(shm_hd.valid()); shm_hd.release();
EXPECT_TRUE(shm_hd.acquire("release-test-1", 512)); EXPECT_FALSE(shm_hd.valid());
EXPECT_TRUE(shm_hd.valid()); EXPECT_TRUE(shm_hd.acquire("release-test-1", 512));
shm_hd.release(); EXPECT_TRUE(shm_hd.valid());
EXPECT_FALSE(shm_hd.valid()); shm_hd.release();
} EXPECT_FALSE(shm_hd.valid());
}
TEST(SHM, get) {
handle shm_hd; TEST(SHM, get) {
EXPECT_TRUE(shm_hd.get() == nullptr); handle shm_hd;
EXPECT_TRUE(shm_hd.acquire("get-test", 2048)); EXPECT_TRUE(shm_hd.get() == nullptr);
EXPECT_TRUE(shm_hd.acquire("get-test", 2048));
auto mem = shm_hd.get();
EXPECT_TRUE(mem != nullptr); auto mem = shm_hd.get();
EXPECT_TRUE(mem == shm_hd.get()); EXPECT_TRUE(mem != nullptr);
EXPECT_TRUE(mem == shm_hd.get());
std::uint8_t buf[1024] = {};
EXPECT_TRUE(memcmp(mem, buf, sizeof(buf)) == 0); std::uint8_t buf[1024] = {};
EXPECT_TRUE(memcmp(mem, buf, sizeof(buf)) == 0);
handle shm_other(shm_hd.name(), shm_hd.size());
EXPECT_TRUE(shm_other.get() != shm_hd.get()); handle shm_other(shm_hd.name(), shm_hd.size());
} EXPECT_TRUE(shm_other.get() != shm_hd.get());
}
TEST(SHM, hello) {
handle shm_hd; TEST(SHM, hello) {
EXPECT_TRUE(shm_hd.acquire("hello-test", 128)); handle shm_hd;
auto mem = shm_hd.get(); EXPECT_TRUE(shm_hd.acquire("hello-test", 128));
EXPECT_TRUE(mem != nullptr); auto mem = shm_hd.get();
EXPECT_TRUE(mem != nullptr);
constexpr char hello[] = "hello!";
std::memcpy(mem, hello, sizeof(hello)); constexpr char hello[] = "hello!";
EXPECT_STREQ((char const *)shm_hd.get(), hello); std::memcpy(mem, hello, sizeof(hello));
EXPECT_STREQ((char const *)shm_hd.get(), hello);
shm_hd.release();
EXPECT_TRUE(shm_hd.get() == nullptr); shm_hd.release();
EXPECT_TRUE(shm_hd.acquire("hello-test", 1024)); EXPECT_TRUE(shm_hd.get() == nullptr);
EXPECT_TRUE(shm_hd.acquire("hello-test", 1024));
mem = shm_hd.get();
EXPECT_TRUE(mem != nullptr); mem = shm_hd.get();
std::uint8_t buf[1024] = {}; EXPECT_TRUE(mem != nullptr);
EXPECT_TRUE(memcmp(mem, buf, sizeof(buf)) == 0); std::uint8_t buf[1024] = {};
EXPECT_TRUE(memcmp(mem, buf, sizeof(buf)) == 0);
std::memcpy(mem, hello, sizeof(hello));
EXPECT_STREQ((char const *)shm_hd.get(), hello); std::memcpy(mem, hello, sizeof(hello));
} EXPECT_STREQ((char const *)shm_hd.get(), hello);
}
TEST(SHM, mt) {
handle shm_hd; TEST(SHM, mt) {
EXPECT_TRUE(shm_hd.acquire("mt-test", 256)); handle shm_hd;
constexpr char hello[] = "hello!"; EXPECT_TRUE(shm_hd.acquire("mt-test", 256));
std::memcpy(shm_hd.get(), hello, sizeof(hello)); constexpr char hello[] = "hello!";
std::memcpy(shm_hd.get(), hello, sizeof(hello));
std::thread {
[&shm_hd] { std::thread {
handle shm_mt(shm_hd.name(), shm_hd.size()); [&shm_hd] {
shm_hd.release(); handle shm_mt(shm_hd.name(), shm_hd.size());
constexpr char hello[] = "hello!"; shm_hd.release();
EXPECT_STREQ((char const *)shm_mt.get(), hello); constexpr char hello[] = "hello!";
} EXPECT_STREQ((char const *)shm_mt.get(), hello);
}.join(); }
}.join();
EXPECT_TRUE(shm_hd.get() == nullptr);
EXPECT_FALSE(shm_hd.valid()); EXPECT_TRUE(shm_hd.get() == nullptr);
EXPECT_FALSE(shm_hd.valid());
EXPECT_TRUE(shm_hd.acquire("mt-test", 1024));
std::uint8_t buf[1024] = {}; EXPECT_TRUE(shm_hd.acquire("mt-test", 1024));
EXPECT_TRUE(memcmp(shm_hd.get(), buf, sizeof(buf)) == 0); std::uint8_t buf[1024] = {};
} EXPECT_TRUE(memcmp(shm_hd.get(), buf, sizeof(buf)) == 0);
}
} // internal-linkage
TEST(SHM, remove) {
{
auto id = ipc::shm::acquire("hello-remove", 111);
EXPECT_TRUE(ipc_ut::expect_exist("hello-remove", true));
ipc::shm::remove(id);
EXPECT_TRUE(ipc_ut::expect_exist("hello-remove", false));
}
{
auto id = ipc::shm::acquire("hello-remove", 111);
EXPECT_TRUE(ipc_ut::expect_exist("hello-remove", true));
ipc::shm::release(id);
EXPECT_TRUE(ipc_ut::expect_exist("hello-remove", true));
ipc::shm::remove("hello-remove");
EXPECT_TRUE(ipc_ut::expect_exist("hello-remove", false));
}
{
handle shm_hd;
EXPECT_TRUE(shm_hd.acquire("mt-test", 256));
EXPECT_TRUE(ipc_ut::expect_exist("mt-test", true));
shm_hd.clear();
EXPECT_TRUE(ipc_ut::expect_exist("mt-test", false));
}
{
handle shm_hd;
EXPECT_TRUE(shm_hd.acquire("mt-test", 256));
EXPECT_TRUE(ipc_ut::expect_exist("mt-test", true));
shm_hd.clear_storage("mt-test");
EXPECT_TRUE(ipc_ut::expect_exist("mt-test", false));
}
}
} // internal-linkage