• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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