1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // (C) Copyright Ion Gaztanaga 2006-2012. 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_MANAGED_OPEN_OR_CREATE_IMPL
12 #define BOOST_INTERPROCESS_MANAGED_OPEN_OR_CREATE_IMPL
13 
14 #ifndef BOOST_CONFIG_HPP
15 #  include <boost/config.hpp>
16 #endif
17 #
18 #if defined(BOOST_HAS_PRAGMA_ONCE)
19 #  pragma once
20 #endif
21 
22 #include <boost/interprocess/detail/config_begin.hpp>
23 #include <boost/interprocess/detail/os_thread_functions.hpp>
24 #include <boost/interprocess/detail/os_file_functions.hpp>
25 #include <boost/interprocess/creation_tags.hpp>
26 #include <boost/interprocess/mapped_region.hpp>
27 #include <boost/interprocess/detail/utilities.hpp>
28 #include <boost/interprocess/detail/type_traits.hpp>
29 #include <boost/interprocess/detail/atomic.hpp>
30 #include <boost/interprocess/detail/interprocess_tester.hpp>
31 #include <boost/interprocess/creation_tags.hpp>
32 #include <boost/interprocess/detail/mpl.hpp>
33 #include <boost/interprocess/permissions.hpp>
34 #include <boost/container/detail/type_traits.hpp>  //alignment_of, aligned_storage
35 #include <boost/interprocess/sync/spin/wait.hpp>
36 #include <boost/move/move.hpp>
37 #include <boost/cstdint.hpp>
38 
39 namespace boost {
40 namespace interprocess {
41 
42 #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
43 namespace ipcdetail{ class interprocess_tester; }
44 
45 
46 template<class DeviceAbstraction>
47 struct managed_open_or_create_impl_device_id_t
48 {
49    typedef const char *type;
50 };
51 
52 #ifdef BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS
53 
54 class xsi_shared_memory_file_wrapper;
55 class xsi_key;
56 
57 template<>
58 struct managed_open_or_create_impl_device_id_t<xsi_shared_memory_file_wrapper>
59 {
60    typedef xsi_key type;
61 };
62 
63 #endif   //BOOST_INTERPROCESS_XSI_SHARED_MEMORY_OBJECTS
64 
65 #endif   //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
66 
67 namespace ipcdetail {
68 
69 
70 template <bool StoreDevice, class DeviceAbstraction>
71 class managed_open_or_create_impl_device_holder
72 {
73    public:
get_device()74    DeviceAbstraction &get_device()
75    {  static DeviceAbstraction dev; return dev; }
76 
get_device() const77    const DeviceAbstraction &get_device() const
78    {  static DeviceAbstraction dev; return dev; }
79 };
80 
81 template <class DeviceAbstraction>
82 class managed_open_or_create_impl_device_holder<true, DeviceAbstraction>
83 {
84    public:
get_device()85    DeviceAbstraction &get_device()
86    {  return dev; }
87 
get_device() const88    const DeviceAbstraction &get_device() const
89    {  return dev; }
90 
91    private:
92    DeviceAbstraction dev;
93 };
94 
95 template<class DeviceAbstraction, std::size_t MemAlignment, bool FileBased, bool StoreDevice>
96 class managed_open_or_create_impl
97    : public managed_open_or_create_impl_device_holder<StoreDevice, DeviceAbstraction>
98 {
99    //Non-copyable
100    BOOST_MOVABLE_BUT_NOT_COPYABLE(managed_open_or_create_impl)
101 
102    typedef typename managed_open_or_create_impl_device_id_t<DeviceAbstraction>::type device_id_t;
103    typedef managed_open_or_create_impl_device_holder<StoreDevice, DeviceAbstraction> DevHolder;
104    enum
105    {
106       UninitializedSegment,
107       InitializingSegment,
108       InitializedSegment,
109       CorruptedSegment
110    };
111 
112    public:
113    static const std::size_t
114       ManagedOpenOrCreateUserOffset =
115          ct_rounded_size
116             < sizeof(boost::uint32_t)
117             , MemAlignment ? (MemAlignment) :
118                (::boost::container::dtl::alignment_of
119                   < ::boost::container::dtl::max_align_t >::value)
120             >::value;
121 
managed_open_or_create_impl()122    managed_open_or_create_impl()
123    {}
124 
managed_open_or_create_impl(create_only_t,const device_id_t & id,std::size_t size,mode_t mode,const void * addr,const permissions & perm)125    managed_open_or_create_impl(create_only_t,
126                  const device_id_t & id,
127                  std::size_t size,
128                  mode_t mode,
129                  const void *addr,
130                  const permissions &perm)
131    {
132       priv_open_or_create
133          ( DoCreate
134          , id
135          , size
136          , mode
137          , addr
138          , perm
139          , null_mapped_region_function());
140    }
141 
managed_open_or_create_impl(open_only_t,const device_id_t & id,mode_t mode,const void * addr)142    managed_open_or_create_impl(open_only_t,
143                  const device_id_t & id,
144                  mode_t mode,
145                  const void *addr)
146    {
147       priv_open_or_create
148          ( DoOpen
149          , id
150          , 0
151          , mode
152          , addr
153          , permissions()
154          , null_mapped_region_function());
155    }
156 
157 
managed_open_or_create_impl(open_or_create_t,const device_id_t & id,std::size_t size,mode_t mode,const void * addr,const permissions & perm)158    managed_open_or_create_impl(open_or_create_t,
159                  const device_id_t & id,
160                  std::size_t size,
161                  mode_t mode,
162                  const void *addr,
163                  const permissions &perm)
164    {
165       priv_open_or_create
166          ( DoOpenOrCreate
167          , id
168          , size
169          , mode
170          , addr
171          , perm
172          , null_mapped_region_function());
173    }
174 
175    template <class ConstructFunc>
managed_open_or_create_impl(create_only_t,const device_id_t & id,std::size_t size,mode_t mode,const void * addr,const ConstructFunc & construct_func,const permissions & perm)176    managed_open_or_create_impl(create_only_t,
177                  const device_id_t & id,
178                  std::size_t size,
179                  mode_t mode,
180                  const void *addr,
181                  const ConstructFunc &construct_func,
182                  const permissions &perm)
183    {
184       priv_open_or_create
185          (DoCreate
186          , id
187          , size
188          , mode
189          , addr
190          , perm
191          , construct_func);
192    }
193 
194    template <class ConstructFunc>
managed_open_or_create_impl(open_only_t,const device_id_t & id,mode_t mode,const void * addr,const ConstructFunc & construct_func)195    managed_open_or_create_impl(open_only_t,
196                  const device_id_t & id,
197                  mode_t mode,
198                  const void *addr,
199                  const ConstructFunc &construct_func)
200    {
201       priv_open_or_create
202          ( DoOpen
203          , id
204          , 0
205          , mode
206          , addr
207          , permissions()
208          , construct_func);
209    }
210 
211    template <class ConstructFunc>
managed_open_or_create_impl(open_or_create_t,const device_id_t & id,std::size_t size,mode_t mode,const void * addr,const ConstructFunc & construct_func,const permissions & perm)212    managed_open_or_create_impl(open_or_create_t,
213                  const device_id_t & id,
214                  std::size_t size,
215                  mode_t mode,
216                  const void *addr,
217                  const ConstructFunc &construct_func,
218                  const permissions &perm)
219    {
220       priv_open_or_create
221          ( DoOpenOrCreate
222          , id
223          , size
224          , mode
225          , addr
226          , perm
227          , construct_func);
228    }
229 
managed_open_or_create_impl(BOOST_RV_REF (managed_open_or_create_impl)moved)230    managed_open_or_create_impl(BOOST_RV_REF(managed_open_or_create_impl) moved)
231    {  this->swap(moved);   }
232 
operator =(BOOST_RV_REF (managed_open_or_create_impl)moved)233    managed_open_or_create_impl &operator=(BOOST_RV_REF(managed_open_or_create_impl) moved)
234    {
235       managed_open_or_create_impl tmp(boost::move(moved));
236       this->swap(tmp);
237       return *this;
238    }
239 
~managed_open_or_create_impl()240    ~managed_open_or_create_impl()
241    {}
242 
get_user_size() const243    std::size_t get_user_size()  const
244    {  return m_mapped_region.get_size() - ManagedOpenOrCreateUserOffset; }
245 
get_user_address() const246    void *get_user_address()  const
247    {  return static_cast<char*>(m_mapped_region.get_address()) + ManagedOpenOrCreateUserOffset;  }
248 
get_real_size() const249    std::size_t get_real_size()  const
250    {  return m_mapped_region.get_size(); }
251 
get_real_address() const252    void *get_real_address()  const
253    {  return m_mapped_region.get_address();  }
254 
swap(managed_open_or_create_impl & other)255    void swap(managed_open_or_create_impl &other)
256    {
257       this->m_mapped_region.swap(other.m_mapped_region);
258    }
259 
flush()260    bool flush()
261    {  return m_mapped_region.flush();  }
262 
get_mapped_region() const263    const mapped_region &get_mapped_region() const
264    {  return m_mapped_region;  }
265 
266 
get_device()267    DeviceAbstraction &get_device()
268    {  return this->DevHolder::get_device(); }
269 
get_device() const270    const DeviceAbstraction &get_device() const
271    {  return this->DevHolder::get_device(); }
272 
273    private:
274 
275    //These are templatized to allow explicit instantiations
276    template<bool dummy>
truncate_device(DeviceAbstraction &,offset_t,false_)277    static void truncate_device(DeviceAbstraction &, offset_t, false_)
278    {} //Empty
279 
280    template<bool dummy>
truncate_device(DeviceAbstraction & dev,offset_t size,true_)281    static void truncate_device(DeviceAbstraction &dev, offset_t size, true_)
282    {  dev.truncate(size);  }
283 
284 
285    template<bool dummy>
check_offset_t_size(std::size_t,false_)286    static bool check_offset_t_size(std::size_t , false_)
287    { return true; } //Empty
288 
289    template<bool dummy>
check_offset_t_size(std::size_t size,true_)290    static bool check_offset_t_size(std::size_t size, true_)
291    { return size == std::size_t(offset_t(size)); }
292 
293    //These are templatized to allow explicit instantiations
294    template<bool dummy>
create_device(DeviceAbstraction & dev,const device_id_t & id,std::size_t size,const permissions & perm,false_ file_like)295    static void create_device(DeviceAbstraction &dev, const device_id_t & id, std::size_t size, const permissions &perm, false_ file_like)
296    {
297       (void)file_like;
298       DeviceAbstraction tmp(create_only, id, read_write, size, perm);
299       tmp.swap(dev);
300    }
301 
302    template<bool dummy>
create_device(DeviceAbstraction & dev,const device_id_t & id,std::size_t,const permissions & perm,true_ file_like)303    static void create_device(DeviceAbstraction &dev, const device_id_t & id, std::size_t, const permissions &perm, true_ file_like)
304    {
305       (void)file_like;
306       DeviceAbstraction tmp(create_only, id, read_write, perm);
307       tmp.swap(dev);
308    }
309 
310    template <class ConstructFunc> inline
priv_open_or_create(create_enum_t type,const device_id_t & id,std::size_t size,mode_t mode,const void * addr,const permissions & perm,ConstructFunc construct_func)311    void priv_open_or_create
312       (create_enum_t type,
313        const device_id_t & id,
314        std::size_t size,
315        mode_t mode, const void *addr,
316        const permissions &perm,
317        ConstructFunc construct_func)
318    {
319       typedef bool_<FileBased> file_like_t;
320       (void)mode;
321       bool created = false;
322       bool ronly   = false;
323       bool cow     = false;
324       DeviceAbstraction dev;
325 
326       if(type != DoOpen){
327          //Check if the requested size is enough to build the managed metadata
328          const std::size_t func_min_size = construct_func.get_min_size();
329          if( (std::size_t(-1) - ManagedOpenOrCreateUserOffset) < func_min_size ||
330              size < (func_min_size + ManagedOpenOrCreateUserOffset) ){
331             throw interprocess_exception(error_info(size_error));
332          }
333       }
334       //Check size can be represented by offset_t (used by truncate)
335       if(type != DoOpen && !check_offset_t_size<FileBased>(size, file_like_t())){
336          throw interprocess_exception(error_info(size_error));
337       }
338       if(type == DoOpen && mode == read_write){
339          DeviceAbstraction tmp(open_only, id, read_write);
340          tmp.swap(dev);
341          created = false;
342       }
343       else if(type == DoOpen && mode == read_only){
344          DeviceAbstraction tmp(open_only, id, read_only);
345          tmp.swap(dev);
346          created = false;
347          ronly   = true;
348       }
349       else if(type == DoOpen && mode == copy_on_write){
350          DeviceAbstraction tmp(open_only, id, read_only);
351          tmp.swap(dev);
352          created = false;
353          cow     = true;
354       }
355       else if(type == DoCreate){
356          create_device<FileBased>(dev, id, size, perm, file_like_t());
357          created = true;
358       }
359       else if(type == DoOpenOrCreate){
360          //This loop is very ugly, but brute force is sometimes better
361          //than diplomacy. If someone knows how to open or create a
362          //file and know if we have really created it or just open it
363          //drop me a e-mail!
364          bool completed = false;
365          spin_wait swait;
366          while(!completed){
367             try{
368                create_device<FileBased>(dev, id, size, perm, file_like_t());
369                created     = true;
370                completed   = true;
371             }
372             catch(interprocess_exception &ex){
373                if(ex.get_error_code() != already_exists_error){
374                   throw;
375                }
376                else{
377                   try{
378                      DeviceAbstraction tmp(open_only, id, read_write);
379                      dev.swap(tmp);
380                      created     = false;
381                      completed   = true;
382                   }
383                   catch(interprocess_exception &e){
384                      if(e.get_error_code() != not_found_error){
385                         throw;
386                      }
387                   }
388                   catch(...){
389                      throw;
390                   }
391                }
392             }
393             catch(...){
394                throw;
395             }
396             swait.yield();
397          }
398       }
399 
400       if(created){
401          try{
402             //If this throws, we are lost
403             truncate_device<FileBased>(dev, size, file_like_t());
404 
405             //If the following throws, we will truncate the file to 1
406             mapped_region        region(dev, read_write, 0, 0, addr);
407             boost::uint32_t *patomic_word = 0;  //avoid gcc warning
408             patomic_word = static_cast<boost::uint32_t*>(region.get_address());
409             boost::uint32_t previous = atomic_cas32(patomic_word, InitializingSegment, UninitializedSegment);
410 
411             if(previous == UninitializedSegment){
412                try{
413                   construct_func( static_cast<char*>(region.get_address()) + ManagedOpenOrCreateUserOffset
414                                 , size - ManagedOpenOrCreateUserOffset, true);
415                   //All ok, just move resources to the external mapped region
416                   m_mapped_region.swap(region);
417                }
418                catch(...){
419                   atomic_write32(patomic_word, CorruptedSegment);
420                   throw;
421                }
422                atomic_write32(patomic_word, InitializedSegment);
423             }
424             else if(previous == InitializingSegment || previous == InitializedSegment){
425                throw interprocess_exception(error_info(already_exists_error));
426             }
427             else{
428                throw interprocess_exception(error_info(corrupted_error));
429             }
430          }
431          catch(...){
432             try{
433                truncate_device<FileBased>(dev, 1u, file_like_t());
434             }
435             catch(...){
436             }
437             throw;
438          }
439       }
440       else{
441          if(FileBased){
442             offset_t filesize = 0;
443             spin_wait swait;
444             while(filesize == 0){
445                if(!get_file_size(file_handle_from_mapping_handle(dev.get_mapping_handle()), filesize)){
446                   error_info err = system_error_code();
447                   throw interprocess_exception(err);
448                }
449                swait.yield();
450             }
451             if(filesize == 1){
452                throw interprocess_exception(error_info(corrupted_error));
453             }
454          }
455 
456          mapped_region  region(dev, ronly ? read_only : (cow ? copy_on_write : read_write), 0, 0, addr);
457 
458          boost::uint32_t *patomic_word = static_cast<boost::uint32_t*>(region.get_address());
459          boost::uint32_t value = atomic_read32(patomic_word);
460 
461          spin_wait swait;
462          while(value == InitializingSegment || value == UninitializedSegment){
463             swait.yield();
464             value = atomic_read32(patomic_word);
465          }
466 
467          if(value != InitializedSegment)
468             throw interprocess_exception(error_info(corrupted_error));
469 
470          construct_func( static_cast<char*>(region.get_address()) + ManagedOpenOrCreateUserOffset
471                         , region.get_size() - ManagedOpenOrCreateUserOffset
472                         , false);
473          //All ok, just move resources to the external mapped region
474          m_mapped_region.swap(region);
475       }
476       if(StoreDevice){
477          this->DevHolder::get_device() = boost::move(dev);
478       }
479    }
480 
swap(managed_open_or_create_impl & left,managed_open_or_create_impl & right)481    friend void swap(managed_open_or_create_impl &left, managed_open_or_create_impl &right)
482    {
483       left.swap(right);
484    }
485 
486    private:
487    friend class interprocess_tester;
dont_close_on_destruction()488    void dont_close_on_destruction()
489    {  interprocess_tester::dont_close_on_destruction(m_mapped_region);  }
490 
491    mapped_region     m_mapped_region;
492 };
493 
494 }  //namespace ipcdetail {
495 
496 }  //namespace interprocess {
497 }  //namespace boost {
498 
499 #include <boost/interprocess/detail/config_end.hpp>
500 
501 #endif   //#ifndef BOOST_INTERPROCESS_MANAGED_OPEN_OR_CREATE_IMPL
502