aboutsummaryrefslogblamecommitdiffstats
path: root/util/system/rwlock.cpp
blob: d4664d72d6f4ab09b19a71487ddd5bc75153e7c9 (plain) (tree)
1
2
3
4
5
6
7
8
9
                   
 
                                    

                        
      
                                       

                        
                              
                       
            
             
 

                                   
 

                                    
 
                            
 




                        

                        


                        
 
                           
                                                                          
 
                                              



                                               
                 
     

                       
 
                                                 



                                            
                 



                
                                              










                                 
                                               









                                   
                                                  









                          
                                               










                            
                                          






                                     
         




                                
                
                               
         



                         
            
             
 

                                   
 

                                    
 
                            
 
                           

                          
                                                      
                      
                                                                                            

     
                           
                                                      
                                                                                           
 
                                              
                                                     
                                                                                          
 
                                                 
                                                        
                                                                                                                

                       
                                              
                                                     
                                                                                                 
 
 
                                               
                                                     
                                                                                          
 
                                                  
                                                        
                                                                                                                

                       
                                               
                                                     
                                                                                                  
 
                                          
                                                     
                                                                                          
 
      




                        
                                
 
                                       
                         
 
                                          
                                   
 
                                       

                         
                                        
                          
 
                                           
                                    
 
                                        

                          
                                   
                     
#include "rwlock.h"

#include <util/generic/yexception.h>

#if defined(_unix_)
    #include <errno.h>
    #include <pthread.h>
#endif

#if defined(_win_) || defined(_darwin_)
    #include "mutex.h"
    #include "condvar.h"

//darwin rwlocks not recursive
class TRWMutex::TImpl {
public:
    TImpl();
    ~TImpl();

    void AcquireRead() noexcept;
    bool TryAcquireRead() noexcept;
    void ReleaseRead() noexcept;

    void AcquireWrite() noexcept;
    bool TryAcquireWrite() noexcept;
    void ReleaseWrite() noexcept;

    void Release() noexcept;

private:
    TMutex Lock_;
    int State_;
    TCondVar ReadCond_;
    TCondVar WriteCond_;
    int BlockedWriters_;
};

TRWMutex::TImpl::TImpl()
    : State_(0)
    , BlockedWriters_(0)
{
}

TRWMutex::TImpl::~TImpl() {
    Y_ABORT_UNLESS(State_ == 0, "failure, State_ != 0");
    Y_ABORT_UNLESS(BlockedWriters_ == 0, "failure, BlockedWriters_ != 0");
}

void TRWMutex::TImpl::AcquireRead() noexcept {
    with_lock (Lock_) {
        while (BlockedWriters_ || State_ < 0) {
            ReadCond_.Wait(Lock_);
        }

        ++State_;
    }

    ReadCond_.Signal();
}

bool TRWMutex::TImpl::TryAcquireRead() noexcept {
    with_lock (Lock_) {
        if (BlockedWriters_ || State_ < 0) {
            return false;
        }

        ++State_;
    }

    return true;
}

void TRWMutex::TImpl::ReleaseRead() noexcept {
    Lock_.Acquire();

    if (--State_ > 0) {
        Lock_.Release();
    } else if (BlockedWriters_) {
        Lock_.Release();
        WriteCond_.Signal();
    } else {
        Lock_.Release();
    }
}

void TRWMutex::TImpl::AcquireWrite() noexcept {
    with_lock (Lock_) {
        while (State_ != 0) {
            ++BlockedWriters_;
            WriteCond_.Wait(Lock_);
            --BlockedWriters_;
        }

        State_ = -1;
    }
}

bool TRWMutex::TImpl::TryAcquireWrite() noexcept {
    with_lock (Lock_) {
        if (State_ != 0) {
            return false;
        }

        State_ = -1;
    }

    return true;
}

void TRWMutex::TImpl::ReleaseWrite() noexcept {
    Lock_.Acquire();
    State_ = 0;

    if (BlockedWriters_) {
        Lock_.Release();
        WriteCond_.Signal();
    } else {
        Lock_.Release();
        ReadCond_.Signal();
    }
}

