summaryrefslogtreecommitdiff
path: root/protocols/Telegram/tdlib/td/tdutils/td/utils/port/RwMutex.h
blob: 01de499bd04a3652994f30b08516a97b2ea80a70 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// 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/Status.h"

#if TD_PORT_POSIX
#include <pthread.h>
#endif

#include <memory>

namespace td {

class RwMutex {
 public:
  RwMutex() {
    init();
  }
  RwMutex(const RwMutex &) = delete;
  RwMutex &operator=(const RwMutex &) = delete;
  RwMutex(RwMutex &&other) noexcept {
    init();
    other.clear();
  }
  RwMutex &operator=(RwMutex &&other) noexcept {
    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