]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
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 | ||
118 | static const char *get_lock_file_subdir_name() | |
119 | { return "gmem"; } | |
120 | ||
121 | static const char *get_lock_file_base_name() | |
122 | { return "lck"; } | |
123 | ||
124 | 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 | { | |
132 | 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 | ||
136 | 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. | |
152 | 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 | ||
192 | 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 | { | |
203 | lock_file_logic(managed_global_memory &shm) | |
204 | : mshm(shm) | |
205 | { shm.atomic_func(*this); } | |
206 | ||
207 | 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 | ||
273 | bool retry() const { return retry_with_new_map; } | |
274 | ||
275 | private: | |
276 | 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 | ||
287 | 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 | { | |
298 | unlink_map_logic(managed_global_memory &mshm) | |
299 | : mshm_(mshm) | |
300 | { mshm.atomic_func(*this); } | |
301 | ||
302 | 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 | ||
326 | static ref_count_ptr *find(managed_global_memory &map, const char *name) | |
327 | { | |
328 | return map.find<ref_count_ptr>(name).first; | |
329 | } | |
330 | ||
331 | 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 | ||
336 | 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> | |
342 | 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 |