mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 16:56:45 +08:00
add: [ipc] shared_memory object
This commit is contained in:
parent
af654d5769
commit
d55d9f549f
@ -37,6 +37,37 @@ LIBIMP_EXPORT std::size_t shm_size(shm_t) noexcept;
|
||||
|
||||
/// @brief Gets the name of the shared memory file based on the shared memory handle.
|
||||
/// @return empty string on failure.
|
||||
LIBIMP_EXPORT std::string shm_file(shm_t) noexcept;
|
||||
LIBIMP_EXPORT std::string shm_name(shm_t) noexcept;
|
||||
|
||||
/**
|
||||
* @brief The shared memory object.
|
||||
*/
|
||||
class LIBIMP_EXPORT shared_memory {
|
||||
shm_t shm_;
|
||||
|
||||
public:
|
||||
explicit shared_memory() noexcept;
|
||||
~shared_memory() noexcept;
|
||||
|
||||
shared_memory(shared_memory &&other) noexcept;
|
||||
shared_memory &operator=(shared_memory &&rhs) & noexcept;
|
||||
void swap(shared_memory &other) noexcept;
|
||||
|
||||
explicit shared_memory(std::string name,
|
||||
std::size_t size = 0,
|
||||
mode::type = mode::create | mode::open) noexcept;
|
||||
|
||||
bool open(std::string name, std::size_t size, mode::type) noexcept;
|
||||
void close() noexcept;
|
||||
bool valid() const noexcept;
|
||||
|
||||
void *get() const noexcept;
|
||||
void *operator*() const noexcept;
|
||||
template <typename T>
|
||||
T *as() { return static_cast<T *>(get()); }
|
||||
|
||||
std::size_t size() const noexcept;
|
||||
std::string name() const noexcept;
|
||||
};
|
||||
|
||||
LIBIPC_NAMESPACE_END_
|
||||
|
||||
@ -67,7 +67,7 @@ result<shm_t> shm_open(std::string name, std::size_t size, mode::type type) noex
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IROTH | S_IWOTH);
|
||||
if (fd == posix::failed) {
|
||||
log.error("shm_open fails. error = {}", sys::error_msg(sys::error_code()));
|
||||
log.error("shm_open fails. error = {}", sys::error());
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -75,14 +75,14 @@ result<shm_t> shm_open(std::string name, std::size_t size, mode::type type) noex
|
||||
/// @brief Try to get the size of this fd
|
||||
struct stat st;
|
||||
if (::fstat(fd, &st) == posix::failed) {
|
||||
log.error("fstat fails. error = {}", sys::error_msg(sys::error_code()));
|
||||
log.error("fstat fails. error = {}", sys::error());
|
||||
::shm_unlink(name.c_str());
|
||||
return {};
|
||||
}
|
||||
size = static_cast<std::size_t>(st.st_size);
|
||||
/// @brief Truncate this fd to a specified length
|
||||
} else if (::ftruncate(fd, size) != 0) {
|
||||
log.error("ftruncate fails. error = {}", sys::error_msg(sys::error_code()));
|
||||
log.error("ftruncate fails. error = {}", sys::error());
|
||||
::shm_unlink(name.c_str());
|
||||
return {};
|
||||
}
|
||||
@ -90,7 +90,7 @@ result<shm_t> shm_open(std::string name, std::size_t size, mode::type type) noex
|
||||
/// @brief Creates a new mapping in the virtual address space of the calling process.
|
||||
void *mem = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (mem == MAP_FAILED) {
|
||||
log.error("mmap fails. error = {}", sys::error_msg(sys::error_code()));
|
||||
log.error("mmap fails. error = {}", sys::error());
|
||||
::shm_unlink(name.c_str());
|
||||
return {};
|
||||
}
|
||||
@ -114,7 +114,7 @@ result_code shm_close(shm_t h) noexcept {
|
||||
}
|
||||
if (::munmap(shm->memp, shm->f_sz) == posix::failed) {
|
||||
auto ec = sys::error_code();
|
||||
log.error("munmap fails. error = {}", sys::error_msg(ec));
|
||||
log.error("munmap fails. error = {}", sys::error(ec));
|
||||
return ec;
|
||||
}
|
||||
/// @brief no unlink the file.
|
||||
|
||||
@ -30,11 +30,11 @@ inline LPSECURITY_ATTRIBUTES get_sa() {
|
||||
using namespace ::LIBIMP_;
|
||||
log::gripper log {"get_sa"};
|
||||
if (!::InitializeSecurityDescriptor(&sd_, SECURITY_DESCRIPTOR_REVISION)) {
|
||||
log.error("InitializeSecurityDescriptor fails. error = {}", sys::error_msg(sys::error_code()));
|
||||
log.error("InitializeSecurityDescriptor fails. error = {}", sys::error());
|
||||
return;
|
||||
}
|
||||
if (!::SetSecurityDescriptorDacl(&sd_, TRUE, NULL, FALSE)) {
|
||||
log.error("SetSecurityDescriptorDacl fails. error = {}", sys::error_msg(sys::error_code()));
|
||||
log.error("SetSecurityDescriptorDacl fails. error = {}", sys::error());
|
||||
return;
|
||||
}
|
||||
sa_.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
|
||||
@ -40,18 +40,21 @@ void mmap_close(HANDLE h) {
|
||||
return;
|
||||
}
|
||||
if (!::CloseHandle(h)) {
|
||||
log.error("CloseHandle fails. error = {}", sys::error_msg(sys::error_code()));
|
||||
log.error("CloseHandle fails. error = {}", sys::error());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates or opens a file mapping object for a specified file.
|
||||
*
|
||||
* @see https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-openfilemappinga
|
||||
* https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createfilemappinga
|
||||
*
|
||||
* @param file Specifies the name of the file mapping object
|
||||
* @param size Specifies the size required to create a file mapping object.
|
||||
* This size is ignored when opening an existing file mapping object
|
||||
* @param type Combinable open modes, create | open
|
||||
*
|
||||
* @return File mapping object HANDLE, NULL on error
|
||||
*/
|
||||
HANDLE mmap_open(std::string const &file, std::size_t size, mode::type type) noexcept {
|
||||
@ -65,26 +68,42 @@ HANDLE mmap_open(std::string const &file, std::size_t size, mode::type type) noe
|
||||
log.error("file name is empty. (TCHAR conversion failed)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// @brief Opens a named file mapping object.
|
||||
if (type == mode::open) {
|
||||
auto try_open = [&]() -> HANDLE {
|
||||
HANDLE h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, t_name.c_str());
|
||||
if (h == NULL) {
|
||||
log.error("OpenFileMapping fails. error = {}", sys::error_msg(sys::error_code()));
|
||||
log.error("OpenFileMapping fails. error = {}", sys::error());
|
||||
}
|
||||
return h;
|
||||
};
|
||||
|
||||
/// @brief Creates or opens a named or unnamed file mapping object for a specified file.
|
||||
auto try_create = [&]() -> HANDLE {
|
||||
HANDLE h = ::CreateFileMapping(INVALID_HANDLE_VALUE, detail::get_sa(), PAGE_READWRITE | SEC_COMMIT,
|
||||
/// @remark dwMaximumSizeHigh always 0 here.
|
||||
0, static_cast<DWORD>(size), t_name.c_str());
|
||||
if (h == NULL) {
|
||||
log.error("CreateFileMapping fails. error = {}", sys::error());
|
||||
return NULL;
|
||||
}
|
||||
return h;
|
||||
};
|
||||
|
||||
if (type == mode::open) {
|
||||
return try_open();
|
||||
} else if ((type == (mode::create | mode::open)) && (size == 0)) {
|
||||
/// @remark CreateFileMapping may returns ERROR_INVALID_PARAMETER when dwMaximumSizeLow is zero.
|
||||
/// @see CreateFileMapping (Windows CE 5.0)
|
||||
/// https://learn.microsoft.com/en-us/previous-versions/windows/embedded/aa517331(v=msdn.10)
|
||||
return try_open();
|
||||
} else if (!(type & mode::create)) {
|
||||
log.error("mode type is invalid. type = {}", type);
|
||||
return NULL;
|
||||
}
|
||||
/// @brief Creates or opens a named or unnamed file mapping object for a specified file.
|
||||
HANDLE h = ::CreateFileMapping(INVALID_HANDLE_VALUE, detail::get_sa(), PAGE_READWRITE | SEC_COMMIT,
|
||||
0, static_cast<DWORD>(size), t_name.c_str());
|
||||
if (h == NULL) {
|
||||
log.error("CreateFileMapping fails. error = {}", sys::error_msg(sys::error_code()));
|
||||
return NULL;
|
||||
}
|
||||
// If the object exists before the function call, the function returns a handle to the existing object
|
||||
// (with its current size, not the specified size), and GetLastError returns ERROR_ALREADY_EXISTS.
|
||||
HANDLE h = try_create();
|
||||
/// @remark If the object exists before the function call, the function returns a handle to the existing object
|
||||
/// (with its current size, not the specified size), and GetLastError returns ERROR_ALREADY_EXISTS.
|
||||
if ((type == mode::create) && (::GetLastError() == ERROR_ALREADY_EXISTS)) {
|
||||
mmap_close(h);
|
||||
log.info("the file being created already exists. file = {}, type = {}", file, type);
|
||||
@ -105,7 +124,7 @@ LPVOID mmap_memof(HANDLE h) {
|
||||
}
|
||||
LPVOID mem = ::MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||
if (h == NULL) {
|
||||
log.error("MapViewOfFile fails. error = {}", sys::error_msg(sys::error_code()));
|
||||
log.error("MapViewOfFile fails. error = {}", sys::error());
|
||||
return NULL;
|
||||
}
|
||||
return mem;
|
||||
@ -123,7 +142,7 @@ SIZE_T mmap_sizeof(LPCVOID mem) {
|
||||
}
|
||||
MEMORY_BASIC_INFORMATION mem_info {};
|
||||
if (::VirtualQuery(mem, &mem_info, sizeof(mem_info)) == 0) {
|
||||
log.error("VirtualQuery fails. error = {}", sys::error_msg(sys::error_code()));
|
||||
log.error("VirtualQuery fails. error = {}", sys::error());
|
||||
return 0;
|
||||
}
|
||||
return mem_info.RegionSize;
|
||||
@ -144,7 +163,7 @@ void mmap_release(HANDLE h, LPCVOID mem) {
|
||||
return;
|
||||
}
|
||||
if (!::UnmapViewOfFile(mem)) {
|
||||
log.warning("UnmapViewOfFile fails. error = {}", sys::error_msg(sys::error_code()));
|
||||
log.warning("UnmapViewOfFile fails. error = {}", sys::error());
|
||||
}
|
||||
mmap_close(h);
|
||||
}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
|
||||
#include <utility> // std::exchange, std::swap (since C++11)
|
||||
|
||||
#include "libimp/log.h"
|
||||
|
||||
#include "libimp/detect_plat.h"
|
||||
@ -10,6 +12,8 @@
|
||||
|
||||
LIBIPC_NAMESPACE_BEG_
|
||||
|
||||
/// @brief C style shared memory access interface implementation.
|
||||
|
||||
void *shm_get(shm_t h) noexcept {
|
||||
LIBIMP_LOG_();
|
||||
if (h == nullptr) {
|
||||
@ -30,7 +34,7 @@ std::size_t shm_size(shm_t h) noexcept {
|
||||
return shm->f_sz;
|
||||
}
|
||||
|
||||
std::string shm_file(shm_t h) noexcept {
|
||||
std::string shm_name(shm_t h) noexcept {
|
||||
LIBIMP_LOG_();
|
||||
if (h == nullptr) {
|
||||
log.error("shm handle is null.");
|
||||
@ -40,4 +44,63 @@ std::string shm_file(shm_t h) noexcept {
|
||||
return shm->file;
|
||||
}
|
||||
|
||||
/// @brief The shared memory object.
|
||||
|
||||
shared_memory::shared_memory() noexcept
|
||||
: shm_(nullptr) {}
|
||||
|
||||
shared_memory::~shared_memory() noexcept {
|
||||
this->close();
|
||||
}
|
||||
|
||||
shared_memory::shared_memory(shared_memory &&other) noexcept
|
||||
: shm_(std::exchange(other.shm_, nullptr)) {}
|
||||
|
||||
shared_memory &shared_memory::operator=(shared_memory &&rhs) & noexcept {
|
||||
this->shm_ = std::exchange(rhs.shm_, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void shared_memory::swap(shared_memory &other) noexcept {
|
||||
std::swap(this->shm_, other.shm_);
|
||||
}
|
||||
|
||||
shared_memory::shared_memory(std::string name, std::size_t size, mode::type type) noexcept
|
||||
: shared_memory() {
|
||||
open(std::move(name), size, type);
|
||||
}
|
||||
|
||||
bool shared_memory::open(std::string name, std::size_t size, mode::type type) noexcept {
|
||||
this->close();
|
||||
auto ret = shm_open(std::move(name), size, type);
|
||||
if (!ret) return false;
|
||||
shm_ = *ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
void shared_memory::close() noexcept {
|
||||
if (!valid()) return;
|
||||
shm_close(std::exchange(shm_, nullptr));
|
||||
}
|
||||
|
||||
bool shared_memory::valid() const noexcept {
|
||||
return this->shm_ != nullptr;
|
||||
}
|
||||
|
||||
void *shared_memory::get() const noexcept {
|
||||
return shm_get(shm_);
|
||||
}
|
||||
|
||||
void *shared_memory::operator*() const noexcept {
|
||||
return this->get();
|
||||
}
|
||||
|
||||
std::size_t shared_memory::size() const noexcept {
|
||||
return shm_size(shm_);
|
||||
}
|
||||
|
||||
std::string shared_memory::name() const noexcept {
|
||||
return shm_name(shm_);
|
||||
}
|
||||
|
||||
LIBIPC_NAMESPACE_END_
|
||||
|
||||
@ -15,16 +15,63 @@ TEST(shm, create_close) {
|
||||
EXPECT_NE(pt1, nullptr);
|
||||
*(int *)pt1 = 0;
|
||||
|
||||
auto shm2 = ipc::shm_open(ipc::shm_file(*shm1), 0, ipc::mode::open);
|
||||
auto shm2 = ipc::shm_open(ipc::shm_name(*shm1), 0, ipc::mode::create | ipc::mode::open);
|
||||
EXPECT_TRUE(shm2);
|
||||
auto shm3 = ipc::shm_open(ipc::shm_name(*shm1), 128, ipc::mode::open);
|
||||
EXPECT_TRUE(shm3);
|
||||
auto shm4 = ipc::shm_open(ipc::shm_name(*shm1), 256, ipc::mode::create | ipc::mode::open);
|
||||
EXPECT_TRUE(shm4);
|
||||
EXPECT_EQ(ipc::shm_size(*shm1), ipc::shm_size(*shm2));
|
||||
EXPECT_EQ(ipc::shm_size(*shm1), ipc::shm_size(*shm3));
|
||||
EXPECT_EQ(ipc::shm_size(*shm1), ipc::shm_size(*shm4));
|
||||
auto pt2 = ipc::shm_get(*shm1);
|
||||
EXPECT_NE(pt2, nullptr);
|
||||
EXPECT_EQ(*(int *)pt2, 0);
|
||||
*(int *)pt1 = 1234;
|
||||
EXPECT_EQ(*(int *)pt2, 1234);
|
||||
|
||||
EXPECT_TRUE(ipc::shm_close(*shm4));
|
||||
EXPECT_TRUE(ipc::shm_close(*shm3));
|
||||
EXPECT_TRUE(ipc::shm_close(*shm2));
|
||||
EXPECT_TRUE(ipc::shm_close(*shm1));
|
||||
EXPECT_FALSE(ipc::shm_close(nullptr));
|
||||
}
|
||||
|
||||
TEST(shm, shared_memory) {
|
||||
ipc::shared_memory shm;
|
||||
EXPECT_FALSE(shm.valid());
|
||||
EXPECT_EQ(shm.size(), 0);
|
||||
EXPECT_EQ(shm.name(), "");
|
||||
EXPECT_EQ(shm.get() , nullptr);
|
||||
EXPECT_EQ(*shm , nullptr);
|
||||
EXPECT_EQ(shm.as<int>(), nullptr);
|
||||
shm.close();
|
||||
|
||||
EXPECT_TRUE(shm.open("hello-ipc-shared-memory", 2048, ipc::mode::create | ipc::mode::open));
|
||||
EXPECT_TRUE(shm.valid());
|
||||
EXPECT_TRUE(shm.size() >= 2048);
|
||||
EXPECT_EQ(shm.name(), "hello-ipc-shared-memory");
|
||||
EXPECT_NE(shm.get() , nullptr);
|
||||
EXPECT_NE(*shm , nullptr);
|
||||
EXPECT_NE(shm.as<int>(), nullptr);
|
||||
*shm.as<int>() = 4321;
|
||||
|
||||
auto shm_r = ipc::shm_open(shm.name());
|
||||
ASSERT_TRUE(shm_r);
|
||||
EXPECT_EQ(*static_cast<int *>(ipc::shm_get(*shm_r)), 4321);
|
||||
|
||||
shm = ipc::shared_memory("hello-ipc-shared-memory-2", 512);
|
||||
EXPECT_TRUE(shm.valid());
|
||||
EXPECT_TRUE(shm.size() >= 512);
|
||||
EXPECT_EQ(shm.name(), "hello-ipc-shared-memory-2");
|
||||
EXPECT_NE(shm.get() , nullptr);
|
||||
EXPECT_NE(*shm , nullptr);
|
||||
EXPECT_NE(shm.as<int>(), nullptr);
|
||||
|
||||
*static_cast<int *>(ipc::shm_get(*shm_r)) = 1234;
|
||||
*shm.as<int>() = 4444;
|
||||
EXPECT_EQ(*static_cast<int *>(ipc::shm_get(*shm_r)), 1234);
|
||||
EXPECT_EQ(*shm.as<int>(), 4444);
|
||||
|
||||
EXPECT_TRUE(ipc::shm_close(*shm_r));
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user