add: [ipc] shared_memory object

This commit is contained in:
mutouyun 2022-10-30 16:09:02 +08:00
parent af654d5769
commit d55d9f549f
6 changed files with 185 additions and 25 deletions

View File

@ -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. /// @brief Gets the name of the shared memory file based on the shared memory handle.
/// @return empty string on failure. /// @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_ LIBIPC_NAMESPACE_END_

View File

@ -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_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH); S_IROTH | S_IWOTH);
if (fd == posix::failed) { 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 {}; 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 /// @brief Try to get the size of this fd
struct stat st; struct stat st;
if (::fstat(fd, &st) == posix::failed) { 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()); ::shm_unlink(name.c_str());
return {}; return {};
} }
size = static_cast<std::size_t>(st.st_size); size = static_cast<std::size_t>(st.st_size);
/// @brief Truncate this fd to a specified length /// @brief Truncate this fd to a specified length
} else if (::ftruncate(fd, size) != 0) { } 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()); ::shm_unlink(name.c_str());
return {}; 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. /// @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); void *mem = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mem == MAP_FAILED) { 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()); ::shm_unlink(name.c_str());
return {}; return {};
} }
@ -114,7 +114,7 @@ result_code shm_close(shm_t h) noexcept {
} }
if (::munmap(shm->memp, shm->f_sz) == posix::failed) { if (::munmap(shm->memp, shm->f_sz) == posix::failed) {
auto ec = sys::error_code(); auto ec = sys::error_code();
log.error("munmap fails. error = {}", sys::error_msg(ec)); log.error("munmap fails. error = {}", sys::error(ec));
return ec; return ec;
} }
/// @brief no unlink the file. /// @brief no unlink the file.

View File

@ -30,11 +30,11 @@ inline LPSECURITY_ATTRIBUTES get_sa() {
using namespace ::LIBIMP_; using namespace ::LIBIMP_;
log::gripper log {"get_sa"}; log::gripper log {"get_sa"};
if (!::InitializeSecurityDescriptor(&sd_, SECURITY_DESCRIPTOR_REVISION)) { 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; return;
} }
if (!::SetSecurityDescriptorDacl(&sd_, TRUE, NULL, FALSE)) { 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; return;
} }
sa_.nLength = sizeof(SECURITY_ATTRIBUTES); sa_.nLength = sizeof(SECURITY_ATTRIBUTES);

View File

@ -40,18 +40,21 @@ void mmap_close(HANDLE h) {
return; return;
} }
if (!::CloseHandle(h)) { 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. * @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 * @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 * https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createfilemappinga
*
* @param file Specifies the name of the file mapping object * @param file Specifies the name of the file mapping object
* @param size Specifies the size required to create a 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 * This size is ignored when opening an existing file mapping object
* @param type Combinable open modes, create | open * @param type Combinable open modes, create | open
*
* @return File mapping object HANDLE, NULL on error * @return File mapping object HANDLE, NULL on error
*/ */
HANDLE mmap_open(std::string const &file, std::size_t size, mode::type type) noexcept { 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)"); log.error("file name is empty. (TCHAR conversion failed)");
return NULL; return NULL;
} }
/// @brief Opens a named file mapping object. /// @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()); HANDLE h = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, t_name.c_str());
if (h == NULL) { if (h == NULL) {
log.error("OpenFileMapping fails. error = {}", sys::error_msg(sys::error_code())); log.error("OpenFileMapping fails. error = {}", sys::error());
} }
return h; 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)) { } else if (!(type & mode::create)) {
log.error("mode type is invalid. type = {}", type); log.error("mode type is invalid. type = {}", type);
return NULL; return NULL;
} }
/// @brief Creates or opens a named or unnamed file mapping object for a specified file. HANDLE h = try_create();
HANDLE h = ::CreateFileMapping(INVALID_HANDLE_VALUE, detail::get_sa(), PAGE_READWRITE | SEC_COMMIT, /// @remark If the object exists before the function call, the function returns a handle to the existing object
0, static_cast<DWORD>(size), t_name.c_str()); /// (with its current size, not the specified size), and GetLastError returns ERROR_ALREADY_EXISTS.
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.
if ((type == mode::create) && (::GetLastError() == ERROR_ALREADY_EXISTS)) { if ((type == mode::create) && (::GetLastError() == ERROR_ALREADY_EXISTS)) {
mmap_close(h); mmap_close(h);
log.info("the file being created already exists. file = {}, type = {}", file, type); 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); LPVOID mem = ::MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (h == NULL) { if (h == NULL) {
log.error("MapViewOfFile fails. error = {}", sys::error_msg(sys::error_code())); log.error("MapViewOfFile fails. error = {}", sys::error());
return NULL; return NULL;
} }
return mem; return mem;
@ -123,7 +142,7 @@ SIZE_T mmap_sizeof(LPCVOID mem) {
} }
MEMORY_BASIC_INFORMATION mem_info {}; MEMORY_BASIC_INFORMATION mem_info {};
if (::VirtualQuery(mem, &mem_info, sizeof(mem_info)) == 0) { 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 0;
} }
return mem_info.RegionSize; return mem_info.RegionSize;
@ -144,7 +163,7 @@ void mmap_release(HANDLE h, LPCVOID mem) {
return; return;
} }
if (!::UnmapViewOfFile(mem)) { if (!::UnmapViewOfFile(mem)) {
log.warning("UnmapViewOfFile fails. error = {}", sys::error_msg(sys::error_code())); log.warning("UnmapViewOfFile fails. error = {}", sys::error());
} }
mmap_close(h); mmap_close(h);
} }

View File

@ -1,4 +1,6 @@
#include <utility> // std::exchange, std::swap (since C++11)
#include "libimp/log.h" #include "libimp/log.h"
#include "libimp/detect_plat.h" #include "libimp/detect_plat.h"
@ -10,6 +12,8 @@
LIBIPC_NAMESPACE_BEG_ LIBIPC_NAMESPACE_BEG_
/// @brief C style shared memory access interface implementation.
void *shm_get(shm_t h) noexcept { void *shm_get(shm_t h) noexcept {
LIBIMP_LOG_(); LIBIMP_LOG_();
if (h == nullptr) { if (h == nullptr) {
@ -30,7 +34,7 @@ std::size_t shm_size(shm_t h) noexcept {
return shm->f_sz; return shm->f_sz;
} }
std::string shm_file(shm_t h) noexcept { std::string shm_name(shm_t h) noexcept {
LIBIMP_LOG_(); LIBIMP_LOG_();
if (h == nullptr) { if (h == nullptr) {
log.error("shm handle is null."); log.error("shm handle is null.");
@ -40,4 +44,63 @@ std::string shm_file(shm_t h) noexcept {
return shm->file; 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_ LIBIPC_NAMESPACE_END_

View File

@ -15,16 +15,63 @@ TEST(shm, create_close) {
EXPECT_NE(pt1, nullptr); EXPECT_NE(pt1, nullptr);
*(int *)pt1 = 0; *(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); 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(*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); auto pt2 = ipc::shm_get(*shm1);
EXPECT_NE(pt2, nullptr); EXPECT_NE(pt2, nullptr);
EXPECT_EQ(*(int *)pt2, 0); EXPECT_EQ(*(int *)pt2, 0);
*(int *)pt1 = 1234; *(int *)pt1 = 1234;
EXPECT_EQ(*(int *)pt2, 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(*shm2));
EXPECT_TRUE(ipc::shm_close(*shm1)); EXPECT_TRUE(ipc::shm_close(*shm1));
EXPECT_FALSE(ipc::shm_close(nullptr)); 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));
}