1 //////////////////////////////////////////////////////////////////////////////
3 // (C) Copyright Ion Gaztanaga 2005-2011. Distributed under the Boost
4 // Software License, Version 1.0. (See accompanying file
5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 // See http://www.boost.org/libs/interprocess for documentation.
9 //////////////////////////////////////////////////////////////////////////////
11 #ifndef BOOST_INTERPROCESS_MAPPED_REGION_HPP
12 #define BOOST_INTERPROCESS_MAPPED_REGION_HPP
14 #include <boost/interprocess/detail/config_begin.hpp>
15 #include <boost/interprocess/detail/workaround.hpp>
17 #include <boost/interprocess/interprocess_fwd.hpp>
18 #include <boost/interprocess/exceptions.hpp>
19 #include <boost/move/move.hpp>
20 #include <boost/interprocess/detail/utilities.hpp>
21 #include <boost/interprocess/detail/os_file_functions.hpp>
23 #include <boost/cstdint.hpp>
25 #if defined (BOOST_INTERPROCESS_WINDOWS)
26 # include <boost/interprocess/detail/win32_api.hpp>
27 # include <boost/interprocess/sync/windows/sync_utils.hpp>
29 # ifdef BOOST_HAS_UNISTD_H
31 # include <sys/mman.h> //mmap
33 # include <sys/stat.h>
34 # include <sys/types.h>
35 # if defined(BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS)
36 # include <sys/shm.h> //System V shared memory...
38 # include <boost/assert.hpp>
40 # error Unknown platform
43 #endif //#if (defined BOOST_INTERPROCESS_WINDOWS)
46 //!Describes mapped region class
49 namespace interprocess {
52 namespace ipcdetail{ class interprocess_tester; }
53 namespace ipcdetail{ class raw_mapped_region_creator; }
57 //!The mapped_region class represents a portion or region created from a
58 //!memory_mappable object.
60 //!The OS can map a region bigger than the requested one, as region must
61 //!be multiple of the page size, but mapped_region will always refer to
62 //!the region specified by the user.
67 BOOST_MOVABLE_BUT_NOT_COPYABLE(mapped_region)
72 //!Creates a mapping region of the mapped memory "mapping", starting in
73 //!offset "offset", and the mapping's size will be "size". The mapping
74 //!can be opened for read only, read-write or copy-on-write.
76 //!If an address is specified, both the offset and the address must be
77 //!multiples of the page size.
79 //!The OS could allocate more pages than size/page_size(), but get_address()
80 //!will always return the address passed in this function (if not null) and
81 //!get_size() will return the specified size.
82 template<class MemoryMappable>
83 mapped_region(const MemoryMappable& mapping
87 ,const void *address = 0);
89 //!Default constructor. Address will be 0 (nullptr).
94 //!Move constructor. *this will be constructed taking ownership of "other"'s
95 //!region and "other" will be left in default constructor state.
96 mapped_region(BOOST_RV_REF(mapped_region) other)
97 #if defined (BOOST_INTERPROCESS_WINDOWS)
98 : m_base(0), m_size(0)
101 , m_file_or_mapping_hnd(ipcdetail::invalid_file())
103 : m_base(0), m_size(0), m_page_offset(0), m_mode(read_only), m_is_xsi(false)
105 { this->swap(other); }
107 //!Destroys the mapped region.
111 //!Move assignment. If *this owns a memory mapped region, it will be
112 //!destroyed and it will take ownership of "other"'s memory mapped region.
113 mapped_region &operator=(BOOST_RV_REF(mapped_region) other)
115 mapped_region tmp(boost::move(other));
120 //!Returns the size of the mapping. Never throws.
121 std::size_t get_size() const;
123 //!Returns the base address of the mapping.
125 void* get_address() const;
127 //!Returns the mode of the mapping used to construct the mapped region.
129 mode_t get_mode() const;
131 //!Flushes to the disk a byte range within the mapped memory.
132 //!If 'async' is true, the function will return before flushing operation is completed
133 //!If 'async' is false, function will return once data has been written into the underlying
134 //!device (i.e., in mapped files OS cached information is written to disk).
135 //!Never throws. Returns false if operation could not be performed.
136 bool flush(std::size_t mapping_offset = 0, std::size_t numbytes = 0, bool async = true);
138 //!Swaps the mapped_region with another
140 void swap(mapped_region &other);
142 //!Returns the size of the page. This size is the minimum memory that
143 //!will be used by the system when mapping a memory mappable source and
144 //!will restrict the address and the offset to map.
145 static std::size_t get_page_size();
149 //!Closes a previously opened memory mapping. Never throws
152 void* priv_map_address() const;
153 std::size_t priv_map_size() const;
154 bool priv_flush_param_check(std::size_t mapping_offset, void *&addr, std::size_t &numbytes) const;
155 static void priv_size_from_mapping_size
156 (offset_t mapping_size, offset_t offset, offset_t page_offset, std::size_t &size);
157 static offset_t priv_page_offset_addr_fixup(offset_t page_offset, const void *&addr);
160 struct page_size_holder
162 static const std::size_t PageSize;
163 static std::size_t get_page_size();
168 std::size_t m_page_offset;
170 #if defined(BOOST_INTERPROCESS_WINDOWS)
171 file_handle_t m_file_or_mapping_hnd;
176 friend class ipcdetail::interprocess_tester;
177 friend class ipcdetail::raw_mapped_region_creator;
178 void dont_close_on_destruction();
179 #if defined(BOOST_INTERPROCESS_WINDOWS) && !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION)
181 static void destroy_syncs_in_range(const void *addr, std::size_t size);
188 inline void swap(mapped_region &x, mapped_region &y)
191 inline mapped_region::~mapped_region()
192 { this->priv_close(); }
194 inline std::size_t mapped_region::get_size() const
197 inline mode_t mapped_region::get_mode() const
200 inline void* mapped_region::get_address() const
203 inline void* mapped_region::priv_map_address() const
204 { return static_cast<char*>(m_base) - m_page_offset; }
206 inline std::size_t mapped_region::priv_map_size() const
207 { return m_size + m_page_offset; }
209 inline bool mapped_region::priv_flush_param_check
210 (std::size_t mapping_offset, void *&addr, std::size_t &numbytes) const
216 if(mapping_offset >= m_size || (mapping_offset + numbytes) > m_size){
220 //Update flush size if the user does not provide it
222 numbytes = m_size - mapping_offset;
224 addr = (char*)this->priv_map_address() + mapping_offset;
225 numbytes += m_page_offset;
229 inline void mapped_region::priv_size_from_mapping_size
230 (offset_t mapping_size, offset_t offset, offset_t page_offset, std::size_t &size)
232 //Check if mapping size fits in the user address space
233 //as offset_t is the maximum file size and its signed.
234 if(mapping_size < offset ||
235 boost::uintmax_t(mapping_size - (offset - page_offset)) >
236 boost::uintmax_t(std::size_t(-1))){
237 error_info err(size_error);
238 throw interprocess_exception(err);
240 size = static_cast<std::size_t>(mapping_size - (offset - page_offset));
243 inline offset_t mapped_region::priv_page_offset_addr_fixup(offset_t offset, const void *&address)
245 //We can't map any offset so we have to obtain system's
247 const std::size_t page_size = mapped_region::get_page_size();
249 //We calculate the difference between demanded and valid offset
250 //(always less than a page in std::size_t, thus, representable by std::size_t)
251 const std::size_t page_offset =
252 static_cast<std::size_t>(offset - (offset / page_size) * page_size);
253 //Update the mapping address
255 address = static_cast<const char*>(address) - page_offset;
260 #if defined (BOOST_INTERPROCESS_WINDOWS)
262 inline mapped_region::mapped_region()
263 : m_base(0), m_size(0), m_page_offset(0), m_mode(read_only)
264 , m_file_or_mapping_hnd(ipcdetail::invalid_file())
268 inline std::size_t mapped_region::page_size_holder<dummy>::get_page_size()
270 winapi::system_info info;
271 get_system_info(&info);
272 return std::size_t(info.dwAllocationGranularity);
275 template<class MemoryMappable>
276 inline mapped_region::mapped_region
277 (const MemoryMappable &mapping
281 ,const void *address)
282 : m_base(0), m_size(0), m_page_offset(0), m_mode(mode)
283 , m_file_or_mapping_hnd(ipcdetail::invalid_file())
285 mapping_handle_t mhandle = mapping.get_mapping_handle();
287 file_handle_t native_mapping_handle = 0;
290 //For "create_file_mapping"
291 unsigned long protection = 0;
292 //For "mapviewoffile"
293 unsigned long map_access = 0;
299 protection |= winapi::page_readonly;
300 map_access |= winapi::file_map_read;
303 protection |= winapi::page_readwrite;
304 map_access |= winapi::file_map_write;
307 protection |= winapi::page_writecopy;
308 map_access |= winapi::file_map_copy;
312 error_info err(mode_error);
313 throw interprocess_exception(err);
318 //For file mapping (including emulated shared memory through temporary files),
319 //the device is a file handle so we need to obtain file's size and call create_file_mapping
320 //to obtain the mapping handle.
321 //For files we don't need the file mapping after mapping the memory, as the file is there
322 //so we'll program the handle close
323 void * handle_to_close = winapi::invalid_handle_value;
325 //Create mapping handle
326 native_mapping_handle = winapi::create_file_mapping
327 ( ipcdetail::file_handle_from_mapping_handle(mapping.get_mapping_handle())
328 , protection, 0, 0, 0);
330 //Check if all is correct
331 if(!native_mapping_handle){
332 error_info err = winapi::get_last_error();
333 throw interprocess_exception(err);
335 handle_to_close = native_mapping_handle;
338 //For windows_shared_memory the device handle is already a mapping handle
339 //and we need to maintain it
340 native_mapping_handle = mhandle.handle;
342 //RAII handle close on scope exit
343 const winapi::handle_closer close_handle(handle_to_close);
346 const offset_t page_offset = priv_page_offset_addr_fixup(offset, address);
348 //Obtain mapping size if user provides 0 size
350 offset_t mapping_size;
351 if(!winapi::get_file_mapping_size(native_mapping_handle, mapping_size)){
352 error_info err = winapi::get_last_error();
353 throw interprocess_exception(err);
356 priv_size_from_mapping_size(mapping_size, offset, page_offset, size);
360 //Map with new offsets and size
361 void *base = winapi::map_view_of_file_ex
362 (native_mapping_handle,
364 offset - page_offset,
365 static_cast<std::size_t>(page_offset + size),
366 const_cast<void*>(address));
369 error_info err = winapi::get_last_error();
370 throw interprocess_exception(err);
373 //Calculate new base for the user
374 m_base = static_cast<char*>(base) + page_offset;
375 m_page_offset = page_offset;
378 //Windows shared memory needs the duplication of the handle if we want to
379 //make mapped_region independent from the mappable device
381 //For mapped files, we duplicate the file handle to be able to FlushFileBuffers
382 if(!winapi::duplicate_current_process_handle(mhandle.handle, &m_file_or_mapping_hnd)){
383 error_info err = winapi::get_last_error();
385 throw interprocess_exception(err);
389 inline bool mapped_region::flush(std::size_t mapping_offset, std::size_t numbytes, bool async)
392 if(!this->priv_flush_param_check(mapping_offset, addr, numbytes)){
396 if(!winapi::flush_view_of_file(addr, numbytes)){
399 //m_file_or_mapping_hnd can be a file handle or a mapping handle.
400 //so flushing file buffers has only sense for files...
401 else if(async && m_file_or_mapping_hnd != winapi::invalid_handle_value &&
402 winapi::get_file_type(m_file_or_mapping_hnd) == winapi::file_type_disk){
403 return winapi::flush_file_buffers(m_file_or_mapping_hnd);
408 inline void mapped_region::priv_close()
411 void *addr = this->priv_map_address();
412 #if !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION)
413 mapped_region::destroy_syncs_in_range<0>(addr, m_size);
415 winapi::unmap_view_of_file(addr);
418 if(m_file_or_mapping_hnd != ipcdetail::invalid_file()){
419 winapi::close_handle(m_file_or_mapping_hnd);
420 m_file_or_mapping_hnd = ipcdetail::invalid_file();
424 inline void mapped_region::dont_close_on_destruction()
427 #else //#if (defined BOOST_INTERPROCESS_WINDOWS)
429 inline mapped_region::mapped_region()
430 : m_base(0), m_size(0), m_page_offset(0), m_mode(read_only), m_is_xsi(false)
434 inline std::size_t mapped_region::page_size_holder<dummy>::get_page_size()
435 { return std::size_t(sysconf(_SC_PAGESIZE)); }
437 template<class MemoryMappable>
438 inline mapped_region::mapped_region
439 ( const MemoryMappable &mapping
443 , const void *address)
444 : m_base(0), m_size(0), m_page_offset(0), m_mode(mode), m_is_xsi(false)
446 mapping_handle_t map_hnd = mapping.get_mapping_handle();
448 //Some systems dont' support XSI shared memory
449 #ifdef BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS
453 int ret = ::shmctl(map_hnd.handle, IPC_STAT, &xsi_ds);
455 error_info err(system_error_code());
456 throw interprocess_exception(err);
460 size = (std::size_t)xsi_ds.shm_segsz;
462 else if(size != (std::size_t)xsi_ds.shm_segsz){
463 error_info err(size_error);
464 throw interprocess_exception(err);
468 if(m_mode == read_only){
471 else if(m_mode != read_write){
472 error_info err(mode_error);
473 throw interprocess_exception(err);
476 void *base = ::shmat(map_hnd.handle, (void*)address, flag);
477 if(base == (void*)-1){
478 error_info err(system_error_code());
479 throw interprocess_exception(err);
489 #endif //ifdef BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS
491 //We calculate the difference between demanded and valid offset
492 const offset_t page_offset = priv_page_offset_addr_fixup(offset, address);
496 if(0 != fstat(map_hnd.handle, &buf)){
497 error_info err(system_error_code());
498 throw interprocess_exception(err);
501 priv_size_from_mapping_size(buf.st_size, offset, page_offset, size);
517 flags |= MAP_PRIVATE;
521 prot |= (PROT_WRITE | PROT_READ);
526 prot |= (PROT_WRITE | PROT_READ);
527 flags |= MAP_PRIVATE;
532 error_info err(mode_error);
533 throw interprocess_exception(err);
538 //Map it to the address space
539 void* base = mmap ( const_cast<void*>(address)
540 , static_cast<std::size_t>(page_offset + size)
543 , mapping.get_mapping_handle().handle
544 , offset - page_offset);
546 //Check if mapping was successful
547 if(base == MAP_FAILED){
548 error_info err = system_error_code();
549 throw interprocess_exception(err);
552 //Calculate new base for the user
553 m_base = static_cast<char*>(base) + page_offset;
554 m_page_offset = page_offset;
557 //Check for fixed mapping error
558 if(address && (base != address)){
559 error_info err(busy_error);
561 throw interprocess_exception(err);
565 inline bool mapped_region::flush(std::size_t mapping_offset, std::size_t numbytes, bool async)
568 if(!this->priv_flush_param_check(mapping_offset, addr, numbytes)){
572 return msync( addr, numbytes, async ? MS_ASYNC : MS_SYNC) == 0;
575 inline void mapped_region::priv_close()
578 #ifdef BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS
580 int ret = ::shmdt(m_base);
581 BOOST_ASSERT(ret == 0);
585 #endif //#ifdef BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS
586 munmap(this->priv_map_address(), this->priv_map_size());
591 inline void mapped_region::dont_close_on_destruction()
594 #endif //##if (defined BOOST_INTERPROCESS_WINDOWS)
597 const std::size_t mapped_region::page_size_holder<dummy>::PageSize
598 = mapped_region::page_size_holder<dummy>::get_page_size();
600 inline std::size_t mapped_region::get_page_size()
602 if(!page_size_holder<0>::PageSize)
603 return page_size_holder<0>::get_page_size();
605 return page_size_holder<0>::PageSize;
608 inline void mapped_region::swap(mapped_region &other)
610 ipcdetail::do_swap(this->m_base, other.m_base);
611 ipcdetail::do_swap(this->m_size, other.m_size);
612 ipcdetail::do_swap(this->m_page_offset, other.m_page_offset);
613 ipcdetail::do_swap(this->m_mode, other.m_mode);
614 #if (defined BOOST_INTERPROCESS_WINDOWS)
615 ipcdetail::do_swap(this->m_file_or_mapping_hnd, other.m_file_or_mapping_hnd);
617 ipcdetail::do_swap(this->m_is_xsi, other.m_is_xsi);
622 struct null_mapped_region_function
624 bool operator()(void *, std::size_t , bool) const
630 } //namespace interprocess {
631 } //namespace boost {
633 #include <boost/interprocess/detail/config_end.hpp>
635 #endif //BOOST_INTERPROCESS_MAPPED_REGION_HPP
637 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
639 #ifndef BOOST_INTERPROCESS_MAPPED_REGION_EXT_HPP
640 #define BOOST_INTERPROCESS_MAPPED_REGION_EXT_HPP
642 #if defined(BOOST_INTERPROCESS_WINDOWS) && !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION)
643 # include <boost/interprocess/sync/windows/sync_utils.hpp>
644 # include <boost/interprocess/detail/windows_intermodule_singleton.hpp>
647 namespace interprocess {
650 inline void mapped_region::destroy_syncs_in_range(const void *addr, std::size_t size)
652 ipcdetail::sync_handles &handles =
653 ipcdetail::windows_intermodule_singleton<ipcdetail::sync_handles>::get();
654 handles.destroy_syncs_in_range(addr, size);
657 } //namespace interprocess {
658 } //namespace boost {
660 #endif //defined(BOOST_INTERPROCESS_WINDOWS) && !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION)
662 #endif //#ifdef BOOST_INTERPROCESS_MAPPED_REGION_EXT_HPP
664 #endif //#if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)