Mercurial > hg > vamp-build-and-test
comparison DEPENDENCIES/generic/include/boost/interprocess/detail/robust_emulation.hpp @ 16:2665513ce2d3
Add boost headers
author | Chris Cannam |
---|---|
date | Tue, 05 Aug 2014 11:11:38 +0100 |
parents | |
children | c530137014c0 |
comparison
equal
deleted
inserted
replaced
15:663ca0da4350 | 16:2665513ce2d3 |
---|---|
1 ////////////////////////////////////////////////////////////////////////////// | |
2 // | |
3 // (C) Copyright Ion Gaztanaga 2010-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_ROBUST_EMULATION_HPP | |
12 #define BOOST_INTERPROCESS_ROBUST_EMULATION_HPP | |
13 | |
14 #if defined(_MSC_VER)&&(_MSC_VER>=1200) | |
15 #pragma once | |
16 #endif | |
17 | |
18 #include <boost/interprocess/detail/config_begin.hpp> | |
19 #include <boost/interprocess/detail/workaround.hpp> | |
20 #include <boost/interprocess/sync/interprocess_mutex.hpp> | |
21 #include <boost/interprocess/sync/interprocess_recursive_mutex.hpp> | |
22 #include <boost/interprocess/detail/atomic.hpp> | |
23 #include <boost/interprocess/detail/os_file_functions.hpp> | |
24 #include <boost/interprocess/detail/tmp_dir_helpers.hpp> | |
25 #include <boost/interprocess/detail/intermodule_singleton.hpp> | |
26 #include <boost/interprocess/exceptions.hpp> | |
27 #include <boost/interprocess/sync/spin/wait.hpp> | |
28 #include <string> | |
29 | |
30 namespace boost{ | |
31 namespace interprocess{ | |
32 namespace ipcdetail{ | |
33 | |
34 namespace robust_emulation_helpers { | |
35 | |
36 template<class T> | |
37 class mutex_traits | |
38 { | |
39 public: | |
40 static void take_ownership(T &t) | |
41 { t.take_ownership(); } | |
42 }; | |
43 | |
44 inline void remove_if_can_lock_file(const char *file_path) | |
45 { | |
46 file_handle_t fhnd = open_existing_file(file_path, read_write); | |
47 | |
48 if(fhnd != invalid_file()){ | |
49 bool acquired; | |
50 if(try_acquire_file_lock(fhnd, acquired) && acquired){ | |
51 delete_file(file_path); | |
52 } | |
53 close_file(fhnd); | |
54 } | |
55 } | |
56 | |
57 inline const char *robust_lock_subdir_path() | |
58 { return "robust"; } | |
59 | |
60 inline const char *robust_lock_prefix() | |
61 { return "lck"; } | |
62 | |
63 inline void robust_lock_path(std::string &s) | |
64 { | |
65 tmp_folder(s); | |
66 s += "/"; | |
67 s += robust_lock_subdir_path(); | |
68 } | |
69 | |
70 inline void create_and_get_robust_lock_file_path(std::string &s, OS_process_id_t pid) | |
71 { | |
72 intermodule_singleton_helpers::create_tmp_subdir_and_get_pid_based_filepath | |
73 (robust_lock_subdir_path(), robust_lock_prefix(), pid, s); | |
74 } | |
75 | |
76 //This class will be a intermodule_singleton. The constructor will create | |
77 //a lock file, the destructor will erase it. | |
78 // | |
79 //We should take in care that another process might be erasing unlocked | |
80 //files while creating this one, so there are some race conditions we must | |
81 //take in care to guarantee some robustness. | |
82 class robust_mutex_lock_file | |
83 { | |
84 file_handle_t fd; | |
85 std::string fname; | |
86 public: | |
87 robust_mutex_lock_file() | |
88 { | |
89 permissions p; | |
90 p.set_unrestricted(); | |
91 //Remove old lock files of other processes | |
92 remove_old_robust_lock_files(); | |
93 //Create path and obtain lock file path for this process | |
94 create_and_get_robust_lock_file_path(fname, get_current_process_id()); | |
95 | |
96 //Now try to open or create the lock file | |
97 fd = create_or_open_file(fname.c_str(), read_write, p); | |
98 //If we can't open or create it, then something unrecoverable has happened | |
99 if(fd == invalid_file()){ | |
100 throw interprocess_exception(other_error, "Robust emulation robust_mutex_lock_file constructor failed: could not open or create file"); | |
101 } | |
102 | |
103 //Now we must take in care a race condition with another process | |
104 //calling "remove_old_robust_lock_files()". No other threads from this | |
105 //process will be creating the lock file because intermodule_singleton | |
106 //guarantees this. So let's loop acquiring the lock and checking if we | |
107 //can't exclusively create the file (if the file is erased by another process | |
108 //then this exclusive open would fail). If the file can't be exclusively created | |
109 //then we have correctly open/create and lock the file. If the file can | |
110 //be exclusively created, then close previous locked file and try again. | |
111 while(1){ | |
112 bool acquired; | |
113 if(!try_acquire_file_lock(fd, acquired) || !acquired ){ | |
114 throw interprocess_exception(other_error, "Robust emulation robust_mutex_lock_file constructor failed: try_acquire_file_lock"); | |
115 } | |
116 //Creating exclusively must fail with already_exists_error | |
117 //to make sure we've locked the file and no one has | |
118 //deleted it between creation and locking | |
119 file_handle_t fd2 = create_new_file(fname.c_str(), read_write, p); | |
120 if(fd2 != invalid_file()){ | |
121 close_file(fd); | |
122 fd = fd2; | |
123 continue; | |
124 } | |
125 //If exclusive creation fails with expected error go ahead | |
126 else if(error_info(system_error_code()).get_error_code() == already_exists_error){ //must already exist | |
127 //Leak descriptor to mantain the file locked until the process dies | |
128 break; | |
129 } | |
130 //If exclusive creation fails with unexpected error throw an unrecoverable error | |
131 else{ | |
132 close_file(fd); | |
133 throw interprocess_exception(other_error, "Robust emulation robust_mutex_lock_file constructor failed: create_file filed with unexpected error"); | |
134 } | |
135 } | |
136 } | |
137 | |
138 ~robust_mutex_lock_file() | |
139 { | |
140 //The destructor is guaranteed by intermodule_singleton to be | |
141 //executed serialized between all threads from current process, | |
142 //so we just need to close and unlink the file. | |
143 close_file(fd); | |
144 //If some other process deletes the file before us after | |
145 //closing it there should not be any problem. | |
146 delete_file(fname.c_str()); | |
147 } | |
148 | |
149 private: | |
150 //This functor is execute for all files in the lock file directory | |
151 class other_process_lock_remover | |
152 { | |
153 public: | |
154 void operator()(const char *filepath, const char *filename) | |
155 { | |
156 std::string pid_str; | |
157 //If the lock file is not our own lock file, then try to do the cleanup | |
158 if(!intermodule_singleton_helpers::check_if_filename_complies_with_pid | |
159 (filename, robust_lock_prefix(), get_current_process_id(), pid_str)){ | |
160 remove_if_can_lock_file(filepath); | |
161 } | |
162 } | |
163 }; | |
164 | |
165 bool remove_old_robust_lock_files() | |
166 { | |
167 std::string refcstrRootDirectory; | |
168 robust_lock_path(refcstrRootDirectory); | |
169 return for_each_file_in_dir(refcstrRootDirectory.c_str(), other_process_lock_remover()); | |
170 } | |
171 }; | |
172 | |
173 } //namespace robust_emulation_helpers { | |
174 | |
175 //This is the mutex class. Mutex should follow mutex concept | |
176 //with an additonal "take_ownership()" function to take ownership of the | |
177 //mutex when robust_spin_mutex determines the previous owner was dead. | |
178 template<class Mutex> | |
179 class robust_spin_mutex | |
180 { | |
181 public: | |
182 static const boost::uint32_t correct_state = 0; | |
183 static const boost::uint32_t fixing_state = 1; | |
184 static const boost::uint32_t broken_state = 2; | |
185 | |
186 typedef robust_emulation_helpers::mutex_traits<Mutex> mutex_traits_t; | |
187 | |
188 robust_spin_mutex(); | |
189 void lock(); | |
190 bool try_lock(); | |
191 bool timed_lock(const boost::posix_time::ptime &abs_time); | |
192 void unlock(); | |
193 void consistent(); | |
194 bool previous_owner_dead(); | |
195 | |
196 private: | |
197 static const unsigned int spin_threshold = 100u; | |
198 bool lock_own_unique_file(); | |
199 bool robust_check(); | |
200 bool check_if_owner_dead_and_take_ownership_atomically(); | |
201 bool is_owner_dead(boost::uint32_t own); | |
202 void owner_to_filename(boost::uint32_t own, std::string &s); | |
203 //The real mutex | |
204 Mutex mtx; | |
205 //The pid of the owner | |
206 volatile boost::uint32_t owner; | |
207 //The state of the mutex (correct, fixing, broken) | |
208 volatile boost::uint32_t state; | |
209 }; | |
210 | |
211 template<class Mutex> | |
212 inline robust_spin_mutex<Mutex>::robust_spin_mutex() | |
213 : mtx(), owner(get_invalid_process_id()), state(correct_state) | |
214 {} | |
215 | |
216 template<class Mutex> | |
217 inline void robust_spin_mutex<Mutex>::lock() | |
218 { | |
219 //If the mutex is broken (recovery didn't call consistent()), | |
220 //then throw an exception | |
221 if(atomic_read32(&this->state) == broken_state){ | |
222 throw interprocess_exception(lock_error, "Broken id"); | |
223 } | |
224 | |
225 //This function provokes intermodule_singleton instantiation | |
226 if(!this->lock_own_unique_file()){ | |
227 throw interprocess_exception(lock_error, "Broken id"); | |
228 } | |
229 | |
230 //Now the logic. Try to lock, if successful mark the owner | |
231 //if it fails, start recovery logic | |
232 spin_wait swait; | |
233 while(1){ | |
234 if (mtx.try_lock()){ | |
235 atomic_write32(&this->owner, get_current_process_id()); | |
236 break; | |
237 } | |
238 else{ | |
239 //Do the dead owner checking each spin_threshold lock tries | |
240 swait.yield(); | |
241 if(0 == (swait.count() & 255u)){ | |
242 //Check if owner dead and take ownership if possible | |
243 if(this->robust_check()){ | |
244 break; | |
245 } | |
246 } | |
247 } | |
248 } | |
249 } | |
250 | |
251 template<class Mutex> | |
252 inline bool robust_spin_mutex<Mutex>::try_lock() | |
253 { | |
254 //Same as lock() but without spinning | |
255 if(atomic_read32(&this->state) == broken_state){ | |
256 throw interprocess_exception(lock_error, "Broken id"); | |
257 } | |
258 | |
259 if(!this->lock_own_unique_file()){ | |
260 throw interprocess_exception(lock_error, "Broken id"); | |
261 } | |
262 | |
263 if (mtx.try_lock()){ | |
264 atomic_write32(&this->owner, get_current_process_id()); | |
265 return true; | |
266 } | |
267 else{ | |
268 if(!this->robust_check()){ | |
269 return false; | |
270 } | |
271 else{ | |
272 return true; | |
273 } | |
274 } | |
275 } | |
276 | |
277 template<class Mutex> | |
278 inline bool robust_spin_mutex<Mutex>::timed_lock | |
279 (const boost::posix_time::ptime &abs_time) | |
280 { | |
281 //Same as lock() but with an additional timeout | |
282 if(abs_time == boost::posix_time::pos_infin){ | |
283 this->lock(); | |
284 return true; | |
285 } | |
286 //Obtain current count and target time | |
287 boost::posix_time::ptime now = microsec_clock::universal_time(); | |
288 | |
289 if(now >= abs_time) | |
290 return this->try_lock(); | |
291 | |
292 spin_wait swait; | |
293 do{ | |
294 if(this->try_lock()){ | |
295 break; | |
296 } | |
297 now = microsec_clock::universal_time(); | |
298 | |
299 if(now >= abs_time){ | |
300 return this->try_lock(); | |
301 } | |
302 // relinquish current time slice | |
303 swait.yield(); | |
304 }while (true); | |
305 | |
306 return true; | |
307 } | |
308 | |
309 template<class Mutex> | |
310 inline void robust_spin_mutex<Mutex>::owner_to_filename(boost::uint32_t own, std::string &s) | |
311 { | |
312 robust_emulation_helpers::create_and_get_robust_lock_file_path(s, own); | |
313 } | |
314 | |
315 template<class Mutex> | |
316 inline bool robust_spin_mutex<Mutex>::robust_check() | |
317 { | |
318 //If the old owner was dead, and we've acquired ownership, mark | |
319 //the mutex as 'fixing'. This means that a "consistent()" is needed | |
320 //to avoid marking the mutex as "broken" when the mutex is unlocked. | |
321 if(!this->check_if_owner_dead_and_take_ownership_atomically()){ | |
322 return false; | |
323 } | |
324 atomic_write32(&this->state, fixing_state); | |
325 return true; | |
326 } | |
327 | |
328 template<class Mutex> | |
329 inline bool robust_spin_mutex<Mutex>::check_if_owner_dead_and_take_ownership_atomically() | |
330 { | |
331 boost::uint32_t cur_owner = get_current_process_id(); | |
332 boost::uint32_t old_owner = atomic_read32(&this->owner), old_owner2; | |
333 //The cas loop guarantees that only one thread from this or another process | |
334 //will succeed taking ownership | |
335 do{ | |
336 //Check if owner is dead | |
337 if(!this->is_owner_dead(old_owner)){ | |
338 return false; | |
339 } | |
340 //If it's dead, try to mark this process as the owner in the owner field | |
341 old_owner2 = old_owner; | |
342 old_owner = atomic_cas32(&this->owner, cur_owner, old_owner); | |
343 }while(old_owner2 != old_owner); | |
344 //If success, we fix mutex internals to assure our ownership | |
345 mutex_traits_t::take_ownership(mtx); | |
346 return true; | |
347 } | |
348 | |
349 template<class Mutex> | |
350 inline bool robust_spin_mutex<Mutex>::is_owner_dead(boost::uint32_t own) | |
351 { | |
352 //If owner is an invalid id, then it's clear it's dead | |
353 if(own == (boost::uint32_t)get_invalid_process_id()){ | |
354 return true; | |
355 } | |
356 | |
357 //Obtain the lock filename of the owner field | |
358 std::string file; | |
359 this->owner_to_filename(own, file); | |
360 | |
361 //Now the logic is to open and lock it | |
362 file_handle_t fhnd = open_existing_file(file.c_str(), read_write); | |
363 | |
364 if(fhnd != invalid_file()){ | |
365 //If we can open the file, lock it. | |
366 bool acquired; | |
367 if(try_acquire_file_lock(fhnd, acquired) && acquired){ | |
368 //If locked, just delete the file | |
369 delete_file(file.c_str()); | |
370 close_file(fhnd); | |
371 return true; | |
372 } | |
373 //If not locked, the owner is suppossed to be still alive | |
374 close_file(fhnd); | |
375 } | |
376 else{ | |
377 //If the lock file does not exist then the owner is dead (a previous cleanup) | |
378 //function has deleted the file. If there is another reason, then this is | |
379 //an unrecoverable error | |
380 if(error_info(system_error_code()).get_error_code() == not_found_error){ | |
381 return true; | |
382 } | |
383 } | |
384 return false; | |
385 } | |
386 | |
387 template<class Mutex> | |
388 inline void robust_spin_mutex<Mutex>::consistent() | |
389 { | |
390 //This function supposes the previous state was "fixing" | |
391 //and the current process holds the mutex | |
392 if(atomic_read32(&this->state) != fixing_state && | |
393 atomic_read32(&this->owner) != (boost::uint32_t)get_current_process_id()){ | |
394 throw interprocess_exception(lock_error, "Broken id"); | |
395 } | |
396 //If that's the case, just update mutex state | |
397 atomic_write32(&this->state, correct_state); | |
398 } | |
399 | |
400 template<class Mutex> | |
401 inline bool robust_spin_mutex<Mutex>::previous_owner_dead() | |
402 { | |
403 //Notifies if a owner recovery has been performed in the last lock() | |
404 return atomic_read32(&this->state) == fixing_state; | |
405 }; | |
406 | |
407 template<class Mutex> | |
408 inline void robust_spin_mutex<Mutex>::unlock() | |
409 { | |
410 //If in "fixing" state, unlock and mark the mutex as unrecoverable | |
411 //so next locks will fail and all threads will be notified that the | |
412 //data protected by the mutex was not recoverable. | |
413 if(atomic_read32(&this->state) == fixing_state){ | |
414 atomic_write32(&this->state, broken_state); | |
415 } | |
416 //Write an invalid owner to minimize pid reuse possibility | |
417 atomic_write32(&this->owner, get_invalid_process_id()); | |
418 mtx.unlock(); | |
419 } | |
420 | |
421 template<class Mutex> | |
422 inline bool robust_spin_mutex<Mutex>::lock_own_unique_file() | |
423 { | |
424 //This function forces instantiation of the singleton | |
425 robust_emulation_helpers::robust_mutex_lock_file* dummy = | |
426 &ipcdetail::intermodule_singleton | |
427 <robust_emulation_helpers::robust_mutex_lock_file>::get(); | |
428 return dummy != 0; | |
429 } | |
430 | |
431 } //namespace ipcdetail{ | |
432 } //namespace interprocess{ | |
433 } //namespace boost{ | |
434 | |
435 #include <boost/interprocess/detail/config_end.hpp> | |
436 | |
437 #endif |