aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--include/winapi/shmem.hpp125
-rw-r--r--src/shmem.cpp88
-rw-r--r--test/unit_tests/shmem.cpp72
3 files changed, 285 insertions, 0 deletions
diff --git a/include/winapi/shmem.hpp b/include/winapi/shmem.hpp
new file mode 100644
index 0000000..0fb2b76
--- /dev/null
+++ b/include/winapi/shmem.hpp
@@ -0,0 +1,125 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#pragma once
+
+#include "handle.hpp"
+
+#include <boost/config.hpp>
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+namespace winapi {
+
+class SharedMemory {
+public:
+ static SharedMemory create(const std::string& name, std::size_t nb);
+ static SharedMemory open(const std::string& name);
+
+ // VS 2013 won't generate these automatically:
+ SharedMemory(SharedMemory&&) BOOST_NOEXCEPT_OR_NOTHROW;
+ SharedMemory& operator=(SharedMemory) BOOST_NOEXCEPT_OR_NOTHROW;
+ void swap(SharedMemory&) BOOST_NOEXCEPT_OR_NOTHROW;
+ SharedMemory(const SharedMemory&) = delete;
+
+ void* get() const { return m_addr.get(); }
+ void* ptr() const { return get(); }
+
+private:
+ struct Unmap {
+ void operator()(void*) const;
+ };
+
+ SharedMemory() = default;
+
+ SharedMemory(Handle&& handle, void* addr) : m_handle{std::move(handle)}, m_addr{addr} {}
+
+ Handle m_handle;
+ std::unique_ptr<void, Unmap> m_addr;
+};
+
+inline void swap(SharedMemory& a, SharedMemory& b) BOOST_NOEXCEPT_OR_NOTHROW {
+ a.swap(b);
+}
+
+template <typename T>
+class SharedObject {
+public:
+ typedef typename std::aligned_storage<sizeof(T), __alignof(T)>::type AlignedType;
+
+ template <typename... Args>
+ static SharedObject create(const std::string& name, Args&&... args) {
+ SharedObject obj{SharedMemory::create(name, sizeof(AlignedType))};
+ new (obj.ptr()) T(std::forward<Args>(args)...);
+ obj.m_destruct = true;
+ return obj;
+ }
+
+ static SharedObject open(const std::string& name) {
+ SharedObject obj{SharedMemory::open(name)};
+ return obj;
+ }
+
+ SharedObject(SharedObject&& other) BOOST_NOEXCEPT_OR_NOTHROW
+ : m_shmem{std::move(other.m_shmem)},
+ m_destruct{other.m_destruct} {}
+
+ SharedObject& operator=(SharedObject other) BOOST_NOEXCEPT_OR_NOTHROW {
+ swap(other);
+ return *this;
+ }
+
+ ~SharedObject() {
+ if (m_destruct && ptr()) {
+ ptr()->~T();
+ }
+ }
+
+ void swap(SharedObject& other) BOOST_NOEXCEPT_OR_NOTHROW {
+ using std::swap;
+ swap(m_shmem, other.m_shmem);
+ swap(m_destruct, other.m_destruct);
+ }
+
+ T* ptr() const { return reinterpret_cast<T*>(m_shmem.ptr()); }
+ T& get() const { return *ptr(); }
+
+ T* operator->() const { return ptr(); }
+ T& operator*() const { return get(); }
+
+private:
+ explicit SharedObject(SharedMemory&& shmem) : m_shmem{std::move(shmem)} {}
+
+ SharedMemory m_shmem;
+ // Destruct only once, no matter the number of mappings.
+ bool m_destruct = false;
+
+ SharedObject(const SharedObject&) = delete;
+};
+
+template <typename T>
+inline void swap(SharedObject<T>& a, SharedObject<T>& b) BOOST_NOEXCEPT_OR_NOTHROW {
+ a.swap(b);
+}
+
+} // namespace winapi
+
+namespace std {
+
+template <>
+inline void swap(winapi::SharedMemory& a, winapi::SharedMemory& b) BOOST_NOEXCEPT_OR_NOTHROW {
+ a.swap(b);
+}
+
+template <typename T>
+inline void swap(winapi::SharedObject<T>& a, winapi::SharedObject<T>& b) BOOST_NOEXCEPT_OR_NOTHROW {
+ a.swap(b);
+}
+
+} // namespace std
diff --git a/src/shmem.cpp b/src/shmem.cpp
new file mode 100644
index 0000000..552351d
--- /dev/null
+++ b/src/shmem.cpp
@@ -0,0 +1,88 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#include <winapi/error.hpp>
+#include <winapi/handle.hpp>
+#include <winapi/shmem.hpp>
+#include <winapi/utf8.hpp>
+#include <winapi/utils.hpp>
+
+#include <boost/config.hpp>
+
+#include <windows.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <utility>
+
+namespace winapi {
+namespace {
+
+void* do_map(const Handle& mapping, std::size_t nb = 0) {
+ const auto addr = ::MapViewOfFile(static_cast<HANDLE>(mapping), FILE_MAP_ALL_ACCESS, 0, 0, nb);
+
+ if (addr == NULL) {
+ throw error::windows(GetLastError(), "MapViewOfFile");
+ }
+
+ return addr;
+}
+
+} // namespace
+
+void SharedMemory::Unmap::operator()(void* ptr) const {
+ const auto ret = ::UnmapViewOfFile(ptr);
+ assert(ret);
+ WINAPI_UNUSED_PARAMETER(ret);
+};
+
+SharedMemory SharedMemory::create(const std::string& name, std::size_t nb) {
+ const auto nb64 = static_cast<std::uint64_t>(nb);
+ static_assert(sizeof(nb64) == 2 * sizeof(DWORD), "sizeof(DWORD) != 32");
+
+ const auto nb_low = static_cast<DWORD>(nb64);
+ const auto nb_high = static_cast<DWORD>(nb64 >> 32);
+
+ const auto h_mapping = ::CreateFileMappingW(
+ INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, nb_high, nb_low, widen(name).c_str());
+
+ if (h_mapping == NULL) {
+ throw error::windows(GetLastError(), "CreateFileMappingW");
+ }
+
+ Handle mapping{h_mapping};
+ const auto addr = do_map(mapping);
+ return {std::move(mapping), addr};
+}
+
+SharedMemory SharedMemory::open(const std::string& name) {
+ const auto h_mapping = ::OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, widen(name).c_str());
+
+ if (h_mapping == NULL) {
+ throw error::windows(GetLastError(), "OpenFileMappingW");
+ }
+
+ Handle mapping{h_mapping};
+ const auto addr = do_map(mapping);
+ return {std::move(mapping), addr};
+}
+
+SharedMemory::SharedMemory(SharedMemory&& other) BOOST_NOEXCEPT_OR_NOTHROW {
+ swap(other);
+}
+
+SharedMemory& SharedMemory::operator=(SharedMemory other) BOOST_NOEXCEPT_OR_NOTHROW {
+ swap(other);
+ return *this;
+}
+
+void SharedMemory::swap(SharedMemory& other) BOOST_NOEXCEPT_OR_NOTHROW {
+ using std::swap;
+ swap(m_handle, other.m_handle);
+ swap(m_addr, other.m_addr);
+}
+
+} // namespace winapi
diff --git a/test/unit_tests/shmem.cpp b/test/unit_tests/shmem.cpp
new file mode 100644
index 0000000..26f1389
--- /dev/null
+++ b/test/unit_tests/shmem.cpp
@@ -0,0 +1,72 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#include <winapi/shmem.hpp>
+
+#include <boost/config.hpp>
+#include <boost/test/unit_test.hpp>
+
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+using namespace winapi;
+
+namespace {
+
+BOOST_CONSTEXPR_OR_CONST auto shmem_name = "test-data-struct";
+
+BOOST_CONSTEXPR_OR_CONST int main_data = -1;
+BOOST_CONSTEXPR_OR_CONST int setter1_data = 69;
+BOOST_CONSTEXPR_OR_CONST int setter2_data = 420;
+
+struct DataStruct {
+ std::mutex mtx;
+ std::condition_variable cv;
+ int data;
+};
+
+void setter1_main() {
+ const auto data_struct = SharedObject<DataStruct>::open(shmem_name);
+ std::unique_lock<std::mutex> lck{data_struct->mtx};
+ BOOST_TEST_MESSAGE(data_struct.ptr());
+
+ data_struct->cv.wait(lck, [&]() { return data_struct->data == main_data; });
+ data_struct->data = setter1_data;
+ data_struct->cv.notify_all();
+}
+
+void setter2_main() {
+ auto data_struct = SharedObject<DataStruct>::open(shmem_name);
+ std::unique_lock<std::mutex> lck{data_struct->mtx};
+ BOOST_TEST_MESSAGE(data_struct.ptr());
+
+ data_struct->cv.wait(lck, [&]() { return data_struct->data == setter1_data; });
+ data_struct->data = setter2_data;
+ data_struct->cv.notify_all();
+}
+
+} // namespace
+
+BOOST_AUTO_TEST_SUITE(shmem_tests)
+
+BOOST_AUTO_TEST_CASE(basic) {
+ auto data_struct = SharedObject<DataStruct>::create(shmem_name);
+
+ std::thread setter1{&setter1_main};
+ std::thread setter2{&setter2_main};
+ {
+ std::lock_guard<std::mutex> lck{data_struct->mtx};
+ data_struct->data = main_data;
+ }
+ data_struct->cv.notify_all();
+
+ setter1.join();
+ setter2.join();
+
+ BOOST_TEST(data_struct->data == setter2_data);
+}
+
+BOOST_AUTO_TEST_SUITE_END()