]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/interprocess/detail/managed_open_or_create_impl.hpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / 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 #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/interprocess/detail/timed_utils.hpp>
37 #include <boost/move/move.hpp>
38 #include <boost/cstdint.hpp>
39
40 namespace boost {
41 namespace interprocess {
42 namespace ipcdetail {
43
44 template <bool StoreDevice, class DeviceAbstraction>
45 class managed_open_or_create_impl_device_holder
46 {
47 public:
48 DeviceAbstraction &get_device()
49 { static DeviceAbstraction dev; return dev; }
50
51 const DeviceAbstraction &get_device() const
52 { static DeviceAbstraction dev; return dev; }
53 };
54
55 template <class DeviceAbstraction>
56 class managed_open_or_create_impl_device_holder<true, DeviceAbstraction>
57 {
58 public:
59 DeviceAbstraction &get_device()
60 { return dev; }
61
62 const DeviceAbstraction &get_device() const
63 { return dev; }
64
65 private:
66 DeviceAbstraction dev;
67 };
68
69 template<class DeviceAbstraction, std::size_t MemAlignment, bool FileBased, bool StoreDevice>
70 class managed_open_or_create_impl
71 : public managed_open_or_create_impl_device_holder<StoreDevice, DeviceAbstraction>
72 {
73 //Non-copyable
74 BOOST_MOVABLE_BUT_NOT_COPYABLE(managed_open_or_create_impl)
75 typedef bool_<FileBased> file_like_t;
76
77 static const unsigned MaxCreateOrOpenTries = BOOST_INTERPROCESS_MANAGED_OPEN_OR_CREATE_INITIALIZE_MAX_TRIES;
78 static const unsigned MaxInitializeTimeSec = BOOST_INTERPROCESS_MANAGED_OPEN_OR_CREATE_INITIALIZE_TIMEOUT_SEC;
79
80 typedef managed_open_or_create_impl_device_holder<StoreDevice, DeviceAbstraction> DevHolder;
81 enum
82 {
83 UninitializedSegment,
84 InitializingSegment,
85 InitializedSegment,
86 CorruptedSegment
87 };
88
89 static const std::size_t RequiredAlignment =
90 MemAlignment ? MemAlignment
91 : boost::container::dtl::alignment_of< boost::container::dtl::max_align_t >::value
92 ;
93
94 public:
95 static const std::size_t ManagedOpenOrCreateUserOffset =
96 ct_rounded_size<sizeof(boost::uint32_t), RequiredAlignment>::value;
97
98 managed_open_or_create_impl()
99 {}
100
101 template <class DeviceId>
102 managed_open_or_create_impl(create_only_t,
103 const DeviceId & id,
104 std::size_t size,
105 mode_t mode,
106 const void *addr,
107 const permissions &perm)
108 {
109 priv_open_or_create
110 ( DoCreate
111 , id
112 , size
113 , mode
114 , addr
115 , perm
116 , null_mapped_region_function());
117 }
118
119 template <class DeviceId>
120 managed_open_or_create_impl(open_only_t,
121 const DeviceId & id,
122 mode_t mode,
123 const void *addr)
124 {
125 priv_open_or_create
126 ( DoOpen
127 , id
128 , 0
129 , mode
130 , addr
131 , permissions()
132 , null_mapped_region_function());
133 }
134
135 template <class DeviceId>
136 managed_open_or_create_impl(open_or_create_t,
137 const DeviceId & id,
138 std::size_t size,
139 mode_t mode,
140 const void *addr,
141 const permissions &perm)
142 {
143 priv_open_or_create
144 ( DoOpenOrCreate
145 , id
146 , size
147 , mode
148 , addr
149 , perm
150 , null_mapped_region_function());
151 }
152
153 template <class DeviceId, class ConstructFunc>
154 managed_open_or_create_impl(create_only_t,
155 const DeviceId & id,
156 std::size_t size,
157 mode_t mode,
158 const void *addr,
159 const ConstructFunc &construct_func,
160 const permissions &perm)
161 {
162 priv_open_or_create
163 (DoCreate
164 , id
165 , size
166 , mode
167 , addr
168 , perm
169 , construct_func);
170 }
171
172 template <class DeviceId, class ConstructFunc>
173 managed_open_or_create_impl(open_only_t,
174 const DeviceId & id,
175 mode_t mode,
176 const void *addr,
177 const ConstructFunc &construct_func)
178 {
179 priv_open_or_create
180 ( DoOpen
181 , id
182 , 0
183 , mode
184 , addr
185 , permissions()
186 , construct_func);
187 }
188
189 template <class DeviceId, class ConstructFunc>
190 managed_open_or_create_impl(open_or_create_t,
191 const DeviceId & id,
192 std::size_t size,
193 mode_t mode,
194 const void *addr,
195 const ConstructFunc &construct_func,
196 const permissions &perm)
197 {
198 priv_open_or_create
199 ( DoOpenOrCreate
200 , id
201 , size
202 , mode
203 , addr
204 , perm
205 , construct_func);
206 }
207
208 managed_open_or_create_impl(BOOST_RV_REF(managed_open_or_create_impl) moved)
209 { this->swap(moved); }
210
211 managed_open_or_create_impl &operator=(BOOST_RV_REF(managed_open_or_create_impl) moved)
212 {
213 managed_open_or_create_impl tmp(boost::move(moved));
214 this->swap(tmp);
215 return *this;
216 }
217
218 ~managed_open_or_create_impl()
219 {}
220
221 std::size_t get_user_size() const
222 { return m_mapped_region.get_size() - ManagedOpenOrCreateUserOffset; }
223
224 void *get_user_address() const
225 { return static_cast<char*>(m_mapped_region.get_address()) + ManagedOpenOrCreateUserOffset; }
226
227 std::size_t get_real_size() const
228 { return m_mapped_region.get_size(); }
229
230 void *get_real_address() const
231 { return m_mapped_region.get_address(); }
232
233 void swap(managed_open_or_create_impl &other)
234 {
235 this->m_mapped_region.swap(other.m_mapped_region);
236 }
237
238 bool flush()
239 { return m_mapped_region.flush(); }
240
241 const mapped_region &get_mapped_region() const
242 { return m_mapped_region; }
243
244 DeviceAbstraction &get_device()
245 { return this->DevHolder::get_device(); }
246
247 const DeviceAbstraction &get_device() const
248 { return this->DevHolder::get_device(); }
249
250 private:
251
252 //These are templatized to allow explicit instantiations
253 template<bool dummy>
254 static void truncate_device(DeviceAbstraction &, offset_t, false_)
255 {} //Empty
256
257 template<bool dummy>
258 static void truncate_device(DeviceAbstraction &dev, offset_t size, true_)
259 { dev.truncate(size); }
260
261
262 template<bool dummy>
263 static bool check_offset_t_size(std::size_t , false_)
264 { return true; } //Empty
265
266 template<bool dummy>
267 static bool check_offset_t_size(std::size_t size, true_)
268 { return size == std::size_t(offset_t(size)); }
269
270 //These are templatized to allow explicit instantiations
271 template<bool dummy, class DeviceId>
272 static void create_device(DeviceAbstraction &dev, const DeviceId & id, std::size_t size, const permissions &perm, false_ /*file_like*/)
273 {
274 DeviceAbstraction tmp(create_only, id, read_write, size, perm);
275 tmp.swap(dev);
276 }
277
278 template<bool dummy, class DeviceId>
279 static void create_device(DeviceAbstraction &dev, const DeviceId & id, std::size_t, const permissions &perm, true_ /*file_like*/)
280 {
281 DeviceAbstraction tmp(create_only, id, read_write, perm);
282 tmp.swap(dev);
283 }
284
285 template <class DeviceId>
286 static bool do_create_else_open(DeviceAbstraction &dev, const DeviceId & id, std::size_t size, const permissions &perm)
287 {
288 //This loop is very ugly, but brute force is sometimes better
289 //than diplomacy. In POSIX file-based resources we can' know if we
290 //effectively created the file or not (there is no ERROR_ALREADY_EXISTS equivalent),
291 //so we try to create exclusively and fallback to open if already exists, with
292 //some retries if opening also fails because the file does not exist
293 //(there is a race, the creator just removed the file after creating it).
294 //
295 //We'll put a maximum retry limit just to avoid possible deadlocks, we don't
296 //want to support pathological use cases.
297 spin_wait swait;
298 unsigned tries = 0;
299 while(1){
300 BOOST_TRY{
301 create_device<FileBased>(dev, id, size, perm, file_like_t());
302 return true;
303 }
304 BOOST_CATCH(interprocess_exception &ex){
305 #ifndef BOOST_NO_EXCEPTIONS
306 if(ex.get_error_code() != already_exists_error){
307 BOOST_RETHROW
308 }
309 else if (++tries == MaxCreateOrOpenTries) {
310 //File existing when trying to create, but non-existing when
311 //trying to open, and tried MaxCreateOrOpenTries times. Something fishy
312 //is happening here and we can't solve it
313 throw interprocess_exception(error_info(corrupted_error));
314 }
315 else{
316 BOOST_TRY{
317 DeviceAbstraction tmp(open_only, id, read_write);
318 dev.swap(tmp);
319 return false;
320 }
321 BOOST_CATCH(interprocess_exception &e){
322 if(e.get_error_code() != not_found_error){
323 BOOST_RETHROW
324 }
325 }
326 BOOST_CATCH(...){
327 BOOST_RETHROW
328 } BOOST_CATCH_END
329 }
330 #endif //#ifndef BOOST_NO_EXCEPTIONS
331 }
332 BOOST_CATCH(...){
333 BOOST_RETHROW
334 } BOOST_CATCH_END
335 swait.yield();
336 }
337 return false;
338 }
339
340 template <class ConstructFunc>
341 static void do_map_after_create
342 (DeviceAbstraction &dev, mapped_region &final_region,
343 std::size_t size, const void *addr, ConstructFunc construct_func)
344 {
345 BOOST_TRY{
346 //If this throws, we are lost
347 truncate_device<FileBased>(dev, static_cast<offset_t>(size), file_like_t());
348
349 //If the following throws, we will truncate the file to 1
350 mapped_region region(dev, read_write, 0, 0, addr);
351 boost::uint32_t *patomic_word = 0; //avoid gcc warning
352 patomic_word = static_cast<boost::uint32_t*>(region.get_address());
353 boost::uint32_t previous = atomic_cas32(patomic_word, InitializingSegment, UninitializedSegment);
354
355 if(previous == UninitializedSegment){
356 BOOST_TRY{
357 construct_func( static_cast<char*>(region.get_address()) + ManagedOpenOrCreateUserOffset
358 , size - ManagedOpenOrCreateUserOffset, true);
359 //All ok, just move resources to the external mapped region
360 final_region.swap(region);
361 }
362 BOOST_CATCH(...){
363 atomic_write32(patomic_word, CorruptedSegment);
364 BOOST_RETHROW
365 } BOOST_CATCH_END
366 atomic_write32(patomic_word, InitializedSegment);
367 }
368 else{
369 atomic_write32(patomic_word, CorruptedSegment);
370 throw interprocess_exception(error_info(corrupted_error));
371 }
372 }
373 BOOST_CATCH(...){
374 BOOST_TRY{
375 truncate_device<FileBased>(dev, 1u, file_like_t());
376 }
377 BOOST_CATCH(...){
378 }
379 BOOST_CATCH_END
380 BOOST_RETHROW
381 }
382 BOOST_CATCH_END
383 }
384
385 template <class ConstructFunc>
386 static void do_map_after_open
387 ( DeviceAbstraction &dev, mapped_region &final_region
388 , const void *addr, ConstructFunc construct_func
389 , bool ronly, bool cow)
390 {
391 const usduration TimeoutSec(usduration_seconds(MaxInitializeTimeSec));
392
393 if(FileBased){
394 offset_t filesize = 0;
395 spin_wait swait;
396
397 //If a file device was used, the creator might be truncating the device, so wait
398 //until the file size is enough to map the initial word
399 ustime ustime_start = microsec_clock<ustime>::universal_time();
400
401 while(1){
402 if(!get_file_size(file_handle_from_mapping_handle(dev.get_mapping_handle()), filesize)){
403 error_info err = system_error_code();
404 throw interprocess_exception(err);
405 }
406 if (filesize != 0)
407 break;
408 else {
409 //More than MaxZeroTruncateTimeSec seconds waiting to the creator
410 //to minimally increase the size of the file: something bad has happened
411 const usduration elapsed(microsec_clock<ustime>::universal_time() - ustime_start);
412 if (elapsed > TimeoutSec){
413 throw interprocess_exception(error_info(corrupted_error));
414 }
415 swait.yield();
416 }
417 }
418 //The creator detected an error creating the file and signalled it with size 1
419 if(filesize == 1){
420 throw interprocess_exception(error_info(corrupted_error));
421 }
422 }
423
424 mapped_region region(dev, ronly ? read_only : (cow ? copy_on_write : read_write), 0, 0, addr);
425
426 boost::uint32_t *patomic_word = static_cast<boost::uint32_t*>(region.get_address());
427 boost::uint32_t value = atomic_read32(patomic_word);
428
429 if (value != InitializedSegment){
430 ustime ustime_start = microsec_clock<ustime>::universal_time();
431 spin_wait swait;
432 while ((value = atomic_read32(patomic_word)) != InitializedSegment){
433 if(value == CorruptedSegment){
434 throw interprocess_exception(error_info(corrupted_error));
435 }
436 //More than MaxZeroTruncateTimeSec seconds waiting to the creator
437 //to minimally increase the size of the file: something bad has happened
438 const usduration elapsed(microsec_clock<ustime>::universal_time() - ustime_start);
439 if (elapsed > TimeoutSec){
440 throw interprocess_exception(error_info(corrupted_error));
441 }
442 swait.yield();
443 }
444 //The size of the file might have grown while Uninitialized -> Initializing, so remap
445 {
446 mapped_region null_map;
447 region.swap(null_map);
448 }
449 mapped_region final_size_map(dev, ronly ? read_only : (cow ? copy_on_write : read_write), 0, 0, addr);
450 final_size_map.swap(region);
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 final_region.swap(region);
457 }
458
459 template <class DeviceId, class ConstructFunc> inline
460 void priv_open_or_create
461 (create_enum_t type,
462 const DeviceId & id,
463 std::size_t size,
464 mode_t mode, const void *addr,
465 const permissions &perm,
466 ConstructFunc construct_func)
467 {
468 if(type != DoOpen){
469 //Check if the requested size is enough to build the managed metadata
470 const std::size_t func_min_size = construct_func.get_min_size();
471 if( (std::size_t(-1) - ManagedOpenOrCreateUserOffset) < func_min_size ||
472 size < (func_min_size + ManagedOpenOrCreateUserOffset) ){
473 throw interprocess_exception(error_info(size_error));
474 }
475 //Check size can be represented by offset_t (used by truncate)
476 if (!check_offset_t_size<FileBased>(size, file_like_t())){
477 throw interprocess_exception(error_info(size_error));
478 }
479 }
480
481 //Now create the device (file, shm file, etc.)
482 DeviceAbstraction dev;
483 (void)mode;
484 bool created = false;
485 bool ronly = false;
486 bool cow = false;
487 if(type == DoOpen){
488 DeviceAbstraction tmp(open_only, id, mode == read_write ? read_write : read_only);
489 tmp.swap(dev);
490 ronly = mode == read_only;
491 cow = mode == copy_on_write;
492 }
493 else if(type == DoCreate){
494 create_device<FileBased>(dev, id, size, perm, file_like_t());
495 created = true;
496 }
497 else { //DoOpenOrCreate
498 created = this->do_create_else_open(dev, id, size, perm);
499 }
500
501 if(created){
502 this->do_map_after_create(dev, m_mapped_region, size, addr, construct_func);
503 }
504 else{
505 this->do_map_after_open(dev, m_mapped_region, addr, construct_func, ronly, cow);
506 }
507
508 if(StoreDevice){
509 this->DevHolder::get_device() = boost::move(dev);
510 }
511 }
512
513 friend void swap(managed_open_or_create_impl &left, managed_open_or_create_impl &right)
514 {
515 left.swap(right);
516 }
517
518 private:
519 friend class interprocess_tester;
520 void dont_close_on_destruction()
521 { interprocess_tester::dont_close_on_destruction(m_mapped_region); }
522
523 mapped_region m_mapped_region;
524 };
525
526 } //namespace ipcdetail {
527
528 } //namespace interprocess {
529 } //namespace boost {
530
531 #include <boost/interprocess/detail/config_end.hpp>
532
533 #endif //#ifndef BOOST_INTERPROCESS_MANAGED_OPEN_OR_CREATE_IMPL