text: //
  // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2017
  //
  // Distributed under the Boost Software License, Version 1.0. (See accompanying
  // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  //
  #pragma once
  
  #include "td/utils/port/config.h"
  
  #include "td/utils/common.h"
  #include "td/utils/logging.h"
  #include "td/utils/Status.h"
  
  #if TD_PORT_POSIX
  #include <pthread.h>
  #endif
  
  namespace td {
  class RwMutex {
   public:
    RwMutex() {
      init();
    }
    RwMutex(const RwMutex &) = delete;
    RwMutex &operator=(const RwMutex &) = delete;
    RwMutex(RwMutex &&other) {
      init();
      other.clear();
    }
    RwMutex &operator=(RwMutex &&other) {
      other.clear();
      return *this;
    }
    ~RwMutex() {
      clear();
    }
  
    bool empty() const {
      return !is_valid_;
    }
  
    void init();
  
    void clear();
  
    struct ReadUnlock {
      void operator()(RwMutex *ptr) {
        ptr->unlock_read_unsafe();
      }
    };
    struct WriteUnlock {
      void operator()(RwMutex *ptr) {
        ptr->unlock_write_unsafe();
      }
    };
  
    using ReadLock = std::unique_ptr<RwMutex, ReadUnlock>;
    using WriteLock = std::unique_ptr<RwMutex, WriteUnlock>;
  
    Result<ReadLock> lock_read() TD_WARN_UNUSED_RESULT {
      lock_read_unsafe();
      return ReadLock(this);
    }
  
    Result<WriteLock> lock_write() TD_WARN_UNUSED_RESULT {
      lock_write_unsafe();
      return WriteLock(this);
    }
  
    void lock_read_unsafe();
  
    void lock_write_unsafe();
  
    void unlock_read_unsafe();
  
    void unlock_write_unsafe();
  
   private:
    bool is_valid_ = false;
  #if TD_PORT_POSIX
    pthread_rwlock_t mutex_;
  #elif TD_PORT_WINDOWS
    unique_ptr<SRWLOCK> mutex_;
  #endif
  };
  
  inline void RwMutex::init() {
    CHECK(empty());
    is_valid_ = true;
  #if TD_PORT_POSIX
    pthread_rwlock_init(&mutex_, nullptr);
  #elif TD_PORT_WINDOWS
    mutex_ = make_unique<SRWLOCK>();
    InitializeSRWLock(mutex_.get());
  #endif
  }
  
  inline void RwMutex::clear() {
    if (is_valid_) {
  #if TD_PORT_POSIX
      pthread_rwlock_destroy(&mutex_);
  #elif TD_PORT_WINDOWS
      mutex_.release();
  #endif
      is_valid_ = false;
    }
  }
  
  inline void RwMutex::lock_read_unsafe() {
    CHECK(!empty());
  // TODO error handling
  #if TD_PORT_POSIX
    pthread_rwlock_rdlock(&mutex_);
  #elif TD_PORT_WINDOWS
    AcquireSRWLockShared(mutex_.get());
  #endif
  }
  
  inline void RwMutex::lock_write_unsafe() {
    CHECK(!empty());
  #if TD_PORT_POSIX
    pthread_rwlock_wrlock(&mutex_);
  #elif TD_PORT_WINDOWS
    AcquireSRWLockExclusive(mutex_.get());
  #endif
  }
  
  inline void RwMutex::unlock_read_unsafe() {
    CHECK(!empty());
  #if TD_PORT_POSIX
    pthread_rwlock_unlock(&mutex_);
  #elif TD_PORT_WINDOWS
    ReleaseSRWLockShared(mutex_.get());
  #endif
  }
  
  inline void RwMutex::unlock_write_unsafe() {
    CHECK(!empty());
  #if TD_PORT_POSIX
    pthread_rwlock_unlock(&mutex_);
  #elif TD_PORT_WINDOWS
    ReleaseSRWLockExclusive(mutex_.get());
  #endif
  }
  
  }  // namespace td
  