mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 08:46:45 +08:00
first commit for shm.
This commit is contained in:
parent
8ca526626f
commit
050bb02198
3
.gitignore
vendored
3
.gitignore
vendored
@ -41,3 +41,6 @@ target_wrapper.*
|
||||
|
||||
# QtCreator CMake
|
||||
CMakeLists.txt.user*
|
||||
|
||||
# My output files
|
||||
output
|
||||
|
||||
42
build/src.pro
Normal file
42
build/src.pro
Normal 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
21
build/test.pro
Normal 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
52
include/export.h
Normal 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
46
include/shm.h
Normal 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
|
||||
50
src/platform/shm_linux.cpp
Normal file
50
src/platform/shm_linux.cpp
Normal 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
72
src/platform/shm_win.cpp
Normal 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
95
src/shm.cpp
Normal 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
27
test/main.cpp
Normal 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
11
test/test.h
Normal 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
91
test/test_shm.cpp
Normal 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
|
||||
Loading…
x
Reference in New Issue
Block a user