portable_intermodule_singleton.hpp 11 KB

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