From 050bb0219814cc041ef516b4bfc596e61684650d Mon Sep 17 00:00:00 2001 From: mutouyun Date: Tue, 20 Nov 2018 11:27:39 +0800 Subject: [PATCH] first commit for shm. --- .gitignore | 3 ++ build/src.pro | 42 +++++++++++++++++ build/test.pro | 21 +++++++++ include/export.h | 52 +++++++++++++++++++++ include/shm.h | 46 ++++++++++++++++++ src/platform/shm_linux.cpp | 50 ++++++++++++++++++++ src/platform/shm_win.cpp | 72 +++++++++++++++++++++++++++++ src/shm.cpp | 95 ++++++++++++++++++++++++++++++++++++++ test/main.cpp | 27 +++++++++++ test/test.h | 11 +++++ test/test_shm.cpp | 91 ++++++++++++++++++++++++++++++++++++ 11 files changed, 510 insertions(+) create mode 100644 build/src.pro create mode 100644 build/test.pro create mode 100644 include/export.h create mode 100644 include/shm.h create mode 100644 src/platform/shm_linux.cpp create mode 100644 src/platform/shm_win.cpp create mode 100644 src/shm.cpp create mode 100644 test/main.cpp create mode 100644 test/test.h create mode 100644 test/test_shm.cpp diff --git a/.gitignore b/.gitignore index 5291a38..a3ee2cf 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ target_wrapper.* # QtCreator CMake CMakeLists.txt.user* + +# My output files +output diff --git a/build/src.pro b/build/src.pro new file mode 100644 index 0000000..0d45823 --- /dev/null +++ b/build/src.pro @@ -0,0 +1,42 @@ +TEMPLATE = lib +TARGET = ipc + +CONFIG += c++14 +CONFIG -= qt + +DEFINES += __IPC_LIBRARY__ +DESTDIR = ../output + +INCLUDEPATH += \ + ../include \ + ../src \ + ../src/platform + +HEADERS += \ + ../include/export.h \ + ../include/shm.h \ + ../test/test.h + +SOURCES += \ + ../src/shm.cpp + +unix { + +SOURCES += \ + ../src/platform/shm_linux.cpp + +LIBS += -lrt + +target.path = /usr/lib +INSTALLS += target + +} # unix + +else:win32 { + +SOURCES += \ + ../src/platform/shm_win.cpp + +LIBS += -lKernel32 + +} # else:win32 diff --git a/build/test.pro b/build/test.pro new file mode 100644 index 0000000..8cfe5d7 --- /dev/null +++ b/build/test.pro @@ -0,0 +1,21 @@ +TEMPLATE = app + +QT += core testlib +CONFIG += console c++14 +CONFIG -= app_bundle + +DESTDIR = ../output + +INCLUDEPATH += \ + ../include \ + ../src \ + ../src/platform + +HEADERS += \ + ../test/test.h + +SOURCES += \ + ../test/main.cpp \ + ../test/test_shm.cpp + +LIBS += -L$${DESTDIR} -lipc diff --git a/include/export.h b/include/export.h new file mode 100644 index 0000000..962708b --- /dev/null +++ b/include/export.h @@ -0,0 +1,52 @@ +#pragma once + +#if defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT) + +# define IPC_DECL_EXPORT Q_DECL_EXPORT +# define IPC_DECL_IMPORT Q_DECL_IMPORT + +#else // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT) + +/* + * Compiler & system detection for IPC_DECL_EXPORT & IPC_DECL_IMPORT. + * Not using QtCore cause it shouldn't depend on Qt & C++. +*/ + +#if defined(_MSC_VER) +# define IPC_DECL_EXPORT __declspec(dllexport) +# define IPC_DECL_IMPORT __declspec(dllimport) +#elif defined(__ARMCC__) || defined(__CC_ARM) +# if defined(ANDROID) || defined(__linux__) || defined(__linux) +# define IPC_DECL_EXPORT __attribute__((visibility("default"))) +# define IPC_DECL_IMPORT __attribute__((visibility("default"))) +# else +# define IPC_DECL_EXPORT __declspec(dllexport) +# define IPC_DECL_IMPORT __declspec(dllimport) +# endif +#elif defined(__GNUC__) +# if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ + defined(WIN64) || defined(_WIN64) || defined(__WIN64__) +# define IPC_DECL_EXPORT __declspec(dllexport) +# define IPC_DECL_IMPORT __declspec(dllimport) +# else +# define IPC_DECL_EXPORT __attribute__((visibility("default"))) +# define IPC_DECL_IMPORT __attribute__((visibility("default"))) +# endif +#else +# define IPC_DECL_EXPORT __attribute__((visibility("default"))) +# define IPC_DECL_IMPORT __attribute__((visibility("default"))) +#endif + +#endif // defined(Q_DECL_EXPORT) && defined(Q_DECL_IMPORT) + +/* + * Define IPC_EXPORT for exporting function & class. +*/ + +#ifndef IPC_EXPORT +#if defined(__IPC_LIBRARY__) +# define IPC_EXPORT IPC_DECL_EXPORT +#else +# define IPC_EXPORT IPC_DECL_IMPORT +#endif +#endif /*IPC_EXPORT*/ diff --git a/include/shm.h b/include/shm.h new file mode 100644 index 0000000..1bbca4e --- /dev/null +++ b/include/shm.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#include "export.h" + +namespace ipc { +namespace shm { + +using handle_t = void*; + +IPC_EXPORT handle_t acquire(std::string const & name, std::size_t size); +IPC_EXPORT void release(handle_t h, std::size_t size); +IPC_EXPORT void* open (handle_t h); +IPC_EXPORT void close (void* mem); + +class handle_; +class IPC_EXPORT handle { +public: + handle(void); + handle(std::string const & name, std::size_t size); + handle(handle&& rhs); + + ~handle(void); + + void swap(handle& rhs); + handle& operator=(handle rhs); + + bool valid(void) const; + std::size_t size(void) const; + std::string const & name(void) const; + + bool acquire(std::string const & name, std::size_t size); + void release(void); + + void* get (void); + void close(void); + +private: + friend class handle_; + handle_* p_; +}; + +} // namespace shm +} // namespace ipc diff --git a/src/platform/shm_linux.cpp b/src/platform/shm_linux.cpp new file mode 100644 index 0000000..8d0845b --- /dev/null +++ b/src/platform/shm_linux.cpp @@ -0,0 +1,50 @@ +#include "shm.h" + +#include +#include +#include +#include +#include + +namespace ipc { +namespace shm { + +handle_t acquire(std::string const & name, std::size_t size) { + if (name.empty() || size == 0) { + return nullptr; + } + int fd = ::shm_open(name.c_str(), O_CREAT | O_RDWR, + S_IRUSR | S_IWUSR | + S_IRGRP | S_IWGRP | + S_IROTH | S_IWOTH); + if (fd == -1) { + return nullptr; + } + if (::ftruncate(fd, size) != 0) { + ::close(fd); + return nullptr; + } + void* mem = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + ::close(fd); + if (mem == MAP_FAILED) { + return nullptr; + } + return mem; +} + +void release(handle_t h, std::size_t size) { + if (h == nullptr) { + return; + } + ::munmap(h, size); +} + +void* open(handle_t h) { + return h; +} + +void close(void* /*mem*/) { +} + +} // namespace shm +} // namespace ipc diff --git a/src/platform/shm_win.cpp b/src/platform/shm_win.cpp new file mode 100644 index 0000000..c5082b2 --- /dev/null +++ b/src/platform/shm_win.cpp @@ -0,0 +1,72 @@ +#include "shm.h" + +#include + +#include +#include +#include + +namespace { + +template +constexpr auto to_tchar(std::string const & str) + -> std::enable_if_t::value, std::string const &> { + return str; +} + +template +inline auto to_tchar(std::string const & str) + -> std::enable_if_t::value, std::wstring> { + std::wstring_convert> converter; + return converter.from_bytes(str); +} + +} // internal-linkage + +namespace ipc { +namespace shm { + +handle_t acquire(std::string const & name, std::size_t size) { + if (name.empty() || size == 0) { + return nullptr; + } + HANDLE h = ::CreateFileMapping( + INVALID_HANDLE_VALUE, + NULL, + PAGE_READWRITE | SEC_COMMIT, + 0, static_cast(size), + to_tchar("__SHM__" + name).c_str() + ); + if (h == NULL) { + return nullptr; + } + return h; +} + +void release(handle_t h, std::size_t /*size*/) { + if (h == nullptr) { + return; + } + ::CloseHandle(h); +} + +void* open(handle_t h) { + if (h == nullptr) { + return nullptr; + } + LPVOID mem = ::MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, 0); + if (mem == NULL) { + return nullptr; + } + return mem; +} + +void close(void* mem) { + if (mem == nullptr) { + return; + } + ::UnmapViewOfFile(mem); +} + +} // namespace shm +} // namespace ipc diff --git a/src/shm.cpp b/src/shm.cpp new file mode 100644 index 0000000..ac694dc --- /dev/null +++ b/src/shm.cpp @@ -0,0 +1,95 @@ +#include "shm.h" + +#include + +namespace ipc { +namespace shm { + +class handle_ { +public: + handle* t_ = nullptr; + handle_t h_ = nullptr; + void* m_ = nullptr; + + std::string n_; + std::size_t s_ = 0; + + ~handle_(void) { + t_->close(); + t_->release(); + } +}; + +handle::handle(void) + : p_(new handle_) { + p_->t_ = this; +} + +handle::handle(std::string const & name, std::size_t size) + : handle() { + acquire(name, size); +} + +handle::handle(handle&& rhs) + : handle() { + swap(rhs); +} + +handle::~handle(void) { + delete p_; +} + +void handle::swap(handle& rhs) { + std::swap(p_, rhs.p_); +} + +handle& handle::operator=(handle rhs) { + swap(rhs); + return *this; +} + +bool handle::valid(void) const { + return (p_ != nullptr) && (p_->h_ != nullptr); +} + +std::size_t handle::size(void) const { + return (p_ == nullptr) ? 0 : p_->s_; +} + +std::string const & handle::name(void) const { + static const std::string dummy; + return (p_ == nullptr) ? dummy : p_->n_; +} + +bool handle::acquire(std::string const & name, std::size_t size) { + if (p_ == nullptr) return false; + close(); + release(); + p_->h_ = shm::acquire(p_->n_ = name, p_->s_ = size); + return valid(); +} + +void handle::release(void) { + if (!valid()) return; + shm::release(p_->h_, p_->s_); + p_->h_ = nullptr; + p_->s_ = 0; + p_->n_.clear(); +} + +void* handle::get(void) { + if (!valid()) return nullptr; + if (p_->m_ == nullptr) { + return p_->m_ = shm::open(p_->h_); + } + else return p_->m_; +} + +void handle::close(void) { + if (!valid()) return; + shm::close(p_->m_); + p_->m_ = nullptr; +} + +} // namespace shm +} // namespace ipc diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..f55234a --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +#include "test.h" + +namespace { + +QVector suites__; + +} // internal-linkage + +TestSuite::TestSuite(void) { + suites__ << this; +} + +int main(int argc, char* argv[]) { + QCoreApplication app(argc, argv); + Q_UNUSED(app) + + int failed_count = 0; + for (const auto& suite : suites__) { + if (QTest::qExec(suite, argc, argv) != 0) + ++failed_count; + } + return failed_count; +} diff --git a/test/test.h b/test/test.h new file mode 100644 index 0000000..260b2df --- /dev/null +++ b/test/test.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +class TestSuite : public QObject +{ + Q_OBJECT + +public: + explicit TestSuite(void); +}; diff --git a/test/test_shm.cpp b/test/test_shm.cpp new file mode 100644 index 0000000..6f2d722 --- /dev/null +++ b/test/test_shm.cpp @@ -0,0 +1,91 @@ +#include +#include +#include + +#include "shm.h" +#include "test.h" + +using namespace ipc::shm; + +namespace { + +class Unit : public TestSuite { + Q_OBJECT + +private slots: + void test_acquire(void); + void test_release(void); + void test_get(void); + void test_hello(void); + void test_mt(void); +} unit__; + +#include "test_shm.moc" + +handle shm_hd__; + +void Unit::test_acquire(void) { + QVERIFY(!shm_hd__.valid()); + + QVERIFY(shm_hd__.acquire("my-test-1", 1024)); + QVERIFY(shm_hd__.size() == 1024); + QCOMPARE(shm_hd__.name().c_str(), "my-test-1"); + + QVERIFY(shm_hd__.acquire("my-test-2", 2048)); + QVERIFY(shm_hd__.size() == 2048); + QCOMPARE(shm_hd__.name().c_str(), "my-test-2"); + + QVERIFY(shm_hd__.acquire("my-test-3", 4096)); + QVERIFY(shm_hd__.size() == 4096); + QCOMPARE(shm_hd__.name().c_str(), "my-test-3"); +} + +void Unit::test_release(void) { + QVERIFY(shm_hd__.valid()); + shm_hd__.release(); + QVERIFY(!shm_hd__.valid()); +} + +void Unit::test_get(void) { + QVERIFY(shm_hd__.get() == nullptr); + QVERIFY(shm_hd__.acquire("my-test", 1024)); + + auto mem = shm_hd__.get(); + QVERIFY(mem != nullptr); + QVERIFY(mem == shm_hd__.get()); + + std::uint8_t buf[1024]; + memset(buf, 0, sizeof(buf)); + QVERIFY(memcmp(mem, buf, sizeof(buf)) == 0); + + shm_hd__.close(); + QVERIFY(mem == shm_hd__.get()); +} + +void Unit::test_hello(void) { + auto mem = shm_hd__.get(); + QVERIFY(mem != nullptr); + + constexpr char hello[] = "hello!"; + std::memcpy(mem, hello, sizeof(hello)); + shm_hd__.close(); + QCOMPARE((char*)shm_hd__.get(), hello); +} + +void Unit::test_mt(void) { + std::thread { + [] { + handle shm_mt(shm_hd__.name(), shm_hd__.size()); + + shm_hd__.close(); + shm_hd__.release(); + + constexpr char hello[] = "hello!"; + QCOMPARE((char*)shm_mt.get(), hello); + } + }.join(); + QVERIFY(shm_hd__.get() == nullptr); + QVERIFY(!shm_hd__.valid()); +} + +} // internal-linkage