1 /*
2 * Copyright Andrey Semashev 2016.
3 * Distributed under the Boost Software License, Version 1.0.
4 * (See accompanying file LICENSE_1_0.txt or copy at
5 * http://www.boost.org/LICENSE_1_0.txt)
6 */
7 /*!
8 * \file windows/mapped_shared_memory.cpp
9 * \author Andrey Semashev
10 * \date 13.02.2016
11 *
12 * \brief This header is the Boost.Log library implementation, see the library documentation
13 * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
14 */
15
16 #include <boost/log/detail/config.hpp>
17 #include <boost/winapi/basic_types.hpp>
18 #include <boost/winapi/handles.hpp>
19 #include <boost/winapi/dll.hpp>
20 #include <boost/winapi/file_mapping.hpp>
21 #include <boost/winapi/page_protection_flags.hpp>
22 #include <boost/winapi/get_last_error.hpp>
23 #include <windows.h> // for error codes
24 #include <cstddef>
25 #include <limits>
26 #include <string>
27 #include <sstream>
28 #include <boost/assert.hpp>
29 #include <boost/cstdint.hpp>
30 #include <boost/memory_order.hpp>
31 #include <boost/atomic/atomic.hpp>
32 #include <boost/throw_exception.hpp>
33 #include <boost/log/exceptions.hpp>
34 #include <boost/log/utility/permissions.hpp>
35 #include "windows/mapped_shared_memory.hpp"
36 #include <boost/log/detail/header.hpp>
37
38 namespace boost {
39
40 BOOST_LOG_OPEN_NAMESPACE
41
42 namespace ipc {
43
44 namespace aux {
45
46 boost::atomic< mapped_shared_memory::nt_query_section_t > mapped_shared_memory::nt_query_section(static_cast< mapped_shared_memory::nt_query_section_t >(NULL));
47
~mapped_shared_memory()48 mapped_shared_memory::~mapped_shared_memory()
49 {
50 if (m_mapped_address)
51 unmap();
52
53 if (m_handle)
54 {
55 BOOST_VERIFY(boost::winapi::CloseHandle(m_handle) != 0);
56 m_handle = NULL;
57 }
58 }
59
60 //! Creates a new file mapping for the shared memory segment or opens the existing one
create(const wchar_t * name,std::size_t size,permissions const & perms)61 void mapped_shared_memory::create(const wchar_t* name, std::size_t size, permissions const& perms)
62 {
63 BOOST_ASSERT(m_handle == NULL);
64
65 const uint64_t size64 = static_cast< uint64_t >(size);
66
67 // Unlike other create functions, this function opens the existing mapping, if one already exists
68 boost::winapi::HANDLE_ h = boost::winapi::CreateFileMappingW
69 (
70 boost::winapi::INVALID_HANDLE_VALUE_,
71 reinterpret_cast< boost::winapi::SECURITY_ATTRIBUTES_* >(perms.get_native()),
72 boost::winapi::PAGE_READWRITE_ | boost::winapi::SEC_COMMIT_,
73 static_cast< boost::winapi::DWORD_ >(size64 >> 32u),
74 static_cast< boost::winapi::DWORD_ >(size64),
75 name
76 );
77
78 boost::winapi::DWORD_ err = boost::winapi::GetLastError();
79 if (BOOST_UNLIKELY(h == NULL || err != ERROR_SUCCESS))
80 {
81 if (h != NULL)
82 boost::winapi::CloseHandle(h);
83 std::ostringstream strm;
84 strm << "Failed to create a shared memory segment of " << size << " bytes";
85 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, strm.str(), (err));
86 }
87
88 m_handle = h;
89 m_size = size;
90 }
91
92 //! Creates a new file mapping for the shared memory segment or opens the existing one. Returns \c true if the region was created and \c false if an existing one was opened.
create_or_open(const wchar_t * name,std::size_t size,permissions const & perms)93 bool mapped_shared_memory::create_or_open(const wchar_t* name, std::size_t size, permissions const& perms)
94 {
95 BOOST_ASSERT(m_handle == NULL);
96
97 const uint64_t size64 = static_cast< uint64_t >(size);
98
99 // Unlike other create functions, this function opens the existing mapping, if one already exists
100 boost::winapi::HANDLE_ h = boost::winapi::CreateFileMappingW
101 (
102 boost::winapi::INVALID_HANDLE_VALUE_,
103 reinterpret_cast< boost::winapi::SECURITY_ATTRIBUTES_* >(perms.get_native()),
104 boost::winapi::PAGE_READWRITE_ | boost::winapi::SEC_COMMIT_,
105 static_cast< boost::winapi::DWORD_ >(size64 >> 32u),
106 static_cast< boost::winapi::DWORD_ >(size64),
107 name
108 );
109
110 boost::winapi::DWORD_ err = boost::winapi::GetLastError();
111 if (BOOST_UNLIKELY(h == NULL))
112 {
113 std::ostringstream strm;
114 strm << "Failed to create or open a shared memory segment of " << size << " bytes";
115 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, strm.str(), (err));
116 }
117
118 const bool created = (err != ERROR_ALREADY_EXISTS);
119 try
120 {
121 if (created)
122 {
123 m_size = size;
124 }
125 else
126 {
127 // If an existing segment was opened, determine its size
128 m_size = obtain_size(h);
129 }
130 }
131 catch (...)
132 {
133 boost::winapi::CloseHandle(h);
134 throw;
135 }
136
137 m_handle = h;
138
139 return created;
140 }
141
142 //! Opens the existing file mapping for the shared memory segment
open(const wchar_t * name)143 void mapped_shared_memory::open(const wchar_t* name)
144 {
145 BOOST_ASSERT(m_handle == NULL);
146
147 // Note: FILE_MAP_WRITE implies reading permission as well
148 boost::winapi::HANDLE_ h = boost::winapi::OpenFileMappingW(boost::winapi::FILE_MAP_WRITE_ | boost::winapi::SECTION_QUERY_, false, name);
149
150 if (BOOST_UNLIKELY(h == NULL))
151 {
152 const boost::winapi::DWORD_ err = boost::winapi::GetLastError();
153 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to create a shared memory segment", (err));
154 }
155
156 try
157 {
158 m_size = obtain_size(h);
159 }
160 catch (...)
161 {
162 boost::winapi::CloseHandle(h);
163 throw;
164 }
165
166 m_handle = h;
167 }
168
169 //! Maps the file mapping into the current process memory
map()170 void mapped_shared_memory::map()
171 {
172 BOOST_ASSERT(m_handle != NULL);
173 BOOST_ASSERT(m_mapped_address == NULL);
174
175 // Note: FILE_MAP_WRITE implies reading permission as well
176 m_mapped_address = boost::winapi::MapViewOfFile
177 (
178 m_handle,
179 boost::winapi::FILE_MAP_WRITE_ | boost::winapi::SECTION_QUERY_,
180 0u,
181 0u,
182 m_size
183 );
184
185 if (BOOST_UNLIKELY(m_mapped_address == NULL))
186 {
187 const boost::winapi::DWORD_ err = boost::winapi::GetLastError();
188 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to map the shared memory segment into the process address space", (err));
189 }
190 }
191
192 //! Unmaps the file mapping
unmap()193 void mapped_shared_memory::unmap()
194 {
195 BOOST_ASSERT(m_mapped_address != NULL);
196
197 BOOST_VERIFY(boost::winapi::UnmapViewOfFile(m_mapped_address) != 0);
198 m_mapped_address = NULL;
199 }
200
201 //! Returns the size of the file mapping identified by the handle
obtain_size(boost::winapi::HANDLE_ h)202 std::size_t mapped_shared_memory::obtain_size(boost::winapi::HANDLE_ h)
203 {
204 nt_query_section_t query_section = nt_query_section.load(boost::memory_order_acquire);
205
206 if (BOOST_UNLIKELY(query_section == NULL))
207 {
208 // Check if ntdll.dll provides NtQuerySection, see: http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FSection%2FNtQuerySection.html
209 boost::winapi::HMODULE_ ntdll = boost::winapi::GetModuleHandleW(L"ntdll.dll");
210 if (BOOST_UNLIKELY(ntdll == NULL))
211 {
212 const boost::winapi::DWORD_ err = boost::winapi::GetLastError();
213 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to obtain a handle to ntdll.dll", (err));
214 }
215
216 query_section = (nt_query_section_t)boost::winapi::get_proc_address(ntdll, "NtQuerySection");
217 if (BOOST_UNLIKELY(query_section == NULL))
218 {
219 const boost::winapi::DWORD_ err = boost::winapi::GetLastError();
220 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to obtain the NtQuerySection function", (err));
221 }
222
223 nt_query_section.store(query_section, boost::memory_order_release);
224 }
225
226 section_basic_information info = {};
227 boost::winapi::NTSTATUS_ err = query_section
228 (
229 h,
230 0u, // SectionBasicInformation
231 &info,
232 sizeof(info),
233 NULL
234 );
235 if (BOOST_UNLIKELY(err != 0u))
236 {
237 BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to test obtain size of the shared memory segment", (ERROR_INVALID_HANDLE));
238 }
239
240 return static_cast< std::size_t >(info.section_size.QuadPart);
241 }
242
243 } // namespace aux
244
245 } // namespace ipc
246
247 BOOST_LOG_CLOSE_NAMESPACE // namespace log
248
249 } // namespace boost
250
251 #include <boost/log/detail/footer.hpp>
252