managed_open_or_create_impl.hpp 19 KB

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