Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
// An interface for read-write locks.
8
9
#ifndef mozilla_RWLock_h
10
#define mozilla_RWLock_h
11
12
#include "mozilla/Assertions.h"
13
#include "mozilla/BlockingResourceBase.h"
14
#include "mozilla/GuardObjects.h"
15
16
#ifndef XP_WIN
17
# include <pthread.h>
18
#endif
19
20
namespace mozilla {
21
22
// A RWLock is similar to a Mutex, but whereas a Mutex permits only a single
23
// reader thread or a single writer thread to access a piece of data, a
24
// RWLock distinguishes between readers and writers: you may have multiple
25
// reader threads concurrently accessing a piece of data or a single writer
26
// thread. This difference should guide your usage of RWLock: if you are not
27
// reading the data from multiple threads simultaneously or you are writing
28
// to the data roughly as often as read from it, then Mutex will suit your
29
// purposes just fine.
30
//
31
// You should be using the AutoReadLock and AutoWriteLock classes, below,
32
// for RAII read locking and write locking, respectively. If you really must
33
// take a read lock manually, call the ReadLock method; to relinquish that
34
// read lock, call the ReadUnlock method. Similarly, WriteLock and WriteUnlock
35
// perform the same operations, but for write locks.
36
//
37
// It is unspecified what happens when a given thread attempts to acquire the
38
// same lock in multiple ways; some underlying implementations of RWLock do
39
// support acquiring a read lock multiple times on a given thread, but you
40
// should not rely on this behavior.
41
//
42
// It is unspecified whether RWLock gives priority to waiting readers or
43
// a waiting writer when unlocking.
44
class RWLock : public BlockingResourceBase {
45
public:
46
explicit RWLock(const char* aName);
47
48
// Windows rwlocks don't need any special handling to be destroyed, but
49
// POSIX ones do.
50
#ifdef XP_WIN
51
~RWLock() = default;
52
#else
53
~RWLock();
54
#endif
55
56
#ifdef DEBUG
57
bool LockedForWritingByCurrentThread();
58
void ReadLock();
59
void ReadUnlock();
60
void WriteLock();
61
void WriteUnlock();
62
#else
63
void ReadLock() { ReadLockInternal(); }
64
void ReadUnlock() { ReadUnlockInternal(); }
65
void WriteLock() { WriteLockInternal(); }
66
void WriteUnlock() { WriteUnlockInternal(); }
67
#endif
68
69
private:
70
void ReadLockInternal();
71
void ReadUnlockInternal();
72
void WriteLockInternal();
73
void WriteUnlockInternal();
74
75
RWLock() = delete;
76
RWLock(const RWLock&) = delete;
77
RWLock& operator=(const RWLock&) = delete;
78
79
#ifndef XP_WIN
80
pthread_rwlock_t mRWLock;
81
#else
82
// SRWLock is pointer-sized. We declare it in such a fashion here to
83
// avoid pulling in windows.h wherever this header is used.
84
void* mRWLock;
85
#endif
86
87
#ifdef DEBUG
88
// We record the owning thread for write locks only.
89
PRThread* mOwningThread;
90
#endif
91
};
92
93
template <typename T>
94
class MOZ_RAII BaseAutoReadLock {
95
public:
96
explicit BaseAutoReadLock(T& aLock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
97
: mLock(&aLock) {
98
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
99
MOZ_ASSERT(mLock, "null lock");
100
mLock->ReadLock();
101
}
102
103
~BaseAutoReadLock() { mLock->ReadUnlock(); }
104
105
private:
106
BaseAutoReadLock() = delete;
107
BaseAutoReadLock(const BaseAutoReadLock&) = delete;
108
BaseAutoReadLock& operator=(const BaseAutoReadLock&) = delete;
109
110
T* mLock;
111
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
112
};
113
114
template <typename T>
115
class MOZ_RAII BaseAutoWriteLock final {
116
public:
117
explicit BaseAutoWriteLock(T& aLock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
118
: mLock(&aLock) {
119
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
120
MOZ_ASSERT(mLock, "null lock");
121
mLock->WriteLock();
122
}
123
124
~BaseAutoWriteLock() { mLock->WriteUnlock(); }
125
126
private:
127
BaseAutoWriteLock() = delete;
128
BaseAutoWriteLock(const BaseAutoWriteLock&) = delete;
129
BaseAutoWriteLock& operator=(const BaseAutoWriteLock&) = delete;
130
131
T* mLock;
132
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
133
};
134
135
// Read lock and unlock a RWLock with RAII semantics. Much preferred to bare
136
// calls to ReadLock() and ReadUnlock().
137
typedef BaseAutoReadLock<RWLock> AutoReadLock;
138
139
// Write lock and unlock a RWLock with RAII semantics. Much preferred to bare
140
// calls to WriteLock() and WriteUnlock().
141
typedef BaseAutoWriteLock<RWLock> AutoWriteLock;
142
143
// XXX: normally we would define StaticRWLock as
144
// MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS, but the contexts in which it
145
// is used (e.g. member variables in a third-party library) are non-trivial
146
// to modify to properly declare everything at static scope. As those
147
// third-party libraries are the only clients, put it behind the detail
148
// namespace to discourage other (possibly erroneous) uses from popping up.
149
150
namespace detail {
151
152
class StaticRWLock {
153
public:
154
// In debug builds, check that mLock is initialized for us as we expect by
155
// the compiler. In non-debug builds, don't declare a constructor so that
156
// the compiler can see that the constructor is trivial.
157
#ifdef DEBUG
158
StaticRWLock() { MOZ_ASSERT(!mLock); }
159
#endif
160
161
void ReadLock() { Lock()->ReadLock(); }
162
void ReadUnlock() { Lock()->ReadUnlock(); }
163
void WriteLock() { Lock()->WriteLock(); }
164
void WriteUnlock() { Lock()->WriteUnlock(); }
165
166
private:
167
RWLock* Lock() {
168
if (mLock) {
169
return mLock;
170
}
171
172
RWLock* lock = new RWLock("StaticRWLock");
173
if (!mLock.compareExchange(nullptr, lock)) {
174
delete lock;
175
}
176
177
return mLock;
178
}
179
180
Atomic<RWLock*> mLock;
181
182
// Disallow copy constructor, but only in debug mode. We only define
183
// a default constructor in debug mode (see above); if we declared
184
// this constructor always, the compiler wouldn't generate a trivial
185
// default constructor for us in non-debug mode.
186
#ifdef DEBUG
187
StaticRWLock(const StaticRWLock& aOther);
188
#endif
189
190
// Disallow these operators.
191
StaticRWLock& operator=(StaticRWLock* aRhs);
192
static void* operator new(size_t) noexcept(true);
193
static void operator delete(void*);
194
};
195
196
typedef BaseAutoReadLock<StaticRWLock> StaticAutoReadLock;
197
typedef BaseAutoWriteLock<StaticRWLock> StaticAutoWriteLock;
198
199
} // namespace detail
200
201
} // namespace mozilla
202
203
#endif // mozilla_RWLock_h