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