Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-17 08:51:58

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