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