inline void robust_spin_mutex<Mutex>::consistent() { //This function supposes the previous state was "fixing" //and the current process holds the mutex if(atomic_read32(&this->state) != fixing_state && atomic_read32(&this->owner) != (boost::uint32_t)get_current_process_id()){ throw interprocess_exception(lock_error, "Broken id"); } //If that's the case, just update mutex state atomic_write32(&this->state, correct_state); }
inline void robust_spin_mutex<Mutex>::unlock() { //If in "fixing" state, unlock and mark the mutex as unrecoverable //so next locks will fail and all threads will be notified that the //data protected by the mutex was not recoverable. if(atomic_read32(&this->state) == fixing_state){ atomic_write32(&this->state, broken_state); } //Write an invalid owner to minimize pid reuse possibility atomic_write32(&this->owner, get_invalid_process_id()); mtx.unlock(); }
inline bool robust_spin_mutex<Mutex>::check_if_owner_dead_and_take_ownership_atomically() { boost::uint32_t cur_owner = get_current_process_id(); boost::uint32_t old_owner = atomic_read32(&this->owner), old_owner2; //The cas loop guarantees that only one thread from this or another process //will succeed taking ownership do{ //Check if owner is dead if(!this->is_owner_dead(old_owner)){ return false; } //If it's dead, try to mark this process as the owner in the owner field old_owner2 = old_owner; old_owner = atomic_cas32(&this->owner, cur_owner, old_owner); }while(old_owner2 != old_owner); //If success, we fix mutex internals to assure our ownership mutex_traits_t::take_ownership(mtx); return true; }
inline void robust_spin_mutex<Mutex>::lock() { //If the mutex is broken (recovery didn't call consistent()), //then throw an exception if(atomic_read32(&this->state) == broken_state){ throw interprocess_exception(lock_error, "Broken id"); } //This function provokes intermodule_singleton instantiation if(!this->lock_own_unique_file()){ throw interprocess_exception(lock_error, "Broken id"); } //Now the logic. Try to lock, if successful mark the owner //if it fails, start recovery logic unsigned int spin_count = 0; while(1){ if (mtx.try_lock()){ atomic_write32(&this->owner, get_current_process_id()); break; } else{ //Do the dead owner checking each spin_threshold lock tries ipcdetail::thread_yield(); ++spin_count; if(spin_count > spin_threshold){ //Check if owner dead and take ownership if possible if(!this->robust_check()){ spin_count = 0; } else{ break; } } } } }
inline bool robust_spin_mutex<Mutex>::try_lock() { //Same as lock() but without spinning if(atomic_read32(&this->state) == broken_state){ throw interprocess_exception(lock_error, "Broken id"); } if(!this->lock_own_unique_file()){ throw interprocess_exception(lock_error, "Broken id"); } if (mtx.try_lock()){ atomic_write32(&this->owner, get_current_process_id()); return true; } else{ if(!this->robust_check()){ return false; } else{ return true; } } }
inline bool robust_spin_mutex<Mutex>::previous_owner_dead() { //Notifies if a owner recovery has been performed in the last lock() return atomic_read32(&this->state) == fixing_state; }
DWORD atomicGetValue32(DWORD &value) { return atomic_read32((boost::uint32_t *)&value); }