void TRWMutex::TImpl::Release() noexcept {
    Lock_.Acquire();

    if (State_ > 0) {
        if (--State_ > 0) {
            Lock_.Release();
        } else if (BlockedWriters_) {
            Lock_.Release();
            WriteCond_.Signal();
        }
    } else {
        State_ = 0;

        if (BlockedWriters_) {
            Lock_.Release();
            WriteCond_.Signal();
        } else {
            Lock_.Release();
            ReadCond_.Signal();
        }
    }
}

#else /* POSIX threads */

class TRWMutex::TImpl {
public:
    TImpl();
    ~TImpl();

    void AcquireRead() noexcept;
    bool TryAcquireRead() noexcept;
    void ReleaseRead() noexcept;

    void AcquireWrite() noexcept;
    bool TryAcquireWrite() noexcept;
    void ReleaseWrite() noexcept;

    void Release() noexcept;

private:
    pthread_rwlock_t Lock_;
};

TRWMutex::TImpl::TImpl() {
    int result = pthread_rwlock_init(&Lock_, nullptr);
    if (result != 0) {
        ythrow yexception() << "rwlock init failed (" << LastSystemErrorText(result) << ")";
    }
}

TRWMutex::TImpl::~TImpl() {
    const int result = pthread_rwlock_destroy(&Lock_);
    Y_ABORT_UNLESS(result == 0, "rwlock destroy failed (%s)", LastSystemErrorText(result));
}

void TRWMutex::TImpl::AcquireRead() noexcept {
    const int result = pthread_rwlock_rdlock(&Lock_);
    Y_ABORT_UNLESS(result == 0, "rwlock rdlock failed (%s)", LastSystemErrorText(result));
}

bool TRWMutex::TImpl::TryAcquireRead() noexcept {
    const int result = pthread_rwlock_tryrdlock(&Lock_);
    Y_ABORT_UNLESS(result == 0 || result == EBUSY, "rwlock tryrdlock failed (%s)", LastSystemErrorText(result));
    return result == 0;
}

void TRWMutex::TImpl::ReleaseRead() noexcept {
    const int result = pthread_rwlock_unlock(&Lock_);
    Y_ABORT_UNLESS(result == 0, "rwlock (read) unlock failed (%s)", LastSystemErrorText(result));
}

void TRWMutex::TImpl::AcquireWrite() noexcept {
    const int result = pthread_rwlock_wrlock(&Lock_);
    Y_ABORT_UNLESS(result == 0, "rwlock wrlock failed (%s)", LastSystemErrorText(result));
}

bool TRWMutex::TImpl::TryAcquireWrite() noexcept {
    const int result = pthread_rwlock_trywrlock(&Lock_);
    Y_ABORT_UNLESS(result == 0 || result == EBUSY, "rwlock trywrlock failed (%s)", LastSystemErrorText(result));
    return result == 0;
}

void TRWMutex::TImpl::ReleaseWrite() noexcept {
    const int result = pthread_rwlock_unlock(&Lock_);
    Y_ABORT_UNLESS(result == 0, "rwlock (write) unlock failed (%s)", LastSystemErrorText(result));
}

void TRWMutex::TImpl::Release() noexcept {
    const int result = pthread_rwlock_unlock(&Lock_);
    Y_ABORT_UNLESS(result == 0, "rwlock unlock failed (%s)", LastSystemErrorText(result));
}

#endif

TRWMutex::TRWMutex()
    : Impl_(new TImpl())
{
}

TRWMutex::~TRWMutex() = default;

void TRWMutex::AcquireRead() noexcept {
    Impl_->AcquireRead();
}

bool TRWMutex::TryAcquireRead() noexcept {
    return Impl_->TryAcquireRead();
}

void TRWMutex::ReleaseRead() noexcept {
    Impl_->ReleaseRead();
}

void TRWMutex::AcquireWrite() noexcept {
    Impl_->AcquireWrite();
}

bool TRWMutex::TryAcquireWrite() noexcept {
    return Impl_->TryAcquireWrite();
}

void TRWMutex::ReleaseWrite() noexcept {
    Impl_->ReleaseWrite();
}

void TRWMutex::Release() noexcept {
    Impl_->Release();
}