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