shared_memory_object.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // (C) Copyright Ion Gaztanaga 2005-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_SHARED_MEMORY_OBJECT_HPP
  11. #define BOOST_INTERPROCESS_SHARED_MEMORY_OBJECT_HPP
  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/workaround.hpp>
  21. #include <boost/interprocess/creation_tags.hpp>
  22. #include <boost/interprocess/exceptions.hpp>
  23. #include <boost/move/utility_core.hpp>
  24. #include <boost/interprocess/interprocess_fwd.hpp>
  25. #include <boost/interprocess/exceptions.hpp>
  26. #include <boost/interprocess/detail/os_file_functions.hpp>
  27. #include <boost/interprocess/detail/shared_dir_helpers.hpp>
  28. #include <boost/interprocess/detail/char_wchar_holder.hpp>
  29. #include <boost/interprocess/permissions.hpp>
  30. #include <boost/move/adl_move_swap.hpp>
  31. #include <cstddef>
  32. #if defined(BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS)
  33. # include <string>
  34. # include <fcntl.h> //posix_fallocate, O_CREAT, O_*...
  35. # include <sys/mman.h> //shm_xxx
  36. # include <unistd.h> //ftruncate, close
  37. # include <sys/stat.h> //mode_t, S_IRWXG, S_IRWXO, S_IRWXU,
  38. # if defined(BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY)
  39. # if defined(__FreeBSD__)
  40. # include <sys/sysctl.h>
  41. # endif
  42. # endif
  43. #else
  44. //
  45. #endif
  46. //!\file
  47. //!Describes a shared memory object management class.
  48. namespace boost {
  49. namespace interprocess {
  50. //!A class that wraps a shared memory mapping that can be used to
  51. //!create mapped regions from the mapped files
  52. class shared_memory_object
  53. {
  54. #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
  55. //Non-copyable and non-assignable
  56. BOOST_MOVABLE_BUT_NOT_COPYABLE(shared_memory_object)
  57. #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
  58. public:
  59. //!Default constructor. Represents an empty shared_memory_object.
  60. shared_memory_object() BOOST_NOEXCEPT;
  61. //!Creates a shared memory object with name "name" and mode "mode", with the access mode "mode"
  62. //!If the file previously exists, throws an error.*/
  63. shared_memory_object(create_only_t, const char *name, mode_t mode, const permissions &perm = permissions())
  64. { this->priv_open_or_create(ipcdetail::DoCreate, name, mode, perm); }
  65. //!Tries to create a shared memory object with name "name" and mode "mode", with the
  66. //!access mode "mode". If the file previously exists, it tries to open it with mode "mode".
  67. //!Otherwise throws an error.
  68. shared_memory_object(open_or_create_t, const char *name, mode_t mode, const permissions &perm = permissions())
  69. { this->priv_open_or_create(ipcdetail::DoOpenOrCreate, name, mode, perm); }
  70. //!Tries to open a shared memory object with name "name", with the access mode "mode".
  71. //!If the file does not previously exist, it throws an error.
  72. shared_memory_object(open_only_t, const char *name, mode_t mode)
  73. { this->priv_open_or_create(ipcdetail::DoOpen, name, mode, permissions()); }
  74. #if defined(BOOST_INTERPROCESS_WCHAR_NAMED_RESOURCES) || defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
  75. //!Creates a shared memory object with name "name" and mode "mode", with the access mode "mode"
  76. //!If the file previously exists, throws an error.
  77. //!
  78. //!Note: This function is only available on operating systems with
  79. //! native wchar_t APIs (e.g. Windows).
  80. shared_memory_object(create_only_t, const wchar_t*name, mode_t mode, const permissions &perm = permissions())
  81. { this->priv_open_or_create(ipcdetail::DoCreate, name, mode, perm); }
  82. //!Tries to create a shared memory object with name "name" and mode "mode", with the
  83. //!access mode "mode". If the file previously exists, it tries to open it with mode "mode".
  84. //!Otherwise throws an error.
  85. //!
  86. //!Note: This function is only available on operating systems with
  87. //! native wchar_t APIs (e.g. Windows).
  88. shared_memory_object(open_or_create_t, const wchar_t*name, mode_t mode, const permissions &perm = permissions())
  89. { this->priv_open_or_create(ipcdetail::DoOpenOrCreate, name, mode, perm); }
  90. //!Tries to open a shared memory object with name "name", with the access mode "mode".
  91. //!If the file does not previously exist, it throws an error.
  92. //!
  93. //!Note: This function is only available on operating systems with
  94. //! native wchar_t APIs (e.g. Windows).
  95. shared_memory_object(open_only_t, const wchar_t*name, mode_t mode)
  96. { this->priv_open_or_create(ipcdetail::DoOpen, name, mode, permissions()); }
  97. #endif //defined(BOOST_INTERPROCESS_WCHAR_NAMED_RESOURCES) || defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
  98. //!Moves the ownership of "moved"'s shared memory object to *this.
  99. //!After the call, "moved" does not represent any shared memory object.
  100. //!Does not throw
  101. shared_memory_object(BOOST_RV_REF(shared_memory_object) moved) BOOST_NOEXCEPT
  102. : m_handle(file_handle_t(ipcdetail::invalid_file()))
  103. , m_mode(read_only)
  104. { this->swap(moved); }
  105. //!Moves the ownership of "moved"'s shared memory to *this.
  106. //!After the call, "moved" does not represent any shared memory.
  107. //!Does not throw
  108. shared_memory_object &operator=(BOOST_RV_REF(shared_memory_object) moved) BOOST_NOEXCEPT
  109. {
  110. shared_memory_object tmp(boost::move(moved));
  111. this->swap(tmp);
  112. return *this;
  113. }
  114. //!Swaps the shared_memory_objects. Does not throw
  115. void swap(shared_memory_object &moved) BOOST_NOEXCEPT;
  116. //!Erases a shared memory object from the system.
  117. //!Returns false on error. Never throws
  118. static bool remove(const char *name);
  119. #if defined(BOOST_INTERPROCESS_WCHAR_NAMED_RESOURCES) || defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
  120. //!Erases a shared memory object from the system.
  121. //!Returns false on error. Never throws
  122. //!
  123. //!Note: This function is only available on operating systems with
  124. //! native wchar_t APIs (e.g. Windows).
  125. static bool remove(const wchar_t *name);
  126. #endif //defined(BOOST_INTERPROCESS_WCHAR_NAMED_RESOURCES) || defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
  127. //!Sets the size of the shared memory mapping
  128. void truncate(offset_t length);
  129. //!Destroys *this and indicates that the calling process is finished using
  130. //!the resource. All mapped regions are still
  131. //!valid after destruction. The destructor function will deallocate
  132. //!any system resources allocated by the system for use by this process for
  133. //!this resource. The resource can still be opened again calling
  134. //!the open constructor overload. To erase the resource from the system
  135. //!use remove().
  136. ~shared_memory_object();
  137. //!Returns the name of the shared memory object.
  138. const char *get_name() const BOOST_NOEXCEPT;
  139. //!Returns true if the size of the shared memory object
  140. //!can be obtained and writes the size in the passed reference
  141. bool get_size(offset_t &size) const BOOST_NOEXCEPT;
  142. //!Returns access mode
  143. mode_t get_mode() const BOOST_NOEXCEPT;
  144. //!Returns mapping handle. Never throws.
  145. mapping_handle_t get_mapping_handle() const BOOST_NOEXCEPT;
  146. #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
  147. private:
  148. //!Closes a previously opened file mapping. Never throws.
  149. void priv_close();
  150. //!Opens or creates a shared memory object.
  151. template<class CharT>
  152. bool priv_open_or_create(ipcdetail::create_enum_t type, const CharT *filename, mode_t mode, const permissions &perm);
  153. file_handle_t m_handle;
  154. mode_t m_mode;
  155. char_wchar_holder m_filename;
  156. #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
  157. };
  158. #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
  159. inline shared_memory_object::shared_memory_object() BOOST_NOEXCEPT
  160. : m_handle(file_handle_t(ipcdetail::invalid_file()))
  161. , m_mode(read_only)
  162. {}
  163. inline shared_memory_object::~shared_memory_object()
  164. { this->priv_close(); }
  165. inline const char *shared_memory_object::get_name() const BOOST_NOEXCEPT
  166. { return m_filename.getn(); }
  167. inline bool shared_memory_object::get_size(offset_t &size) const BOOST_NOEXCEPT
  168. { return ipcdetail::get_file_size((file_handle_t)m_handle, size); }
  169. inline void shared_memory_object::swap(shared_memory_object &other) BOOST_NOEXCEPT
  170. {
  171. boost::adl_move_swap(m_handle, other.m_handle);
  172. boost::adl_move_swap(m_mode, other.m_mode);
  173. m_filename.swap(other.m_filename);
  174. }
  175. inline mapping_handle_t shared_memory_object::get_mapping_handle() const BOOST_NOEXCEPT
  176. {
  177. return ipcdetail::mapping_handle_from_file_handle(m_handle);
  178. }
  179. inline mode_t shared_memory_object::get_mode() const BOOST_NOEXCEPT
  180. { return m_mode; }
  181. #if !defined(BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS)
  182. template<class CharT>
  183. inline bool shared_memory_object::priv_open_or_create
  184. (ipcdetail::create_enum_t type, const CharT *filename, mode_t mode, const permissions &perm)
  185. {
  186. m_filename = filename;
  187. std::basic_string<CharT> shmfile;
  188. ipcdetail::create_shared_dir_cleaning_old_and_get_filepath(filename, shmfile);
  189. //Set accesses
  190. if (mode != read_write && mode != read_only){
  191. error_info err = other_error;
  192. throw interprocess_exception(err);
  193. }
  194. switch(type){
  195. case ipcdetail::DoOpen:
  196. m_handle = ipcdetail::open_existing_file(shmfile.c_str(), mode, true);
  197. break;
  198. case ipcdetail::DoCreate:
  199. m_handle = ipcdetail::create_new_file(shmfile.c_str(), mode, perm, true);
  200. break;
  201. case ipcdetail::DoOpenOrCreate:
  202. m_handle = ipcdetail::create_or_open_file(shmfile.c_str(), mode, perm, true);
  203. break;
  204. default:
  205. {
  206. error_info err = other_error;
  207. throw interprocess_exception(err);
  208. }
  209. }
  210. //Check for error
  211. if(m_handle == ipcdetail::invalid_file()){
  212. error_info err = system_error_code();
  213. this->priv_close();
  214. throw interprocess_exception(err);
  215. }
  216. m_mode = mode;
  217. return true;
  218. }
  219. #if defined(BOOST_INTERPROCESS_WCHAR_NAMED_RESOURCES)
  220. inline bool shared_memory_object::remove(const wchar_t *filename)
  221. {
  222. BOOST_TRY{
  223. //Make sure a temporary path is created for shared memory
  224. std::wstring shmfile;
  225. ipcdetail::shared_filepath(filename, shmfile);
  226. return ipcdetail::delete_file(shmfile.c_str());
  227. }
  228. BOOST_CATCH(...){
  229. return false;
  230. } BOOST_CATCH_END
  231. }
  232. #endif
  233. inline bool shared_memory_object::remove(const char *filename)
  234. {
  235. BOOST_TRY{
  236. //Make sure a temporary path is created for shared memory
  237. std::string shmfile;
  238. ipcdetail::shared_filepath(filename, shmfile);
  239. return ipcdetail::delete_file(shmfile.c_str());
  240. }
  241. BOOST_CATCH(...){
  242. return false;
  243. } BOOST_CATCH_END
  244. }
  245. inline void shared_memory_object::truncate(offset_t length)
  246. {
  247. if(!ipcdetail::truncate_file(m_handle, (std::size_t)length)){
  248. error_info err = system_error_code();
  249. throw interprocess_exception(err);
  250. }
  251. }
  252. inline void shared_memory_object::priv_close()
  253. {
  254. if(m_handle != ipcdetail::invalid_file()){
  255. ipcdetail::close_file(m_handle);
  256. m_handle = ipcdetail::invalid_file();
  257. }
  258. }
  259. #else //!defined(BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS)
  260. namespace shared_memory_object_detail {
  261. #ifdef BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY
  262. #if defined(__FreeBSD__)
  263. inline bool use_filesystem_based_posix()
  264. {
  265. int jailed = 0;
  266. std::size_t len = sizeof(jailed);
  267. ::sysctlbyname("security.jail.jailed", &jailed, &len, NULL, 0);
  268. return jailed != 0;
  269. }
  270. #else
  271. #error "Not supported platform for BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY"
  272. #endif
  273. #endif
  274. } //shared_memory_object_detail
  275. template<class CharT>
  276. inline bool shared_memory_object::priv_open_or_create
  277. (ipcdetail::create_enum_t type,
  278. const CharT *filename,
  279. mode_t mode, const permissions &perm)
  280. {
  281. #if defined(BOOST_INTERPROCESS_FILESYSTEM_BASED_POSIX_SHARED_MEMORY)
  282. const bool add_leading_slash = false;
  283. #elif defined(BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY)
  284. const bool add_leading_slash = !shared_memory_object_detail::use_filesystem_based_posix();
  285. #else
  286. const bool add_leading_slash = true;
  287. #endif
  288. std::basic_string<CharT> fname;
  289. if(add_leading_slash){
  290. ipcdetail::add_leading_slash(filename, fname);
  291. }
  292. else{
  293. ipcdetail::create_shared_dir_cleaning_old_and_get_filepath(filename, fname);
  294. }
  295. //Create new mapping
  296. int oflag = 0;
  297. if(mode == read_only){
  298. oflag |= O_RDONLY;
  299. }
  300. else if(mode == read_write){
  301. oflag |= O_RDWR;
  302. }
  303. else{
  304. error_info err(mode_error);
  305. throw interprocess_exception(err);
  306. }
  307. ::mode_t unix_perm = perm.get_permissions();
  308. switch(type){
  309. case ipcdetail::DoOpen:
  310. {
  311. //No oflag addition
  312. m_handle = shm_open(fname.c_str(), oflag, unix_perm);
  313. }
  314. break;
  315. case ipcdetail::DoCreate:
  316. {
  317. oflag |= (O_CREAT | O_EXCL);
  318. m_handle = shm_open(fname.c_str(), oflag, unix_perm);
  319. if(m_handle >= 0){
  320. ::fchmod(m_handle, unix_perm);
  321. }
  322. }
  323. break;
  324. case ipcdetail::DoOpenOrCreate:
  325. {
  326. //We need a create/open loop to change permissions correctly using fchmod, since
  327. //with "O_CREAT" only we don't know if we've created or opened the shm.
  328. while(true){
  329. //Try to create shared memory
  330. m_handle = shm_open(fname.c_str(), oflag | (O_CREAT | O_EXCL), unix_perm);
  331. //If successful change real permissions
  332. if(m_handle >= 0){
  333. ::fchmod(m_handle, unix_perm);
  334. }
  335. //If already exists, try to open
  336. else if(errno == EEXIST){
  337. m_handle = shm_open(fname.c_str(), oflag, unix_perm);
  338. //If open fails and errno tells the file does not exist
  339. //(shm was removed between creation and opening tries), just retry
  340. if(m_handle < 0 && errno == ENOENT){
  341. continue;
  342. }
  343. }
  344. //Exit retries
  345. break;
  346. }
  347. }
  348. break;
  349. default:
  350. {
  351. error_info err = other_error;
  352. throw interprocess_exception(err);
  353. }
  354. }
  355. //Check for error
  356. if(m_handle < 0){
  357. error_info err = errno;
  358. this->priv_close();
  359. throw interprocess_exception(err);
  360. }
  361. m_filename = filename;
  362. m_mode = mode;
  363. return true;
  364. }
  365. inline bool shared_memory_object::remove(const char *filename)
  366. {
  367. BOOST_TRY{
  368. std::string filepath;
  369. #if defined(BOOST_INTERPROCESS_FILESYSTEM_BASED_POSIX_SHARED_MEMORY)
  370. const bool add_leading_slash = false;
  371. #elif defined(BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY)
  372. const bool add_leading_slash = !shared_memory_object_detail::use_filesystem_based_posix();
  373. #else
  374. const bool add_leading_slash = true;
  375. #endif
  376. if(add_leading_slash){
  377. ipcdetail::add_leading_slash(filename, filepath);
  378. }
  379. else{
  380. ipcdetail::shared_filepath(filename, filepath);
  381. }
  382. return 0 == shm_unlink(filepath.c_str());
  383. }
  384. BOOST_CATCH(...){
  385. return false;
  386. } BOOST_CATCH_END
  387. }
  388. inline void shared_memory_object::truncate(offset_t length)
  389. {
  390. #ifdef BOOST_INTERPROCESS_POSIX_FALLOCATE
  391. int ret = EINTR;
  392. while (EINTR == ret) {
  393. ret = posix_fallocate(m_handle, 0, length);
  394. }
  395. if (ret && ret != EOPNOTSUPP && ret != ENODEV){
  396. error_info err(ret);
  397. throw interprocess_exception(err);
  398. }
  399. //ftruncate fallback
  400. #endif //BOOST_INTERPROCESS_POSIX_FALLOCATE
  401. handle_eintr:
  402. if (0 != ftruncate(m_handle, length)){
  403. if (errno == EINTR)
  404. goto handle_eintr;
  405. error_info err(system_error_code());
  406. throw interprocess_exception(err);
  407. }
  408. }
  409. inline void shared_memory_object::priv_close()
  410. {
  411. if(m_handle != -1){
  412. ::close(m_handle);
  413. m_handle = -1;
  414. }
  415. }
  416. #endif
  417. //!A class that stores the name of a shared memory
  418. //!and calls shared_memory_object::remove(name) in its destructor
  419. //!Useful to remove temporary shared memory objects in the presence
  420. //!of exceptions
  421. class remove_shared_memory_on_destroy
  422. {
  423. const char * m_name;
  424. public:
  425. remove_shared_memory_on_destroy(const char *name)
  426. : m_name(name)
  427. {}
  428. ~remove_shared_memory_on_destroy()
  429. { shared_memory_object::remove(m_name); }
  430. };
  431. #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
  432. } //namespace interprocess {
  433. } //namespace boost {
  434. #include <boost/interprocess/detail/config_end.hpp>
  435. #endif //BOOST_INTERPROCESS_SHARED_MEMORY_OBJECT_HPP