libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
rw_locking.hpp
Go to the documentation of this file.
1 #ifndef LIBJMMCG_CORE_RW_LOCKING_HPP
2 #define LIBJMMCG_CORE_RW_LOCKING_HPP
3 
4 /******************************************************************************
5 ** Copyright © 2011 by J.M.McGuiness, coder@hussar.me.uk
6 **
7 ** This library is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU Lesser General Public
9 ** License as published by the Free Software Foundation; either
10 ** version 2.1 of the License, or (at your option) any later version.
11 **
12 ** This library is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ** Lesser General Public License for more details.
16 **
17 ** You should have received a copy of the GNU Lesser General Public
18 ** License along with this library; if not, write to the Free Software
19 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 
22 #include "atomic_counter.hpp"
23 #include "locking.hpp"
24 
25 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE { namespace ppd { namespace lock { namespace rw {
26 
27  template<class T> class locker;
28 
29  template<class L>
31  public:
32  typedef L atomic_t;
33  typedef typename atomic_t::lock_traits lock_traits;
36 
37  /**
38  To assist in allowing compile-time computation of the algorithmic order of the threading model.
39  */
40  static constexpr ppd::generic_traits::memory_access_modes memory_access_mode=atomic_t::memory_access_mode;
41 
42  private:
43  atomic_t &rw_lk;
44 
45  public:
46  explicit constexpr __stdcall decaying_write_impl(atomic_t &l) noexcept(true) FORCE_INLINE;
47 
48  /**
49  Note that this is the write-lock interface.
50  */
51  atomic_state_type __fastcall lock() noexcept(noexcept(rw_lk.lock())) FORCE_INLINE {
52  return rw_lk.lock();
53  }
54  /**
55  Note that this is the write-lock interface. Also note that the timeout is ignored, the lock waits indefinitely.
56  */
57  atomic_state_type __fastcall lock(const timeout_type) noexcept(false) FORCE_INLINE;
58  /**
59  Note that this is the write-lock interface.
60  */
61  atomic_state_type __fastcall unlock() noexcept(true) FORCE_INLINE;
62  /// Atomically convert the writer lock to a read lock.
63  void decay() noexcept(true) FORCE_INLINE;
64  };
65  /// A class that allows a write lock to optionally, atomically decay to a read lock.
66  /**
67  Only taken if there are no other readers nor any writers. If multiple writers attempt to gain the lock, only one will succeed, the choice is undefined. If at least one read lock is held, then any write locks will block until there are no read locks.
68 
69  \see rw
70  \see read
71  \see write
72  */
73  template<class T>
74  class decaying_write_impl<locker<T> > {
75  public:
76  typedef locker<T> atomic_t;
77  typedef typename atomic_t::lock_traits lock_traits;
80 
81  /**
82  To assist in allowing compile-time computation of the algorithmic order of the threading model.
83  */
84  static constexpr ppd::generic_traits::memory_access_modes memory_access_mode=atomic_t::memory_access_mode;
85 
86  explicit constexpr __stdcall decaying_write_impl(atomic_t &l) noexcept(true) FORCE_INLINE;
87 
88  /**
89  Note that this is the write-lock interface.
90  */
91  atomic_state_type __fastcall lock() noexcept(false) FORCE_INLINE;
92  /**
93  Note that this is the write-lock interface. Also note that the timeout is ignored, the lock waits indefinitely.
94  */
95  atomic_state_type __fastcall lock(const timeout_type) noexcept(false) FORCE_INLINE;
96  /**
97  Note that this is the write-lock interface.
98  */
99  atomic_state_type __fastcall unlock() noexcept(true) FORCE_INLINE;
100  /// Atomically convert the writer lock to a read lock.
101  void decay() noexcept(true) FORCE_INLINE;
102 
103  private:
104  atomic_t &rw_lk;
105  typename atomic_t::writer_atomic_t &exclusive_w_lk;
106  bool decayed;
107  };
108 
109  /// A readers-writer lock to control a resource.
110  /**
111  The classic lock that allows many readers, but only one writer to occupy the lock at any one time.
112  The order of writers vs. readers is implementation defined.
113  This allows CREW implementations.
114 
115  \see read
116  \see write
117  \see decaying_write
118  */
119  template<
120  class T ///< The lock traits.
121  >
122  class locker final : public lockable<T> {
123  public:
124  typedef typename lockable<T>::lock_traits lock_traits;
127  /**
128  To assist in allowing compile-time computation of the algorithmic order of the threading model.
129  */
131 
132  /// Take a shared read lock on the resource, using RAII.
133  /**
134  If a writer has acquired the resource, then this will block until no writers have the resource. If multiple writers and readers are waiting for the resource, then the order in which readers and writers acquire the resource is undefined.
135 
136  \see rw
137  \see write
138  */
139  class read_lock_type final {
140  public:
141  typedef locker atomic_t;
142  typedef typename atomic_t::lock_traits lock_traits;
143  typedef typename lock_traits::timeout_type timeout_type;
144 
145  /**
146  To assist in allowing compile-time computation of the algorithmic order of the threading model.
147  */
149 
150  /**
151  Note that the time-out is ignored, the lock waits indefinitely.
152  */
153  __stdcall read_lock_type(atomic_t &l, const timeout_type) noexcept(false) FORCE_INLINE;
154  read_lock_type(read_lock_type const &)=delete;
155  __stdcall ~read_lock_type() noexcept(true) FORCE_INLINE;
156 
157  private:
158  atomic_t &lk;
159  };
160 
161  /// Take an exclusive write-lock on the resource, using RAII.
162  /**
163  Only taken if there are no other readers nor any writers. If multiple writers attempt to gain the lock, only one will succeed, the choice is undefined. If at least one read lock is held, then any write locks will block until there are no read locks.
164 
165  \see rw
166  \see read_lock_type
167  \see decaying_write
168  */
169  class write_lock_type final {
170  public:
171  typedef locker atomic_t;
172  typedef typename atomic_t::lock_traits lock_traits;
173  typedef typename lock_traits::timeout_type timeout_type;
174 
175  /**
176  To assist in allowing compile-time computation of the algorithmic order of the threading model.
177  */
179 
180  /**
181  Note that the timeout is ignored, the lock waits indefinitely.
182  */
183  __stdcall write_lock_type(atomic_t &l, const timeout_type) noexcept(false) FORCE_INLINE;
184  write_lock_type(write_lock_type const &)=delete;
185  __stdcall ~write_lock_type() noexcept(true) FORCE_INLINE;
186 
187  /**
188  Does nothing, just to make the interface the same.
189  */
190  constexpr void decay() const noexcept(true) FORCE_INLINE {}
191 
192  private:
193  const typename atomic_t::writer_lk_t exclusive_w_lk;
194  atomic_t &rw_lk;
195  };
196 
197  /// A class that allows a write lock to optionally, atomically decay to a read lock, using RAII.
198  /**
199  \see decaying_write_impl
200  */
201  class decaying_write_lock_type final : protected decaying_write_impl<locker<T> > {
202  public:
203  typedef decaying_write_impl<locker<T> > base_t;
204  typedef typename base_t::atomic_t atomic_t;
205  typedef typename atomic_t::lock_traits lock_traits;
207  using base_t::memory_access_mode;
208 
209  /**
210  Note that the timeout is ignored, the lock waits indefinitely.
211  */
212  __stdcall decaying_write_lock_type(atomic_t &l, const timeout_type) noexcept(false) FORCE_INLINE;
213  __stdcall ~decaying_write_lock_type() noexcept(true) FORCE_INLINE;
214 
215  void operator=(decaying_write_lock_type const &)=delete;
216  void operator=(decaying_write_lock_type &&)=delete;
217 
218  /// Atomically convert the writer lock to a read lock.
219  void decay() noexcept(true) FORCE_INLINE;
220  };
221 
222  __stdcall locker() noexcept(false) FORCE_INLINE;
223 
224  locker(locker const &)=delete;
225  locker(locker &&)=delete;
226  void operator=(locker const &)=delete;
227  void operator=(locker &&)=delete;
228 
229  /**
230  Note that this is the read-lock interface.
231  */
232  atomic_state_type __fastcall lock() noexcept(false) override FORCE_INLINE;
233  /**
234  Note that this is the read-lock interface.
235  */
236  atomic_state_type __fastcall lock(const timeout_type) noexcept(false) override FORCE_INLINE {
237  return this->lock();
238  }
239  /**
240  Note that this is the read-lock interface.
241  */
242  atomic_state_type __fastcall unlock() noexcept(true) override FORCE_INLINE;
243 
244 ///\todo G++ issues: private:
248  typedef typename lock_traits::template atomic_counter_type<long> atomic_counter_type;
249  friend class write;
250  friend class decaying_write;
251 
255 
256  private:
257  atomic_state_type __fastcall try_lock() noexcept(true) override FORCE_INLINE {
258  return lock_traits::atom_unset;
259  }
260  };
261 
262 } } } } }
263 
264 #include "rw_locking_impl.hpp"
265 
266 #endif