Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:38:27

0001 //////////////////////////////////////////////////////////////////////////////
0002 //
0003 // (C) Copyright Ion Gaztanaga 2010-2012. Distributed under the Boost
0004 // Software License, Version 1.0. (See accompanying file
0005 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 // See http://www.boost.org/libs/interprocess for documentation.
0008 //
0009 //////////////////////////////////////////////////////////////////////////////
0010 
0011 #ifndef BOOST_INTERPROCESS_ROBUST_EMULATION_HPP
0012 #define BOOST_INTERPROCESS_ROBUST_EMULATION_HPP
0013 
0014 #ifndef BOOST_CONFIG_HPP
0015 #  include <boost/config.hpp>
0016 #endif
0017 #
0018 #if defined(BOOST_HAS_PRAGMA_ONCE)
0019 #pragma once
0020 #endif
0021 
0022 #include <boost/interprocess/detail/config_begin.hpp>
0023 #include <boost/interprocess/detail/workaround.hpp>
0024 #include <boost/interprocess/sync/interprocess_mutex.hpp>
0025 #include <boost/interprocess/sync/interprocess_recursive_mutex.hpp>
0026 #include <boost/interprocess/detail/atomic.hpp>
0027 #include <boost/interprocess/detail/os_file_functions.hpp>
0028 #include <boost/interprocess/detail/shared_dir_helpers.hpp>
0029 #include <boost/interprocess/detail/intermodule_singleton.hpp>
0030 #include <boost/interprocess/detail/portable_intermodule_singleton.hpp>
0031 #include <boost/interprocess/exceptions.hpp>
0032 #include <boost/interprocess/sync/spin/wait.hpp>
0033 #include <boost/interprocess/sync/detail/common_algorithms.hpp>
0034 #include <string>
0035 
0036 namespace boost{
0037 namespace interprocess{
0038 namespace ipcdetail{
0039 
0040 namespace robust_emulation_helpers {
0041 
0042 template<class T>
0043 class mutex_traits
0044 {
0045    public:
0046    static void take_ownership(T &t)
0047    {  t.take_ownership(); }
0048 };
0049 
0050 inline void remove_if_can_lock_file(const char *file_path)
0051 {
0052    file_handle_t fhnd = open_existing_file(file_path, read_write);
0053 
0054    if(fhnd != invalid_file()){
0055       bool acquired;
0056       if(try_acquire_file_lock(fhnd, acquired) && acquired){
0057          delete_file(file_path);
0058       }
0059       close_file(fhnd);
0060    }
0061 }
0062 
0063 inline const char *robust_lock_subdir_path()
0064 {  return "robust"; }
0065 
0066 inline const char *robust_lock_prefix()
0067 {  return "lck"; }
0068 
0069 inline void robust_lock_path(std::string &s)
0070 {
0071    get_shared_dir(s);
0072    s += "/";
0073    s += robust_lock_subdir_path();
0074 }
0075 
0076 inline void create_and_get_robust_lock_file_path(std::string &s, OS_process_id_t pid)
0077 {
0078    intermodule_singleton_helpers::create_tmp_subdir_and_get_pid_based_filepath
0079       (robust_lock_subdir_path(), robust_lock_prefix(), pid, s);
0080 }
0081 
0082 //This class will be a intermodule_singleton. The constructor will create
0083 //a lock file, the destructor will erase it.
0084 //
0085 //We should take in care that another process might be erasing unlocked
0086 //files while creating this one, so there are some race conditions we must
0087 //take in care to guarantee some robustness.
0088 class robust_mutex_lock_file
0089 {
0090    file_handle_t fd;
0091    std::string fname;
0092    public:
0093    robust_mutex_lock_file()
0094    {
0095       permissions p;
0096       p.set_unrestricted();
0097       //Remove old lock files of other processes
0098       remove_old_robust_lock_files();
0099       //Create path and obtain lock file path for this process
0100       create_and_get_robust_lock_file_path(fname, get_current_process_id());
0101 
0102       //Now try to open or create the lock file
0103       fd = create_or_open_file(fname.c_str(), read_write, p);
0104       //If we can't open or create it, then something unrecoverable has happened
0105       if(fd == invalid_file()){
0106          throw interprocess_exception(other_error, "Robust emulation robust_mutex_lock_file constructor failed: could not open or create file");
0107       }
0108 
0109       //Now we must take in care a race condition with another process
0110       //calling "remove_old_robust_lock_files()". No other threads from this
0111       //process will be creating the lock file because intermodule_singleton
0112       //guarantees this. So let's loop acquiring the lock and checking if we
0113       //can't exclusively create the file (if the file is erased by another process
0114       //then this exclusive open would fail). If the file can't be exclusively created
0115       //then we have correctly open/create and lock the file. If the file can
0116       //be exclusively created, then close previous locked file and try again.
0117       while(1){
0118          bool acquired;
0119          if(!try_acquire_file_lock(fd, acquired) || !acquired ){
0120             throw interprocess_exception(other_error, "Robust emulation robust_mutex_lock_file constructor failed: try_acquire_file_lock");
0121          }
0122          //Creating exclusively must fail with already_exists_error
0123          //to make sure we've locked the file and no one has
0124          //deleted it between creation and locking
0125          file_handle_t fd2 = create_new_file(fname.c_str(), read_write, p);
0126          if(fd2 != invalid_file()){
0127             close_file(fd);
0128             fd = fd2;
0129             continue;
0130          }
0131          //If exclusive creation fails with expected error go ahead
0132          else if(error_info(system_error_code()).get_error_code() == already_exists_error){ //must already exist
0133             //Leak descriptor to mantain the file locked until the process dies
0134             break;
0135          }
0136          //If exclusive creation fails with unexpected error throw an unrecoverable error
0137          else{
0138             close_file(fd);
0139             throw interprocess_exception(other_error, "Robust emulation robust_mutex_lock_file constructor failed: create_file filed with unexpected error");
0140          }
0141       }
0142    }
0143 
0144    ~robust_mutex_lock_file()
0145    {
0146       //The destructor is guaranteed by intermodule_singleton to be
0147       //executed serialized between all threads from current process,
0148       //so we just need to close and unlink the file.
0149       close_file(fd);
0150       //If some other process deletes the file before us after
0151       //closing it there should not be any problem.
0152       delete_file(fname.c_str());
0153    }
0154 
0155    private:
0156    //This functor is execute for all files in the lock file directory
0157    class other_process_lock_remover
0158    {
0159       public:
0160       void operator()(const char *filepath, const char *filename)
0161       {
0162          std::string pid_str;
0163          //If the lock file is not our own lock file, then try to do the cleanup
0164          if(!intermodule_singleton_helpers::check_if_filename_complies_with_pid
0165             (filename, robust_lock_prefix(), get_current_process_id(), pid_str)){
0166             remove_if_can_lock_file(filepath);
0167          }
0168       }
0169    };
0170 
0171    bool remove_old_robust_lock_files()
0172    {
0173       std::string refcstrRootDirectory;
0174       robust_lock_path(refcstrRootDirectory);
0175       return for_each_file_in_dir(refcstrRootDirectory.c_str(), other_process_lock_remover());
0176    }
0177 };
0178 
0179 }  //namespace robust_emulation_helpers {
0180 
0181 //This is the mutex class. Mutex should follow mutex concept
0182 //with an additonal "take_ownership()" function to take ownership of the
0183 //mutex when robust_spin_mutex determines the previous owner was dead.
0184 template<class Mutex>
0185 class robust_spin_mutex
0186 {
0187    public:
0188    static const boost::uint32_t correct_state = 0;
0189    static const boost::uint32_t fixing_state  = 1;
0190    static const boost::uint32_t broken_state  = 2;
0191 
0192    typedef robust_emulation_helpers::mutex_traits<Mutex> mutex_traits_t;
0193 
0194    robust_spin_mutex();
0195    void lock();
0196    bool try_lock();
0197    template<class TimePoint>
0198    bool timed_lock(const TimePoint &abs_time);
0199    void unlock();
0200    void consistent();
0201    bool previous_owner_dead();
0202 
0203    private:
0204    static const unsigned int spin_threshold = 100u;
0205    bool lock_own_unique_file();
0206    bool robust_check();
0207    bool check_if_owner_dead_and_take_ownership_atomically();
0208    bool is_owner_dead(boost::uint32_t own);
0209    void owner_to_filename(boost::uint32_t own, std::string &s);
0210    //The real mutex
0211    Mutex mtx;
0212    //The pid of the owner
0213    volatile boost::uint32_t owner;
0214    //The state of the mutex (correct, fixing, broken)
0215    volatile boost::uint32_t state;
0216 };
0217 
0218 template<class Mutex>
0219 inline robust_spin_mutex<Mutex>::robust_spin_mutex()
0220    : mtx(), owner((boost::uint32_t)get_invalid_process_id()), state(correct_state)
0221 {}
0222 
0223 template<class Mutex>
0224 inline void robust_spin_mutex<Mutex>::lock()
0225 {  try_based_lock(*this);  }
0226 
0227 template<class Mutex>
0228 inline bool robust_spin_mutex<Mutex>::try_lock()
0229 {
0230    //Same as lock() but without spinning
0231    if(atomic_read32(&this->state) == broken_state){
0232       throw interprocess_exception(lock_error, "Broken id");
0233    }
0234 
0235    if(!this->lock_own_unique_file()){
0236       throw interprocess_exception(lock_error, "Broken id");
0237    }
0238 
0239    if (mtx.try_lock()){
0240       atomic_write32(&this->owner, static_cast<boost::uint32_t>(get_current_process_id()));
0241       return true;
0242    }
0243    else{
0244       if(!this->robust_check()){
0245          return false;
0246       }
0247       else{
0248          return true;
0249       }
0250    }
0251 }
0252 
0253 template<class Mutex>
0254 template<class TimePoint>
0255 inline bool robust_spin_mutex<Mutex>::timed_lock
0256    (const TimePoint &abs_time)
0257 {  return try_based_timed_lock(*this, abs_time);   }
0258 
0259 template<class Mutex>
0260 inline void robust_spin_mutex<Mutex>::owner_to_filename(boost::uint32_t own, std::string &s)
0261 {
0262    robust_emulation_helpers::create_and_get_robust_lock_file_path(s, (OS_process_id_t)own);
0263 }
0264 
0265 template<class Mutex>
0266 inline bool robust_spin_mutex<Mutex>::robust_check()
0267 {
0268    //If the old owner was dead, and we've acquired ownership, mark
0269    //the mutex as 'fixing'. This means that a "consistent()" is needed
0270    //to avoid marking the mutex as "broken" when the mutex is unlocked.
0271    if(!this->check_if_owner_dead_and_take_ownership_atomically()){
0272       return false;
0273    }
0274    atomic_write32(&this->state, fixing_state);
0275    return true;
0276 }
0277 
0278 template<class Mutex>
0279 inline bool robust_spin_mutex<Mutex>::check_if_owner_dead_and_take_ownership_atomically()
0280 {
0281    boost::uint32_t cur_owner = static_cast<boost::uint32_t>(get_current_process_id());
0282    boost::uint32_t old_owner = atomic_read32(&this->owner), old_owner2;
0283    //The cas loop guarantees that only one thread from this or another process
0284    //will succeed taking ownership
0285    do{
0286       //Check if owner is dead
0287       if(!this->is_owner_dead(old_owner)){
0288          return false;
0289       }
0290       //If it's dead, try to mark this process as the owner in the owner field
0291       old_owner2 = old_owner;
0292       old_owner = atomic_cas32(&this->owner, cur_owner, old_owner);
0293    }while(old_owner2 != old_owner);
0294    //If success, we fix mutex internals to assure our ownership
0295    mutex_traits_t::take_ownership(mtx);
0296    return true;
0297 }
0298 
0299 template<class Mutex>
0300 inline bool robust_spin_mutex<Mutex>::is_owner_dead(boost::uint32_t own)
0301 {
0302    //If owner is an invalid id, then it's clear it's dead
0303    if(own == static_cast<boost::uint32_t>(get_invalid_process_id())){
0304       return true;
0305    }
0306 
0307    //Obtain the lock filename of the owner field
0308    std::string file;
0309    this->owner_to_filename(own, file);
0310 
0311    //Now the logic is to open and lock it
0312    file_handle_t fhnd = open_existing_file(file.c_str(), read_write);
0313 
0314    if(fhnd != invalid_file()){
0315       //If we can open the file, lock it.
0316       bool acquired;
0317       if(try_acquire_file_lock(fhnd, acquired) && acquired){
0318          //If locked, just delete the file
0319          delete_file(file.c_str());
0320          close_file(fhnd);
0321          return true;
0322       }
0323       //If not locked, the owner is suppossed to be still alive
0324       close_file(fhnd);
0325    }
0326    else{
0327       //If the lock file does not exist then the owner is dead (a previous cleanup)
0328       //function has deleted the file. If there is another reason, then this is
0329       //an unrecoverable error
0330       if(error_info(system_error_code()).get_error_code() == not_found_error){
0331          return true;
0332       }
0333    }
0334    return false;
0335 }
0336 
0337 template<class Mutex>
0338 inline void robust_spin_mutex<Mutex>::consistent()
0339 {
0340    //This function supposes the previous state was "fixing"
0341    //and the current process holds the mutex
0342    if(atomic_read32(&this->state) != fixing_state &&
0343       atomic_read32(&this->owner) != (boost::uint32_t)get_current_process_id()){
0344       throw interprocess_exception(lock_error, "Broken id");
0345    }
0346    //If that's the case, just update mutex state
0347    atomic_write32(&this->state, correct_state);
0348 }
0349 
0350 template<class Mutex>
0351 inline bool robust_spin_mutex<Mutex>::previous_owner_dead()
0352 {
0353    //Notifies if a owner recovery has been performed in the last lock()
0354    return atomic_read32(&this->state) == fixing_state;
0355 }
0356 
0357 template<class Mutex>
0358 inline void robust_spin_mutex<Mutex>::unlock()
0359 {
0360    //If in "fixing" state, unlock and mark the mutex as unrecoverable
0361    //so next locks will fail and all threads will be notified that the
0362    //data protected by the mutex was not recoverable.
0363    if(atomic_read32(&this->state) == fixing_state){
0364       atomic_write32(&this->state, broken_state);
0365    }
0366    //Write an invalid owner to minimize pid reuse possibility
0367    atomic_write32(&this->owner, static_cast<boost::uint32_t>(get_invalid_process_id()));
0368    mtx.unlock();
0369 }
0370 
0371 template<class Mutex>
0372 inline bool robust_spin_mutex<Mutex>::lock_own_unique_file()
0373 {
0374    //This function forces instantiation of the singleton
0375    robust_emulation_helpers::robust_mutex_lock_file* dummy =
0376       &ipcdetail::intermodule_singleton
0377          <robust_emulation_helpers::robust_mutex_lock_file>::get();
0378    return dummy != 0;
0379 }
0380 
0381 }  //namespace ipcdetail{
0382 }  //namespace interprocess{
0383 }  //namespace boost{
0384 
0385 #include <boost/interprocess/detail/config_end.hpp>
0386 
0387 #endif