Imported Upstream version 1.51.0
[platform/upstream/boost.git] / boost / interprocess / mapped_region.hpp
1 //////////////////////////////////////////////////////////////////////////////
2 //
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)
6 //
7 // See http://www.boost.org/libs/interprocess for documentation.
8 //
9 //////////////////////////////////////////////////////////////////////////////
10
11 #ifndef BOOST_INTERPROCESS_MAPPED_REGION_HPP
12 #define BOOST_INTERPROCESS_MAPPED_REGION_HPP
13
14 #include <boost/interprocess/detail/config_begin.hpp>
15 #include <boost/interprocess/detail/workaround.hpp>
16
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>
22 #include <string>
23 #include <boost/cstdint.hpp>
24
25 #if defined (BOOST_INTERPROCESS_WINDOWS)
26 #  include <boost/interprocess/detail/win32_api.hpp>
27 #  include <boost/interprocess/sync/windows/sync_utils.hpp>
28 #else
29 #  ifdef BOOST_HAS_UNISTD_H
30 #    include <fcntl.h>
31 #    include <sys/mman.h>     //mmap
32 #    include <unistd.h>
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...
37 #    endif
38 #    include <boost/assert.hpp>
39 #  else
40 #    error Unknown platform
41 #  endif
42
43 #endif   //#if (defined BOOST_INTERPROCESS_WINDOWS)
44
45 //!\file
46 //!Describes mapped region class
47
48 namespace boost {
49 namespace interprocess {
50
51 /// @cond
52 namespace ipcdetail{ class interprocess_tester; }
53 namespace ipcdetail{ class raw_mapped_region_creator; }
54
55 /// @endcond
56
57 //!The mapped_region class represents a portion or region created from a
58 //!memory_mappable object.
59 //!
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.
63 class mapped_region
64 {
65    /// @cond
66    //Non-copyable
67    BOOST_MOVABLE_BUT_NOT_COPYABLE(mapped_region)
68    /// @endcond
69
70    public:
71
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.
75    //!
76    //!If an address is specified, both the offset and the address must be
77    //!multiples of the page size.
78    //!
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
84                 ,mode_t mode
85                 ,offset_t offset = 0
86                 ,std::size_t size = 0
87                 ,const void *address = 0);
88
89    //!Default constructor. Address will be 0 (nullptr).
90    //!Size will be 0.
91    //!Does not throw
92    mapped_region();
93
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)
99    ,  m_page_offset(0)
100    ,  m_mode(read_only)
101    ,  m_file_or_mapping_hnd(ipcdetail::invalid_file())
102    #else
103    :  m_base(0), m_size(0), m_page_offset(0), m_mode(read_only), m_is_xsi(false)
104    #endif
105    {  this->swap(other);   }
106
107    //!Destroys the mapped region.
108    //!Does not throw
109    ~mapped_region();
110
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)
114    {
115       mapped_region tmp(boost::move(other));
116       this->swap(tmp);
117       return *this;
118    }
119
120    //!Returns the size of the mapping. Never throws.
121    std::size_t get_size() const;
122
123    //!Returns the base address of the mapping.
124    //!Never throws.
125    void*       get_address() const;
126
127    //!Returns the mode of the mapping used to construct the mapped region.
128    //!Never throws.
129    mode_t get_mode() const;
130
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);
137
138    //!Swaps the mapped_region with another
139    //!mapped region
140    void swap(mapped_region &other);
141
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();
146
147    /// @cond
148    private:
149    //!Closes a previously opened memory mapping. Never throws
150    void priv_close();
151
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);
158
159    template<int dummy>
160    struct page_size_holder
161    {
162       static const std::size_t PageSize;
163       static std::size_t get_page_size();
164    };
165
166    void*             m_base;
167    std::size_t       m_size;
168    std::size_t       m_page_offset;
169    mode_t            m_mode;
170    #if defined(BOOST_INTERPROCESS_WINDOWS)
171    file_handle_t     m_file_or_mapping_hnd;
172    #else
173    bool              m_is_xsi;
174    #endif
175
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)
180    template<int Dummy>
181    static void destroy_syncs_in_range(const void *addr, std::size_t size);
182    #endif
183    /// @endcond
184 };
185
186 ///@cond
187
188 inline void swap(mapped_region &x, mapped_region &y)
189 {  x.swap(y);  }
190
191 inline mapped_region::~mapped_region()
192 {  this->priv_close(); }
193
194 inline std::size_t mapped_region::get_size()  const
195 {  return m_size; }
196
197 inline mode_t mapped_region::get_mode()  const
198 {  return m_mode;   }
199
200 inline void*    mapped_region::get_address()  const
201 {  return m_base; }
202
203 inline void*    mapped_region::priv_map_address()  const
204 {  return static_cast<char*>(m_base) - m_page_offset; }
205
206 inline std::size_t mapped_region::priv_map_size()  const
207 {  return m_size + m_page_offset; }
208
209 inline bool mapped_region::priv_flush_param_check
210    (std::size_t mapping_offset, void *&addr, std::size_t &numbytes) const
211 {
212    //Check some errors
213    if(m_base == 0)
214       return false;
215
216    if(mapping_offset >= m_size || (mapping_offset + numbytes) > m_size){
217       return false;
218    }
219
220    //Update flush size if the user does not provide it
221    if(numbytes == 0){
222       numbytes = m_size - mapping_offset;
223    }
224    addr = (char*)this->priv_map_address() + mapping_offset;
225    numbytes += m_page_offset;
226    return true;
227 }
228
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)
231 {
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);
239    }
240    size = static_cast<std::size_t>(mapping_size - (offset - page_offset));
241 }
242
243 inline offset_t mapped_region::priv_page_offset_addr_fixup(offset_t offset, const void *&address)
244 {
245    //We can't map any offset so we have to obtain system's
246    //memory granularity
247    const std::size_t page_size  = mapped_region::get_page_size();
248
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
254    if(address){
255       address = static_cast<const char*>(address) - page_offset;
256    }
257    return page_offset;
258 }
259
260 #if defined (BOOST_INTERPROCESS_WINDOWS)
261
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())
265 {}
266
267 template<int dummy>
268 inline std::size_t mapped_region::page_size_holder<dummy>::get_page_size()
269 {
270    winapi::system_info info;
271    get_system_info(&info);
272    return std::size_t(info.dwAllocationGranularity);
273 }
274
275 template<class MemoryMappable>
276 inline mapped_region::mapped_region
277    (const MemoryMappable &mapping
278    ,mode_t mode
279    ,offset_t offset
280    ,std::size_t size
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())
284 {
285    mapping_handle_t mhandle = mapping.get_mapping_handle();
286    {
287       file_handle_t native_mapping_handle = 0;
288
289       //Set accesses
290       //For "create_file_mapping"
291       unsigned long protection = 0;
292       //For "mapviewoffile"
293       unsigned long map_access = 0;
294
295       switch(mode)
296       {
297          case read_only:
298          case read_private:
299             protection   |= winapi::page_readonly;
300             map_access   |= winapi::file_map_read;
301          break;
302          case read_write:
303             protection   |= winapi::page_readwrite;
304             map_access   |= winapi::file_map_write;
305          break;
306          case copy_on_write:
307             protection   |= winapi::page_writecopy;
308             map_access   |= winapi::file_map_copy;
309          break;
310          default:
311             {
312                error_info err(mode_error);
313                throw interprocess_exception(err);
314             }
315          break;
316       }
317
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;
324       if(!mhandle.is_shm){
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);
329
330          //Check if all is correct
331          if(!native_mapping_handle){
332             error_info err = winapi::get_last_error();
333             throw interprocess_exception(err);
334          }
335          handle_to_close = native_mapping_handle;
336       }
337       else{
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;
341       }
342       //RAII handle close on scope exit
343       const winapi::handle_closer close_handle(handle_to_close);
344       (void)close_handle;
345
346       const offset_t page_offset = priv_page_offset_addr_fixup(offset, address);
347
348       //Obtain mapping size if user provides 0 size
349       if(size == 0){
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);
354          }
355          //This can throw
356          priv_size_from_mapping_size(mapping_size, offset, page_offset, size);
357       }
358
359
360       //Map with new offsets and size
361       void *base = winapi::map_view_of_file_ex
362                                  (native_mapping_handle,
363                                  map_access,
364                                  offset - page_offset,
365                                  static_cast<std::size_t>(page_offset + size),
366                                  const_cast<void*>(address));
367       //Check error
368       if(!base){
369          error_info err = winapi::get_last_error();
370          throw interprocess_exception(err);
371       }
372
373       //Calculate new base for the user
374       m_base = static_cast<char*>(base) + page_offset;
375       m_page_offset = page_offset;
376       m_size = size;
377    }
378    //Windows shared memory needs the duplication of the handle if we want to
379    //make mapped_region independent from the mappable device
380    //
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();
384       this->priv_close();
385       throw interprocess_exception(err);
386    }
387 }
388
389 inline bool mapped_region::flush(std::size_t mapping_offset, std::size_t numbytes, bool async)
390 {
391    void *addr;
392    if(!this->priv_flush_param_check(mapping_offset, addr, numbytes)){
393       return false;
394    }
395    //Flush it all
396    if(!winapi::flush_view_of_file(addr, numbytes)){
397       return false;
398    }
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);
404    }
405    return true;
406 }
407
408 inline void mapped_region::priv_close()
409 {
410    if(m_base){
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);
414       #endif
415       winapi::unmap_view_of_file(addr);
416       m_base = 0;
417    }
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();
421    }
422 }
423
424 inline void mapped_region::dont_close_on_destruction()
425 {}
426
427 #else    //#if (defined BOOST_INTERPROCESS_WINDOWS)
428
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)
431 {}
432
433 template<int dummy>
434 inline std::size_t mapped_region::page_size_holder<dummy>::get_page_size()
435 {  return std::size_t(sysconf(_SC_PAGESIZE)); }
436
437 template<class MemoryMappable>
438 inline mapped_region::mapped_region
439    ( const MemoryMappable &mapping
440    , mode_t mode
441    , offset_t offset
442    , std::size_t size
443    , const void *address)
444    : m_base(0), m_size(0), m_page_offset(0), m_mode(mode), m_is_xsi(false)
445 {
446    mapping_handle_t map_hnd = mapping.get_mapping_handle();
447
448    //Some systems dont' support XSI shared memory
449    #ifdef BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS
450    if(map_hnd.is_xsi){
451       //Get the size
452       ::shmid_ds xsi_ds;
453       int ret = ::shmctl(map_hnd.handle, IPC_STAT, &xsi_ds);
454       if(ret == -1){
455          error_info err(system_error_code());
456          throw interprocess_exception(err);
457       }
458       //Compare sizess
459       if(size == 0){
460          size = (std::size_t)xsi_ds.shm_segsz;
461       }
462       else if(size != (std::size_t)xsi_ds.shm_segsz){
463          error_info err(size_error);
464          throw interprocess_exception(err);
465       }
466       //Calculate flag
467       int flag = 0;
468       if(m_mode == read_only){
469          flag |= SHM_RDONLY;
470       }
471       else if(m_mode != read_write){
472          error_info err(mode_error);
473          throw interprocess_exception(err);
474       }
475       //Attach memory
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);
480       }
481       //Update members
482       m_base   = base;
483       m_size   = size;
484       m_mode   = mode;
485       m_page_offset = 0;
486       m_is_xsi = true;
487       return;
488    }
489    #endif   //ifdef BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS
490
491    //We calculate the difference between demanded and valid offset
492    const offset_t page_offset = priv_page_offset_addr_fixup(offset, address);
493
494    if(size == 0){
495       struct ::stat buf;
496       if(0 != fstat(map_hnd.handle, &buf)){
497          error_info err(system_error_code());
498          throw interprocess_exception(err);
499       }
500       //This can throw
501       priv_size_from_mapping_size(buf.st_size, offset, page_offset, size);
502    }
503
504    //Create new mapping
505    int prot    = 0;
506    int flags   = 0;
507
508    switch(mode)
509    {
510       case read_only:
511          prot  |= PROT_READ;
512          flags |= MAP_SHARED;
513       break;
514
515       case read_private:
516          prot  |= (PROT_READ);
517          flags |= MAP_PRIVATE;
518       break;
519
520       case read_write:
521          prot  |= (PROT_WRITE | PROT_READ);
522          flags |= MAP_SHARED;
523       break;
524
525       case copy_on_write:
526          prot  |= (PROT_WRITE | PROT_READ);
527          flags |= MAP_PRIVATE;
528       break;
529
530       default:
531          {
532             error_info err(mode_error);
533             throw interprocess_exception(err);
534          }
535       break;
536    }
537
538    //Map it to the address space
539    void* base = mmap ( const_cast<void*>(address)
540                      , static_cast<std::size_t>(page_offset + size)
541                      , prot
542                      , flags
543                      , mapping.get_mapping_handle().handle
544                      , offset - page_offset);
545
546    //Check if mapping was successful
547    if(base == MAP_FAILED){
548       error_info err = system_error_code();
549       throw interprocess_exception(err);
550    }
551
552    //Calculate new base for the user
553    m_base = static_cast<char*>(base) + page_offset;
554    m_page_offset = page_offset;
555    m_size   = size;
556
557    //Check for fixed mapping error
558    if(address && (base != address)){
559       error_info err(busy_error);
560       this->priv_close();
561       throw interprocess_exception(err);
562    }
563 }
564
565 inline bool mapped_region::flush(std::size_t mapping_offset, std::size_t numbytes, bool async)
566 {
567    void *addr;
568    if(!this->priv_flush_param_check(mapping_offset, addr, numbytes)){
569       return false;
570    }
571    //Flush it all
572    return msync( addr, numbytes, async ? MS_ASYNC : MS_SYNC) == 0;
573 }
574
575 inline void mapped_region::priv_close()
576 {
577    if(m_base != 0){
578       #ifdef BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS
579       if(m_is_xsi){
580          int ret = ::shmdt(m_base);
581          BOOST_ASSERT(ret == 0);
582          (void)ret;
583          return;
584       }
585       #endif //#ifdef BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS
586       munmap(this->priv_map_address(), this->priv_map_size());
587       m_base = 0;
588    }
589 }
590
591 inline void mapped_region::dont_close_on_destruction()
592 {  m_base = 0;   }
593
594 #endif   //##if (defined BOOST_INTERPROCESS_WINDOWS)
595
596 template<int dummy>
597 const std::size_t mapped_region::page_size_holder<dummy>::PageSize
598    = mapped_region::page_size_holder<dummy>::get_page_size();
599
600 inline std::size_t mapped_region::get_page_size()
601 {
602    if(!page_size_holder<0>::PageSize)
603       return page_size_holder<0>::get_page_size();
604    else
605       return page_size_holder<0>::PageSize;
606 }
607
608 inline void mapped_region::swap(mapped_region &other)
609 {
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);
616    #else
617    ipcdetail::do_swap(this->m_is_xsi, other.m_is_xsi);
618    #endif
619 }
620
621 //!No-op functor
622 struct null_mapped_region_function
623 {
624    bool operator()(void *, std::size_t , bool) const
625       {   return true;   }
626 };
627
628 /// @endcond
629
630 }  //namespace interprocess {
631 }  //namespace boost {
632
633 #include <boost/interprocess/detail/config_end.hpp>
634
635 #endif   //BOOST_INTERPROCESS_MAPPED_REGION_HPP
636
637 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
638
639 #ifndef BOOST_INTERPROCESS_MAPPED_REGION_EXT_HPP
640 #define BOOST_INTERPROCESS_MAPPED_REGION_EXT_HPP
641
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>
645
646 namespace boost {
647 namespace interprocess {
648
649 template<int Dummy>
650 inline void mapped_region::destroy_syncs_in_range(const void *addr, std::size_t size)
651 {
652    ipcdetail::sync_handles &handles =
653       ipcdetail::windows_intermodule_singleton<ipcdetail::sync_handles>::get();
654    handles.destroy_syncs_in_range(addr, size);
655 }
656
657 }  //namespace interprocess {
658 }  //namespace boost {
659
660 #endif   //defined(BOOST_INTERPROCESS_WINDOWS) && !defined(BOOST_INTERPROCESS_FORCE_GENERIC_EMULATION)
661
662 #endif   //#ifdef BOOST_INTERPROCESS_MAPPED_REGION_EXT_HPP
663
664 #endif   //#if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
665