summaryrefslogtreecommitdiffstats
path: root/memory/volatile/VolatileBuffer.h
blob: badd7f3cec5ee662cfff155df99a57d21d18a2f4 (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
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozalloc_VolatileBuffer_h
#define mozalloc_VolatileBuffer_h

#include "mozilla/mozalloc.h"
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/RefCounted.h"

/* VolatileBuffer
 *
 * This class represents a piece of memory that can potentially be reclaimed
 * by the OS when not in use. As long as there are one or more
 * VolatileBufferPtrs holding on to a VolatileBuffer, the memory will remain
 * available. However, when there are no VolatileBufferPtrs holding a
 * VolatileBuffer, the OS can purge the pages if it wants to. The OS can make
 * better decisions about what pages to purge than we can.
 *
 * VolatileBuffers may not always be volatile - if the allocation is too small,
 * or if the OS doesn't support the feature, or if the OS doesn't want to,
 * the buffer will be allocated on heap.
 *
 * VolatileBuffer allocations are fallible. They are intended for uses where
 * one may allocate large buffers for caching data. Init() must be called
 * exactly once.
 *
 * After getting a reference to VolatileBuffer using VolatileBufferPtr,
 * WasPurged() can be used to check if the OS purged any pages in the buffer.
 * The OS cannot purge a buffer immediately after a VolatileBuffer is
 * initialized. At least one VolatileBufferPtr must be created before the
 * buffer can be purged, so the first use of VolatileBufferPtr does not need
 * to check WasPurged().
 *
 * When a buffer is purged, some or all of the buffer is zeroed out. This
 * API cannot tell which parts of the buffer were lost.
 *
 * VolatileBuffer and VolatileBufferPtr are threadsafe.
 */

namespace mozilla {

class VolatileBuffer {
  friend class VolatileBufferPtr_base;

 public:
  MOZ_DECLARE_REFCOUNTED_TYPENAME(VolatileBuffer)
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VolatileBuffer)

  VolatileBuffer();

  /* aAlignment must be a multiple of the pointer size */
  bool Init(size_t aSize, size_t aAlignment = sizeof(void*));

  size_t HeapSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
  size_t NonHeapSizeOfExcludingThis() const;
  bool OnHeap() const;

 protected:
  bool Lock(void** aBuf);
  void Unlock();

 private:
  ~VolatileBuffer();

  /**
   * Protects mLockCount, mFirstLock, and changes to the volatility of our
   * buffer.  Other member variables are read-only except in Init() and the
   * destructor.
   */
  Mutex mMutex MOZ_UNANNOTATED;

  void* mBuf;
  size_t mSize;
  int mLockCount;
#if defined(ANDROID)
  int mFd;
#elif defined(XP_DARWIN)
  bool mHeap;
#elif defined(XP_WIN)
  bool mHeap;
  bool mFirstLock;
#endif
};

class VolatileBufferPtr_base {
 public:
  explicit VolatileBufferPtr_base(VolatileBuffer* vbuf)
      : mVBuf(vbuf), mMapping(nullptr), mPurged(false) {
    Lock();
  }

  ~VolatileBufferPtr_base() { Unlock(); }

  bool WasBufferPurged() const { return mPurged; }

 protected:
  RefPtr<VolatileBuffer> mVBuf;
  void* mMapping;

  void Set(VolatileBuffer* vbuf) {
    Unlock();
    mVBuf = vbuf;
    Lock();
  }

 private:
  bool mPurged;

  void Lock() {
    if (mVBuf) {
      mPurged = !mVBuf->Lock(&mMapping);
    } else {
      mMapping = nullptr;
      mPurged = false;
    }
  }

  void Unlock() {
    if (mVBuf) {
      mVBuf->Unlock();
    }
  }
};

template <class T>
class VolatileBufferPtr : public VolatileBufferPtr_base {
 public:
  explicit VolatileBufferPtr(VolatileBuffer* vbuf)
      : VolatileBufferPtr_base(vbuf) {}
  VolatileBufferPtr() : VolatileBufferPtr_base(nullptr) {}

  VolatileBufferPtr(VolatileBufferPtr&& aOther)
      : VolatileBufferPtr_base(aOther.mVBuf) {
    aOther.Set(nullptr);
  }

  operator T*() const { return (T*)mMapping; }

  VolatileBufferPtr& operator=(VolatileBuffer* aVBuf) {
    Set(aVBuf);
    return *this;
  }

  VolatileBufferPtr& operator=(VolatileBufferPtr&& aOther) {
    MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
    Set(aOther.mVBuf);
    aOther.Set(nullptr);
    return *this;
  }

 private:
  VolatileBufferPtr(VolatileBufferPtr const& vbufptr) = delete;
};

}  // namespace mozilla

#endif /* mozalloc_VolatileBuffer_h */