Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/CopyOnWrite.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
/**
7
 * CopyOnWrite<T> allows code to safely read from a data structure without
8
 * worrying that reentrant code will modify it.
9
 */
10
11
#ifndef mozilla_image_CopyOnWrite_h
12
#define mozilla_image_CopyOnWrite_h
13
14
#include "mozilla/RefPtr.h"
15
#include "MainThreadUtils.h"
16
#include "nsISupportsImpl.h"
17
18
namespace mozilla {
19
namespace image {
20
21
///////////////////////////////////////////////////////////////////////////////
22
// Implementation Details
23
///////////////////////////////////////////////////////////////////////////////
24
25
namespace detail {
26
27
template <typename T>
28
class CopyOnWriteValue final
29
{
30
public:
31
  NS_INLINE_DECL_REFCOUNTING(CopyOnWriteValue)
32
33
  explicit CopyOnWriteValue(T* aValue)
34
    : mValue(aValue)
35
    , mReaders(0)
36
    , mWriter(false)
37
0
  {
38
0
  }
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::CopyOnWriteValue(mozilla::image::ObserverTable*)
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::CopyOnWriteValue(Value*)
39
  explicit CopyOnWriteValue(already_AddRefed<T>& aValue)
40
    : mValue(aValue)
41
    , mReaders(0)
42
    , mWriter(false)
43
  {
44
  }
45
  explicit CopyOnWriteValue(already_AddRefed<T>&& aValue)
46
    : mValue(aValue)
47
    , mReaders(0)
48
    , mWriter(false)
49
  {
50
  }
51
  explicit CopyOnWriteValue(const RefPtr<T>& aValue)
52
    : mValue(aValue)
53
    , mReaders(0)
54
    , mWriter(false)
55
  {
56
  }
57
  explicit CopyOnWriteValue(RefPtr<T>&& aValue)
58
    : mValue(aValue)
59
    , mReaders(0)
60
    , mWriter(false)
61
  {
62
  }
63
64
0
  T* get() { return mValue.get(); }
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::get()
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::get()
65
  const T* get() const { return mValue.get(); }
66
67
0
  bool HasReaders() const { return mReaders > 0; }
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::HasReaders() const
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::HasReaders() const
68
0
  bool HasWriter() const { return mWriter; }
69
  bool HasUsers() const { return HasReaders() || HasWriter(); }
70
71
0
  void LockForReading() { MOZ_ASSERT(!HasWriter()); mReaders++; }
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::LockForReading()
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::LockForReading()
72
0
  void UnlockForReading() { MOZ_ASSERT(HasReaders()); mReaders--; }
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::UnlockForReading()
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::UnlockForReading()
73
74
  struct MOZ_STACK_CLASS AutoReadLock
75
  {
76
    explicit AutoReadLock(CopyOnWriteValue* aValue)
77
      : mValue(aValue)
78
0
    {
79
0
      mValue->LockForReading();
80
0
    }
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::AutoReadLock::AutoReadLock(mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>*)
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::AutoReadLock::AutoReadLock(mozilla::image::detail::CopyOnWriteValue<Value>*)
81
0
    ~AutoReadLock() { mValue->UnlockForReading(); }
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::AutoReadLock::~AutoReadLock()
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::AutoReadLock::~AutoReadLock()
82
    CopyOnWriteValue<T>* mValue;
83
  };
84
85
0
  void LockForWriting() { MOZ_ASSERT(!HasUsers()); mWriter = true; }
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::LockForWriting()
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::LockForWriting()
86
0
  void UnlockForWriting() { MOZ_ASSERT(HasWriter()); mWriter = false; }
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::UnlockForWriting()
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::UnlockForWriting()
87
88
  struct MOZ_STACK_CLASS AutoWriteLock
89
  {
90
    explicit AutoWriteLock(CopyOnWriteValue* aValue)
91
      : mValue(aValue)
92
0
    {
93
0
      mValue->LockForWriting();
94
0
    }
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::AutoWriteLock::AutoWriteLock(mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>*)
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::AutoWriteLock::AutoWriteLock(mozilla::image::detail::CopyOnWriteValue<Value>*)
95
0
    ~AutoWriteLock() { mValue->UnlockForWriting(); }
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::AutoWriteLock::~AutoWriteLock()
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::AutoWriteLock::~AutoWriteLock()
96
    CopyOnWriteValue<T>* mValue;
97
  };
98
99
private:
100
  CopyOnWriteValue(const CopyOnWriteValue&) = delete;
101
  CopyOnWriteValue(CopyOnWriteValue&&) = delete;
102
103
0
  ~CopyOnWriteValue() { }
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<mozilla::image::ObserverTable>::~CopyOnWriteValue()
Unexecuted instantiation: mozilla::image::detail::CopyOnWriteValue<Value>::~CopyOnWriteValue()
104
105
  RefPtr<T> mValue;
106
  uint64_t mReaders = 0;
107
  bool mWriter = false;
108
};
109
110
} // namespace detail
111
112
113
///////////////////////////////////////////////////////////////////////////////
114
// Public API
115
///////////////////////////////////////////////////////////////////////////////
116
117
/**
118
 * CopyOnWrite<T> allows code to safely read from a data structure without
119
 * worrying that reentrant code will modify it. If reentrant code would modify
120
 * the data structure while other code is reading from it, a copy is made so
121
 * that readers can continue to use the old version.
122
 *
123
 * Note that it's legal to nest a writer inside any number of readers, but
124
 * nothing can be nested inside a writer. This is because it's assumed that the
125
 * state of the contained data structure may not be consistent during the write.
126
 *
127
 * This is a main-thread-only data structure.
128
 *
129
 * To work with CopyOnWrite<T>, a type T needs to be reference counted and to
130
 * support copy construction.
131
 */
132
template <typename T>
133
class CopyOnWrite final
134
{
135
  typedef detail::CopyOnWriteValue<T> CopyOnWriteValue;
136
137
public:
138
  explicit CopyOnWrite(T* aValue)
139
  : mValue(new CopyOnWriteValue(aValue))
140
0
  { }
Unexecuted instantiation: mozilla::image::CopyOnWrite<mozilla::image::ObserverTable>::CopyOnWrite(mozilla::image::ObserverTable*)
Unexecuted instantiation: mozilla::image::CopyOnWrite<Value>::CopyOnWrite(Value*)
141
142
  explicit CopyOnWrite(already_AddRefed<T>& aValue)
143
    : mValue(new CopyOnWriteValue(aValue))
144
  { }
145
146
  explicit CopyOnWrite(already_AddRefed<T>&& aValue)
147
    : mValue(new CopyOnWriteValue(aValue))
148
  { }
149
150
  explicit CopyOnWrite(const RefPtr<T>& aValue)
151
    : mValue(new CopyOnWriteValue(aValue))
152
  { }
153
154
  explicit CopyOnWrite(RefPtr<T>&& aValue)
155
    : mValue(new CopyOnWriteValue(aValue))
156
  { }
157
158
  /// @return true if it's safe to read at this time.
159
0
  bool CanRead() const { return !mValue->HasWriter(); }
160
161
  /**
162
   * Read from the contained data structure using the function @aReader.
163
   * @aReader will be passed a pointer of type |const T*|. It's not legal to
164
   * call this while a writer is active.
165
   *
166
   * @return whatever value @aReader returns, or nothing if @aReader is a void
167
   *         function.
168
   */
169
  template <typename ReadFunc>
170
  auto Read(ReadFunc aReader) const
171
    -> decltype(aReader(static_cast<const T*>(nullptr)))
172
0
  {
173
0
    MOZ_ASSERT(NS_IsMainThread());
174
0
    MOZ_ASSERT(CanRead());
175
0
176
0
    // Run the provided function while holding a read lock.
177
0
    RefPtr<CopyOnWriteValue> cowValue = mValue;
178
0
    typename CopyOnWriteValue::AutoReadLock lock(cowValue);
179
0
    return aReader(cowValue->get());
180
0
  }
Unexecuted instantiation: Unified_cpp_image1.cpp:_ZNK7mozilla5image11CopyOnWriteINS0_13ObserverTableEE4ReadIZNS0_15ProgressTracker18SyncNotifyProgressEjRKNS_3gfx12IntRectTypedINS6_12UnknownUnitsEEEE3$_1EEDTclfp_scPKS2_LDnEEET_
Unexecuted instantiation: Unified_cpp_image1.cpp:_ZNK7mozilla5image11CopyOnWriteINS0_13ObserverTableEE4ReadIZNKS0_15ProgressTracker13ObserverCountEvE3$_4EEDTclfp_scPKS2_LDnEEET_
Unexecuted instantiation: Unified_cpp_image1.cpp:_ZNK7mozilla5image11CopyOnWriteINS0_13ObserverTableEE4ReadIZNS0_15ProgressTracker14OnUnlockedDrawEvE3$_5EEDTclfp_scPKS2_LDnEEET_
Unexecuted instantiation: Unified_cpp_image1.cpp:_ZNK7mozilla5image11CopyOnWriteINS0_13ObserverTableEE4ReadIZNS0_15ProgressTracker9OnDiscardEvE3$_6EEDTclfp_scPKS2_LDnEEET_
Unexecuted instantiation: Unified_cpp_image1.cpp:_ZNK7mozilla5image11CopyOnWriteINS0_13ObserverTableEE4ReadIZNS0_15ProgressTracker16OnImageAvailableEvE3$_7EEDTclfp_scPKS2_LDnEEET_
Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZN26ImageCopyOnWrite_Read_Test8TestBodyEvE4$_29EEDTclfp_scPKS2_LDnEEET_
Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZN35ImageCopyOnWrite_RecursiveRead_Test8TestBodyEvE4$_30EEDTclfp_scPKS2_LDnEEET_
Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZZN35ImageCopyOnWrite_RecursiveRead_Test8TestBodyEvENK4$_30clEPKS2_EUlS8_E_EEDTclfp_scS8_LDnEEET_
Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvE4$_32EEDTclfp_scPKS2_LDnEEET_
Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZZZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvENK4$_32clEPKS2_ENKUlPS2_E_clES9_EUlS8_E_EEDTclfp_scS8_LDnEEET_
181
182
  /**
183
   * Read from the contained data structure using the function @aReader.
184
   * @aReader will be passed a pointer of type |const T*|. If it's currently not
185
   * possible to read because a writer is currently active, @aOnError will be
186
   * called instead.
187
   *
188
   * @return whatever value @aReader or @aOnError returns (their return types
189
   *         must be consistent), or nothing if the provided functions are void.
190
   */
191
  template <typename ReadFunc, typename ErrorFunc>
192
  auto Read(ReadFunc aReader, ErrorFunc aOnError) const
193
    -> decltype(aReader(static_cast<const T*>(nullptr)))
194
0
  {
195
0
    MOZ_ASSERT(NS_IsMainThread());
196
0
197
0
    if (!CanRead()) {
198
0
      return aOnError();
199
0
    }
200
0
201
0
    return Read(aReader);
202
0
  }
Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZZN35ImageCopyOnWrite_RecursiveRead_Test8TestBodyEvENK4$_30clEPKS2_EUlS8_E_ZZNS5_8TestBodyEvENKS6_clES8_EUlvE_EEDTclfp_scS8_LDnEEET_T0_
Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZNK7mozilla5image11CopyOnWriteI5ValueE4ReadIZZZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvENK4$_32clEPKS2_ENKUlPS2_E_clES9_EUlS8_E_ZZZNS5_8TestBodyEvENKS6_clES8_ENKSA_clES9_EUlvE_EEDTclfp_scS8_LDnEEET_T0_
203
204
  /// @return true if it's safe to write at this time.
205
0
  bool CanWrite() const { return !mValue->HasWriter(); }
206
207
  /**
208
   * Write to the contained data structure using the function @aWriter.
209
   * @aWriter will be passed a pointer of type |T*|. It's not legal to call this
210
   * while another writer is active.
211
   *
212
   * If readers are currently active, they will be able to continue reading from
213
   * a copy of the old version of the data structure. The copy will be destroyed
214
   * when all its readers finish.  Later readers and writers will see the
215
   * version of the data structure produced by the most recent call to Write().
216
   *
217
   * @return whatever value @aWriter returns, or nothing if @aWriter is a void
218
   *         function.
219
   */
220
  template <typename WriteFunc>
221
  auto Write(WriteFunc aWriter)
222
    -> decltype(aWriter(static_cast<T*>(nullptr)))
223
0
  {
224
0
    MOZ_ASSERT(NS_IsMainThread());
225
0
    MOZ_ASSERT(CanWrite());
226
0
227
0
    // If there are readers, we need to copy first.
228
0
    if (mValue->HasReaders()) {
229
0
      mValue = new CopyOnWriteValue(new T(*mValue->get()));
230
0
    }
231
0
232
0
    // Run the provided function while holding a write lock.
233
0
    RefPtr<CopyOnWriteValue> cowValue = mValue;
234
0
    typename CopyOnWriteValue::AutoWriteLock lock(cowValue);
235
0
    return aWriter(cowValue->get());
236
0
  }
Unexecuted instantiation: Unified_cpp_image1.cpp:_ZN7mozilla5image11CopyOnWriteINS0_13ObserverTableEE5WriteIZNS0_15ProgressTracker11AddObserverEPNS0_17IProgressObserverEE3$_2EEDTclfp_scPS2_LDnEEET_
Unexecuted instantiation: Unified_cpp_image1.cpp:_ZN7mozilla5image11CopyOnWriteINS0_13ObserverTableEE5WriteIZNS0_15ProgressTracker14RemoveObserverEPNS0_17IProgressObserverEE3$_3EEDTclfp_scPS2_LDnEEET_
Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZN7mozilla5image11CopyOnWriteI5ValueE5WriteIZN27ImageCopyOnWrite_Write_Test8TestBodyEvE4$_31EEDTclfp_scPS2_LDnEEET_
Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZN7mozilla5image11CopyOnWriteI5ValueE5WriteIZZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvENK4$_32clEPKS2_EUlPS2_E_EEDTclfp_scS9_LDnEEET_
Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZN7mozilla5image11CopyOnWriteI5ValueE5WriteIZZZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvENK4$_32clEPKS2_ENKUlPS2_E_clES9_EUlS9_E_EEDTclfp_scS9_LDnEEET_
237
238
  /**
239
   * Write to the contained data structure using the function @aWriter.
240
   * @aWriter will be passed a pointer of type |T*|. If it's currently not
241
   * possible to write because a writer is currently active, @aOnError will be
242
   * called instead.
243
   *
244
   * If readers are currently active, they will be able to continue reading from
245
   * a copy of the old version of the data structure. The copy will be destroyed
246
   * when all its readers finish.  Later readers and writers will see the
247
   * version of the data structure produced by the most recent call to Write().
248
   *
249
   * @return whatever value @aWriter or @aOnError returns (their return types
250
   *         must be consistent), or nothing if the provided functions are void.
251
   */
252
  template <typename WriteFunc, typename ErrorFunc>
253
  auto Write(WriteFunc aWriter, ErrorFunc aOnError)
254
    -> decltype(aWriter(static_cast<T*>(nullptr)))
255
0
  {
256
0
    MOZ_ASSERT(NS_IsMainThread());
257
0
258
0
    if (!CanWrite()) {
259
0
      return aOnError();
260
0
    }
261
0
262
0
    return Write(aWriter);
263
0
  }
Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZN7mozilla5image11CopyOnWriteI5ValueE5WriteIZZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvENK4$_32clEPKS2_EUlPS2_E_ZZNS5_8TestBodyEvENKS6_clES8_EUlvE_EEDTclfp_scS9_LDnEEET_T0_
Unexecuted instantiation: Unified_cpp_image_test_gtest0.cpp:_ZN7mozilla5image11CopyOnWriteI5ValueE5WriteIZZZN36ImageCopyOnWrite_WriteRecursive_Test8TestBodyEvENK4$_32clEPKS2_ENKUlPS2_E_clES9_EUlS9_E_ZZZNS5_8TestBodyEvENKS6_clES8_ENKSA_clES9_EUlvE0_EEDTclfp_scS9_LDnEEET_T0_
264
265
private:
266
  CopyOnWrite(const CopyOnWrite&) = delete;
267
  CopyOnWrite(CopyOnWrite&&) = delete;
268
269
  RefPtr<CopyOnWriteValue> mValue;
270
};
271
272
} // namespace image
273
} // namespace mozilla
274
275
#endif // mozilla_image_CopyOnWrite_h