summaryrefslogtreecommitdiffstats
path: root/src/common/async/shared_mutex.h
blob: 3e471a4df6fa6f1e23518cd02ab7af1cb5567796 (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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2018 Red Hat
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation. See file COPYING.
 *
 */

#pragma once

#include "common/async/detail/shared_mutex.h"

namespace ceph::async {

/**
 * An asynchronous shared mutex for use with boost::asio.
 *
 * A shared mutex class with asynchronous lock operations that complete on a
 * boost::asio executor. The class also has synchronous interfaces that meet
 * most of the standard library's requirements for the SharedMutex concept,
 * which makes it compatible with lock_guard, unique_lock, and shared_lock.
 *
 * All lock requests can fail with operation_aborted on cancel() or destruction.
 * The non-error_code overloads of lock() and lock_shared() will throw this
 * error as an exception of type boost::system::system_error.
 *
 * Exclusive locks are prioritized over shared locks. Locks of the same type
 * are granted in fifo order. The implementation defines a limit on the number
 * of shared locks to 65534 at a time.
 *
 * Example use:
 *
 *   boost::asio::io_context context;
 *   SharedMutex mutex{context.get_executor()};
 *
 *   mutex.async_lock([&] (boost::system::error_code ec, auto lock) {
 *       if (!ec) {
 *         // mutate shared state ...
 *       }
 *     });
 *   mutex.async_lock_shared([&] (boost::system::error_code ec, auto lock) {
 *       if (!ec) {
 *         // read shared state ...
 *       }
 *     });
 *
 *   context.run();
 */
template <typename Executor>
class SharedMutex {
 public:
  explicit SharedMutex(const Executor& ex);

  /// on destruction, all pending lock requests are canceled
  ~SharedMutex();

  using executor_type = Executor;
  executor_type get_executor() const noexcept { return ex; }

  /// initiate an asynchronous request for an exclusive lock. when the lock is
  /// granted, the completion handler is invoked with a successful error code
  /// and a std::unique_lock that owns this mutex.
  /// Signature = void(boost::system::error_code, std::unique_lock)
  template <typename CompletionToken>
  auto async_lock(CompletionToken&& token);

  /// wait synchronously for an exclusive lock. if an error occurs before the
  /// lock is granted, that error is thrown as an exception
  void lock();

  /// wait synchronously for an exclusive lock. if an error occurs before the
  /// lock is granted, that error is assigned to 'ec'
  void lock(boost::system::error_code& ec);

  /// try to acquire an exclusive lock. if the lock is not immediately
  /// available, returns false
  bool try_lock();

  /// releases an exclusive lock. not required to be called from the same thread
  /// that initiated the lock
  void unlock();

  /// initiate an asynchronous request for a shared lock. when the lock is
  /// granted, the completion handler is invoked with a successful error code
  /// and a std::shared_lock that owns this mutex.
  /// Signature = void(boost::system::error_code, std::shared_lock)
  template <typename CompletionToken>
  auto async_lock_shared(CompletionToken&& token);

  /// wait synchronously for a shared lock. if an error occurs before the
  /// lock is granted, that error is thrown as an exception
  void lock_shared();

  /// wait synchronously for a shared lock. if an error occurs before the lock
  /// is granted, that error is assigned to 'ec'
  void lock_shared(boost::system::error_code& ec);

  /// try to acquire a shared lock. if the lock is not immediately available,
  /// returns false
  bool try_lock_shared();

  /// releases a shared lock. not required to be called from the same thread
  /// that initiated the lock
  void unlock_shared();

  /// cancel any pending requests for exclusive or shared locks with an
  /// operation_aborted error
  void cancel();

 private:
  Executor ex; //< default callback executor
  boost::intrusive_ptr<detail::SharedMutexImpl> impl;

  // allow lock guards to access impl
  friend class std::unique_lock<SharedMutex>;
  friend class std::shared_lock<SharedMutex>;
};


template <typename Executor>
SharedMutex<Executor>::SharedMutex(const Executor& ex)
  : ex(ex), impl(new detail::SharedMutexImpl)
{
}

template <typename Executor>
SharedMutex<Executor>::~SharedMutex()
{
  try {
    impl->cancel();
  } catch (const std::exception&) {
    // swallow any exceptions, the destructor can't throw
  }
}

template <typename Executor>
template <typename CompletionToken>
auto SharedMutex<Executor>::async_lock(CompletionToken&& token)
{
  return impl->async_lock(*this, std::forward<CompletionToken>(token));
}

template <typename Executor>
void SharedMutex<Executor>::lock()
{
  impl->lock();
}

template <typename Executor>
void SharedMutex<Executor>::lock(boost::system::error_code& ec)
{
  impl->lock(ec);
}

template <typename Executor>
bool SharedMutex<Executor>::try_lock()
{
  return impl->try_lock();
}

template <typename Executor>
void SharedMutex<Executor>::unlock()
{
  impl->unlock();
}

template <typename Executor>
template <typename CompletionToken>
auto SharedMutex<Executor>::async_lock_shared(CompletionToken&& token)
{
  return impl->async_lock_shared(*this, std::forward<CompletionToken>(token));
}

template <typename Executor>
void SharedMutex<Executor>::lock_shared()
{
  impl->lock_shared();
}

template <typename Executor>
void SharedMutex<Executor>::lock_shared(boost::system::error_code& ec)
{
  impl->lock_shared(ec);
}

template <typename Executor>
bool SharedMutex<Executor>::try_lock_shared()
{
  return impl->try_lock_shared();
}

template <typename Executor>
void SharedMutex<Executor>::unlock_shared()
{
  impl->unlock_shared();
}

template <typename Executor>
void SharedMutex<Executor>::cancel()
{
  impl->cancel();
}

} // namespace ceph::async

#include "common/async/detail/shared_lock.h"