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