first commit for shm.

This commit is contained in:
mutouyun 2018-11-20 11:27:39 +08:00
parent 8ca526626f
commit 050bb02198
11 changed files with 510 additions and 0 deletions

3
.gitignore vendored
View File

@ -41,3 +41,6 @@ target_wrapper.*
# QtCreator CMake # QtCreator CMake
CMakeLists.txt.user* CMakeLists.txt.user*
# My output files
output

42
build/src.pro Normal file
View File

@ -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

21
build/test.pro Normal file
View File

@ -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

52
include/export.h Normal file
View File

@ -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*/

46
include/shm.h Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include <string>
#include <cstddef>
#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

View File

@ -0,0 +1,50 @@
#include "shm.h"
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
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

72
src/platform/shm_win.cpp Normal file
View File

@ -0,0 +1,72 @@
#include "shm.h"
#include <windows.h>
#include <type_traits>
#include <locale>
#include <codecvt>
namespace {
template <typename T = TCHAR>
constexpr auto to_tchar(std::string const & str)
-> std::enable_if_t<std::is_same<std::string::value_type, T>::value, std::string const &> {
return str;
}
template <typename T = TCHAR>
inline auto to_tchar(std::string const & str)
-> std::enable_if_t<std::is_same<std::wstring::value_type, T>::value, std::wstring> {
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> 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<DWORD>(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

95
src/shm.cpp Normal file
View File

@ -0,0 +1,95 @@
#include "shm.h"
#include <utility>
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

27
test/main.cpp Normal file
View File

@ -0,0 +1,27 @@
#include <QCoreApplication>
#include <QObject>
#include <QVector>
#include "test.h"
namespace {
QVector<QObject*> 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;
}

11
test/test.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include <QtTest>
class TestSuite : public QObject
{
Q_OBJECT
public:
explicit TestSuite(void);
};

91
test/test_shm.cpp Normal file
View File

@ -0,0 +1,91 @@
#include <cstring>
#include <cstdint>
#include <thread>
#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