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