shared_memory_object.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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. #ifdef BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS
  157. static int eintr_aware_shm_open(const char* name, int oflag, ::mode_t mode);
  158. #endif
  159. #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
  160. };
  161. #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED)
  162. inline shared_memory_object::shared_memory_object() BOOST_NOEXCEPT
  163. : m_handle(file_handle_t(ipcdetail::invalid_file()))
  164. , m_mode(read_only)
  165. {}
  166. inline shared_memory_object::~shared_memory_object()
  167. { this->priv_close(); }
  168. inline const char *shared_memory_object::get_name() const BOOST_NOEXCEPT
  169. { return m_filename.getn(); }
  170. inline bool shared_memory_object::get_size(offset_t &size) const BOOST_NOEXCEPT
  171. { return ipcdetail::get_file_size((file_handle_t)m_handle, size); }
  172. inline void shared_memory_object::swap(shared_memory_object &other) BOOST_NOEXCEPT
  173. {
  174. boost::adl_move_swap(m_handle, other.m_handle);
  175. boost::adl_move_swap(m_mode, other.m_mode);
  176. m_filename.swap(other.m_filename);
  177. }
  178. inline mapping_handle_t shared_memory_object::get_mapping_handle() const BOOST_NOEXCEPT
  179. {
  180. return ipcdetail::mapping_handle_from_file_handle(m_handle);
  181. }
  182. inline mode_t shared_memory_object::get_mode() const BOOST_NOEXCEPT
  183. { return m_mode; }
  184. #if !defined(BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS)
  185. template<class CharT>
  186. inline bool shared_memory_object::priv_open_or_create
  187. (ipcdetail::create_enum_t type, const CharT *filename, mode_t mode, const permissions &perm)
  188. {
  189. m_filename = filename;
  190. std::basic_string<CharT> shmfile;
  191. ipcdetail::create_shared_dir_cleaning_old_and_get_filepath(filename, shmfile);
  192. //Set accesses
  193. if (mode != read_write && mode != read_only){
  194. error_info err = other_error;
  195. throw interprocess_exception(err);
  196. }
  197. switch(type){
  198. case ipcdetail::DoOpen:
  199. m_handle = ipcdetail::open_existing_file(shmfile.c_str(), mode, true);
  200. break;
  201. case ipcdetail::DoCreate:
  202. m_handle = ipcdetail::create_new_file(shmfile.c_str(), mode, perm, true);
  203. break;
  204. case ipcdetail::DoOpenOrCreate:
  205. m_handle = ipcdetail::create_or_open_file(shmfile.c_str(), mode, perm, true);
  206. break;
  207. default:
  208. {
  209. error_info err = other_error;
  210. throw interprocess_exception(err);
  211. }
  212. }
  213. //Check for error
  214. if(m_handle == ipcdetail::invalid_file()){
  215. error_info err = system_error_code();
  216. this->priv_close();
  217. throw interprocess_exception(err);
  218. }
  219. m_mode = mode;
  220. return true;
  221. }
  222. #if defined(BOOST_INTERPROCESS_WCHAR_NAMED_RESOURCES)
  223. inline bool shared_memory_object::remove(const wchar_t *filename)
  224. {
  225. BOOST_INTERPROCESS_TRY{
  226. //Make sure a temporary path is created for shared memory
  227. std::wstring shmfile;
  228. ipcdetail::shared_filepath(filename, shmfile);
  229. return ipcdetail::delete_file(shmfile.c_str());
  230. }
  231. BOOST_INTERPROCESS_CATCH(...){
  232. return false;
  233. } BOOST_INTERPROCESS_CATCH_END
  234. }
  235. #endif
  236. inline bool shared_memory_object::remove(const char *filename)
  237. {
  238. BOOST_INTERPROCESS_TRY{
  239. //Make sure a temporary path is created for shared memory
  240. std::string shmfile;
  241. ipcdetail::shared_filepath(filename, shmfile);
  242. return ipcdetail::delete_file(shmfile.c_str());
  243. }
  244. BOOST_INTERPROCESS_CATCH(...){
  245. return false;
  246. } BOOST_INTERPROCESS_CATCH_END
  247. }
  248. inline void shared_memory_object::truncate(offset_t length)
  249. {
  250. if(!ipcdetail::truncate_file(m_handle, (std::size_t)length)){
  251. error_info err = system_error_code();
  252. throw interprocess_exception(err);
  253. }
  254. }
  255. inline void shared_memory_object::priv_close()
  256. {
  257. if(m_handle != ipcdetail::invalid_file()){
  258. ipcdetail::close_file(m_handle);
  259. m_handle = ipcdetail::invalid_file();
  260. }
  261. }
  262. #else //!defined(BOOST_INTERPROCESS_POSIX_SHARED_MEMORY_OBJECTS)
  263. namespace shared_memory_object_detail {
  264. #ifdef BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY
  265. #if defined(__FreeBSD__)
  266. inline bool use_filesystem_based_posix()
  267. {
  268. int jailed = 0;
  269. std::size_t len = sizeof(jailed);
  270. ::sysctlbyname("security.jail.jailed", &jailed, &len, NULL, 0);
  271. return jailed != 0;
  272. }
  273. #else
  274. #error "Not supported platform for BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY"
  275. #endif
  276. #endif
  277. } //shared_memory_object_detail
  278. template<class CharT>
  279. inline bool shared_memory_object::priv_open_or_create
  280. (ipcdetail::create_enum_t type,
  281. const CharT *filename,
  282. mode_t mode, const permissions &perm)
  283. {
  284. #if defined(BOOST_INTERPROCESS_FILESYSTEM_BASED_POSIX_SHARED_MEMORY)
  285. const bool add_leading_slash = false;
  286. #elif defined(BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY)
  287. const bool add_leading_slash = !shared_memory_object_detail::use_filesystem_based_posix();
  288. #else
  289. const bool add_leading_slash = true;
  290. #endif
  291. std::basic_string<CharT> fname;
  292. if(add_leading_slash){
  293. ipcdetail::add_leading_slash(filename, fname);
  294. }
  295. else{
  296. ipcdetail::create_shared_dir_cleaning_old_and_get_filepath(filename, fname);
  297. }
  298. //Create new mapping
  299. int oflag = 0;
  300. if(mode == read_only){
  301. oflag |= O_RDONLY;
  302. }
  303. else if(mode == read_write){
  304. oflag |= O_RDWR;
  305. }
  306. else{
  307. error_info err(mode_error);
  308. throw interprocess_exception(err);
  309. }
  310. ::mode_t unix_perm = perm.get_permissions();
  311. switch(type){
  312. case ipcdetail::DoOpen:
  313. {
  314. //No oflag addition
  315. m_handle = eintr_aware_shm_open(fname.c_str(), oflag, unix_perm);
  316. }
  317. break;
  318. case ipcdetail::DoCreate:
  319. {
  320. oflag |= (O_CREAT | O_EXCL);
  321. m_handle = eintr_aware_shm_open(fname.c_str(), oflag, unix_perm);
  322. if(m_handle >= 0){
  323. ::fchmod(m_handle, unix_perm);
  324. }
  325. }
  326. break;
  327. case ipcdetail::DoOpenOrCreate:
  328. {
  329. //We need a create/open loop to change permissions correctly using fchmod, since
  330. //with "O_CREAT" only we don't know if we've created or opened the shm.
  331. while(true){
  332. //Try to create shared memory
  333. m_handle = eintr_aware_shm_open(fname.c_str(), oflag | (O_CREAT | O_EXCL), unix_perm);
  334. //If successful change real permissions
  335. if(m_handle >= 0){
  336. ::fchmod(m_handle, unix_perm);
  337. }
  338. //If already exists, try to open
  339. else if(errno == EEXIST){
  340. m_handle = eintr_aware_shm_open(fname.c_str(), oflag, unix_perm);
  341. //If open fails and errno tells the file does not exist
  342. //(shm was removed between creation and opening tries), just retry
  343. if(m_handle < 0 && errno == ENOENT){
  344. continue;
  345. }
  346. }
  347. //Exit retries
  348. break;
  349. }
  350. }
  351. break;
  352. default:
  353. {
  354. error_info err = other_error;
  355. throw interprocess_exception(err);
  356. }
  357. }
  358. //Check for error
  359. if(m_handle < 0){
  360. error_info err = errno;
  361. this->priv_close();
  362. throw interprocess_exception(err);
  363. }
  364. m_filename = filename;
  365. m_mode = mode;
  366. return true;
  367. }
  368. inline bool shared_memory_object::remove(const char *filename)
  369. {
  370. BOOST_INTERPROCESS_TRY{
  371. std::string filepath;
  372. #if defined(BOOST_INTERPROCESS_FILESYSTEM_BASED_POSIX_SHARED_MEMORY)
  373. const bool add_leading_slash = false;
  374. #elif defined(BOOST_INTERPROCESS_RUNTIME_FILESYSTEM_BASED_POSIX_SHARED_MEMORY)
  375. const bool add_leading_slash = !shared_memory_object_detail::use_filesystem_based_posix();
  376. #else
  377. const bool add_leading_slash = true;
  378. #endif
  379. if(add_leading_slash){
  380. ipcdetail::add_leading_slash(filename, filepath);
  381. }
  382. else{
  383. ipcdetail::shared_filepath(filename, filepath);
  384. }
  385. return 0 == shm_unlink(filepath.c_str());
  386. }
  387. BOOST_INTERPROCESS_CATCH(...){
  388. return false;
  389. } BOOST_INTERPROCESS_CATCH_END
  390. }
  391. inline void shared_memory_object::truncate(offset_t length)
  392. {
  393. #ifdef BOOST_INTERPROCESS_POSIX_FALLOCATE
  394. int ret = EINTR;
  395. while (EINTR == ret) {
  396. ret = posix_fallocate(m_handle, 0, length);
  397. }
  398. if (ret && ret != EOPNOTSUPP && ret != ENODEV){
  399. error_info err(ret);
  400. throw interprocess_exception(err);
  401. }
  402. //ftruncate fallback
  403. #endif //BOOST_INTERPROCESS_POSIX_FALLOCATE
  404. handle_eintr:
  405. if (0 != ftruncate(m_handle, length)){
  406. if (errno == EINTR)
  407. goto handle_eintr;
  408. error_info err(system_error_code());
  409. throw interprocess_exception(err);
  410. }
  411. }
  412. inline void shared_memory_object::priv_close()
  413. {
  414. if(m_handle != -1){
  415. ::close(m_handle);
  416. m_handle = -1;
  417. }
  418. }
  419. inline int shared_memory_object::eintr_aware_shm_open(const char* name, int oflag, ::mode_t mode)
  420. {
  421. int shm_open_ret;
  422. do {
  423. shm_open_ret = shm_open(name, oflag, mode);
  424. } while (shm_open_ret == -1 && errno == EINTR);
  425. return shm_open_ret;
  426. }
  427. #endif
  428. //!A class that stores the name of a shared memory
  429. //!and calls shared_memory_object::remove(name) in its destructor
  430. //!Useful to remove temporary shared memory objects in the presence
  431. //!of exceptions
  432. class remove_shared_memory_on_destroy
  433. {
  434. const char * m_name;
  435. public:
  436. remove_shared_memory_on_destroy(const char *name)
  437. : m_name(name)
  438. {}
  439. ~remove_shared_memory_on_destroy()
  440. { shared_memory_object::remove(m_name); }
  441. };
  442. #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED
  443. } //namespace interprocess {
  444. } //namespace boost {
  445. #include <boost/interprocess/detail/config_end.hpp>
  446. #endif //BOOST_INTERPROCESS_SHARED_MEMORY_OBJECT_HPP