mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-07 01:06: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
|
# QtCreator CMake
|
||||||
CMakeLists.txt.user*
|
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