portable_intermodule_singleton.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // (C) Copyright Ion Gaztanaga 2009-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_PORTABLE_INTERMODULE_SINGLETON_HPP
  11. #define BOOST_INTERPROCESS_PORTABLE_INTERMODULE_SINGLETON_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/detail/managed_global_memory.hpp>
  22. #include <boost/interprocess/detail/intermodule_singleton_common.hpp>
  23. #include <boost/interprocess/shared_memory_object.hpp>
  24. #include <boost/interprocess/detail/atomic.hpp>
  25. #include <boost/interprocess/detail/os_thread_functions.hpp>
  26. #include <boost/interprocess/detail/shared_dir_helpers.hpp>
  27. #include <boost/interprocess/detail/os_file_functions.hpp>
  28. #include <boost/interprocess/detail/file_locking_helpers.hpp>
  29. #include <boost/assert.hpp>
  30. #include <cstddef>
  31. #include <cstdio>
  32. #include <cstring>
  33. #include <string>
  34. namespace boost{
  35. namespace interprocess{
  36. namespace ipcdetail{
  37. typedef basic_managed_global_memory<shared_memory_object, true> managed_global_memory;
  38. namespace intermodule_singleton_helpers {
  39. static void create_tmp_subdir_and_get_pid_based_filepath
  40. (const char *subdir_name, const char *file_prefix, OS_process_id_t pid, std::string &s, bool creation_time = false)
  41. {
  42. //Let's create a lock file for each process gmem that will mark if
  43. //the process is alive or not
  44. create_shared_dir_and_clean_old(s);
  45. s += "/";
  46. s += subdir_name;
  47. if(!open_or_create_directory(s.c_str())){
  48. error_info err = system_error_code();
  49. throw interprocess_exception(err);
  50. }
  51. s += "/";
  52. s += file_prefix;
  53. if(creation_time){
  54. std::string sstamp;
  55. get_pid_creation_time_str(sstamp);
  56. s += sstamp;
  57. }
  58. else{
  59. pid_str_t pid_str;
  60. get_pid_str(pid_str, pid);
  61. s += pid_str;
  62. }
  63. }
  64. static bool check_if_filename_complies_with_pid
  65. (const char *filename, const char *prefix, OS_process_id_t pid, std::string &file_suffix, bool creation_time = false)
  66. {
  67. //Check if filename complies with lock file name pattern
  68. std::string fname(filename);
  69. std::string fprefix(prefix);
  70. if(fname.size() <= fprefix.size()){
  71. return false;
  72. }
  73. fname.resize(fprefix.size());
  74. if(fname != fprefix){
  75. return false;
  76. }
  77. //If not our lock file, delete it if we can lock it
  78. fname = filename;
  79. fname.erase(0, fprefix.size());
  80. pid_str_t pid_str;
  81. get_pid_str(pid_str, pid);
  82. file_suffix = pid_str;
  83. if(creation_time){
  84. std::size_t p = fname.find('_');
  85. if (p == std::string::npos){
  86. return false;
  87. }
  88. std::string save_suffix(fname);
  89. fname.erase(p);
  90. fname.swap(file_suffix);
  91. bool ret = (file_suffix == fname);
  92. file_suffix.swap(save_suffix);
  93. return ret;
  94. }
  95. else{
  96. fname.swap(file_suffix);
  97. return (file_suffix == fname);
  98. }
  99. }
  100. template<>
  101. struct thread_safe_global_map_dependant<managed_global_memory>
  102. {
  103. private:
  104. static const int GMemMarkToBeRemoved = -1;
  105. static const int GMemNotPresent = -2;
  106. static const char *get_lock_file_subdir_name()
  107. { return "gmem"; }
  108. static const char *get_lock_file_base_name()
  109. { return "lck"; }
  110. static void create_and_get_singleton_lock_file_path(std::string &s)
  111. {
  112. create_tmp_subdir_and_get_pid_based_filepath
  113. (get_lock_file_subdir_name(), get_lock_file_base_name(), get_current_process_id(), s, true);
  114. }
  115. struct gmem_erase_func
  116. {
  117. gmem_erase_func(const char *shm_name, const char *singleton_lock_file_path, managed_global_memory & shm)
  118. :shm_name_(shm_name), singleton_lock_file_path_(singleton_lock_file_path), shm_(shm)
  119. {}
  120. void operator()()
  121. {
  122. locking_file_serial_id *pserial_id = shm_.find<locking_file_serial_id>("lock_file_fd").first;
  123. if(pserial_id){
  124. pserial_id->fd = GMemMarkToBeRemoved;
  125. }
  126. delete_file(singleton_lock_file_path_);
  127. shared_memory_object::remove(shm_name_);
  128. }
  129. const char * const shm_name_;
  130. const char * const singleton_lock_file_path_;
  131. managed_global_memory & shm_;
  132. };
  133. //This function applies shared memory erasure logic based on the passed lock file.
  134. static void apply_gmem_erase_logic(const char *filepath, const char *filename)
  135. {
  136. int fd = GMemMarkToBeRemoved;
  137. try{
  138. std::string str;
  139. //If the filename is current process lock file, then avoid it
  140. if(check_if_filename_complies_with_pid
  141. (filename, get_lock_file_base_name(), get_current_process_id(), str, true)){
  142. return;
  143. }
  144. //Open and lock the other process' lock file
  145. fd = try_open_and_lock_file(filepath);
  146. if(fd < 0){
  147. return;
  148. }
  149. //If done, then the process is dead so take global shared memory name
  150. //(the name is based on the lock file name) and try to apply erasure logic
  151. str.insert(0, get_map_base_name());
  152. try{
  153. managed_global_memory shm(open_only, str.c_str());
  154. gmem_erase_func func(str.c_str(), filepath, shm);
  155. shm.try_atomic_func(func);
  156. }
  157. catch(interprocess_exception &e){
  158. //If shared memory is not found erase the lock file
  159. if(e.get_error_code() == not_found_error){
  160. delete_file(filepath);
  161. }
  162. }
  163. }
  164. catch(...){
  165. }
  166. if(fd >= 0){
  167. close_lock_file(fd);
  168. }
  169. }
  170. public:
  171. static bool remove_old_gmem()
  172. {
  173. std::string refcstrRootDirectory;
  174. get_shared_dir(refcstrRootDirectory);
  175. refcstrRootDirectory += "/";
  176. refcstrRootDirectory += get_lock_file_subdir_name();
  177. return for_each_file_in_dir(refcstrRootDirectory.c_str(), apply_gmem_erase_logic);
  178. }
  179. struct lock_file_logic
  180. {
  181. lock_file_logic(managed_global_memory &shm)
  182. : mshm(shm)
  183. { shm.atomic_func(*this); }
  184. void operator()(void)
  185. {
  186. retry_with_new_map = false;
  187. //First find the file locking descriptor id
  188. locking_file_serial_id *pserial_id =
  189. mshm.find<locking_file_serial_id>("lock_file_fd").first;
  190. int fd;
  191. //If not found schedule a creation
  192. if(!pserial_id){
  193. fd = GMemNotPresent;
  194. }
  195. //Else get it
  196. else{
  197. fd = pserial_id->fd;
  198. }
  199. //If we need to create a new one, do it
  200. if(fd == GMemNotPresent){
  201. std::string lck_str;
  202. //Create a unique current pid based lock file path
  203. create_and_get_singleton_lock_file_path(lck_str);
  204. //Open or create and lock file
  205. int fd_lockfile = open_or_create_and_lock_file(lck_str.c_str());
  206. //If failed, write a bad file descriptor to notify other modules that
  207. //something was wrong and unlink shared memory. Mark the function object
  208. //to tell caller to retry with another shared memory
  209. if(fd_lockfile < 0){
  210. this->register_lock_file(GMemMarkToBeRemoved);
  211. std::string s;
  212. get_map_name(s);
  213. shared_memory_object::remove(s.c_str());
  214. retry_with_new_map = true;
  215. }
  216. //If successful, register the file descriptor
  217. else{
  218. this->register_lock_file(fd_lockfile);
  219. }
  220. }
  221. //If the fd was invalid (maybe a previous try failed) notify caller that
  222. //should retry creation logic, since this shm might have been already
  223. //unlinked since the shm was removed
  224. else if (fd == GMemMarkToBeRemoved){
  225. retry_with_new_map = true;
  226. }
  227. //If the stored fd is not valid (a open fd, a normal file with the
  228. //expected size, or does not have the same file id number,
  229. //then it's an old shm from an old process with the same pid.
  230. //If that's the case, mark it as invalid
  231. else if(!is_valid_fd(fd) ||
  232. !is_normal_file(fd) ||
  233. 0 != get_size(fd) ||
  234. !compare_file_serial(fd, *pserial_id)){
  235. pserial_id->fd = GMemMarkToBeRemoved;
  236. std::string s;
  237. get_map_name(s);
  238. shared_memory_object::remove(s.c_str());
  239. retry_with_new_map = true;
  240. }
  241. else{
  242. //If the lock file is ok, increment reference count of
  243. //attached modules to shared memory
  244. atomic_inc32(&pserial_id->modules_attached_to_gmem_count);
  245. }
  246. }
  247. bool retry() const { return retry_with_new_map; }
  248. private:
  249. locking_file_serial_id * register_lock_file(int fd)
  250. {
  251. locking_file_serial_id *pinfo = mshm.construct<locking_file_serial_id>("lock_file_fd")();
  252. fill_file_serial_id(fd, *pinfo);
  253. return pinfo;
  254. }
  255. managed_global_memory &mshm;
  256. bool retry_with_new_map;
  257. };
  258. static void construct_map(void *addr)
  259. {
  260. std::string s;
  261. intermodule_singleton_helpers::get_map_name(s);
  262. const char *MapName = s.c_str();
  263. const std::size_t MapSize = intermodule_singleton_helpers::get_map_size();;
  264. ::new (addr)managed_global_memory(open_or_create, MapName, MapSize);
  265. }
  266. struct unlink_map_logic
  267. {
  268. unlink_map_logic(managed_global_memory &mshm)
  269. : mshm_(mshm)
  270. { mshm.atomic_func(*this); }
  271. void operator()()
  272. {
  273. locking_file_serial_id *pserial_id =
  274. mshm_.find<locking_file_serial_id>
  275. ("lock_file_fd").first;
  276. BOOST_ASSERT(0 != pserial_id);
  277. if(1 == atomic_dec32(&pserial_id->modules_attached_to_gmem_count)){
  278. int fd = pserial_id->fd;
  279. if(fd > 0){
  280. pserial_id->fd = GMemMarkToBeRemoved;
  281. std::string s;
  282. create_and_get_singleton_lock_file_path(s);
  283. delete_file(s.c_str());
  284. close_lock_file(fd);
  285. intermodule_singleton_helpers::get_map_name(s);
  286. shared_memory_object::remove(s.c_str());
  287. }
  288. }
  289. }
  290. private:
  291. managed_global_memory &mshm_;
  292. };
  293. static ref_count_ptr *find(managed_global_memory &map, const char *name)
  294. {
  295. return map.find<ref_count_ptr>(name).first;
  296. }
  297. static ref_count_ptr *insert(managed_global_memory &map, const char *name, const ref_count_ptr &ref)
  298. {
  299. return map.construct<ref_count_ptr>(name)(ref);
  300. }
  301. static bool erase(managed_global_memory &map, const char *name)
  302. {
  303. return map.destroy<ref_count_ptr>(name);
  304. }
  305. template<class F>
  306. static void atomic_func(managed_global_memory &map, F &f)
  307. {
  308. map.atomic_func(f);
  309. }
  310. };
  311. } //namespace intermodule_singleton_helpers {
  312. template<typename C, bool LazyInit = true, bool Phoenix = false>
  313. class portable_intermodule_singleton
  314. : public intermodule_singleton_impl<C, LazyInit, Phoenix, managed_global_memory>
  315. {};
  316. } //namespace ipcdetail{
  317. } //namespace interprocess{
  318. } //namespace boost{
  319. #include <boost/interprocess/detail/config_end.hpp>
  320. #endif //#ifndef BOOST_INTERPROCESS_PORTABLE_INTERMODULE_SINGLETON_HPP