summaryrefslogtreecommitdiffstats
path: root/memory/volatile
diff options
context:
space:
mode:
Diffstat (limited to 'memory/volatile')
-rw-r--r--memory/volatile/VolatileBuffer.h161
-rw-r--r--memory/volatile/VolatileBufferAshmem.cpp120
-rw-r--r--memory/volatile/VolatileBufferFallback.cpp67
-rw-r--r--memory/volatile/VolatileBufferOSX.cpp105
-rw-r--r--memory/volatile/VolatileBufferWindows.cpp130
-rw-r--r--memory/volatile/moz.build31
-rw-r--r--memory/volatile/tests/TestVolatileBuffer.cpp100
-rw-r--r--memory/volatile/tests/moz.build11
8 files changed, 725 insertions, 0 deletions
diff --git a/memory/volatile/VolatileBuffer.h b/memory/volatile/VolatileBuffer.h
new file mode 100644
index 0000000000..badd7f3cec
--- /dev/null
+++ b/memory/volatile/VolatileBuffer.h
@@ -0,0 +1,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 */
diff --git a/memory/volatile/VolatileBufferAshmem.cpp b/memory/volatile/VolatileBufferAshmem.cpp
new file mode 100644
index 0000000000..99a0d1307f
--- /dev/null
+++ b/memory/volatile/VolatileBufferAshmem.cpp
@@ -0,0 +1,120 @@
+/* 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/. */
+
+#include "VolatileBuffer.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/mozalloc.h"
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "mozilla/Ashmem.h"
+
+#ifdef MOZ_MEMORY
+extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size);
+#endif
+
+#define MIN_VOLATILE_ALLOC_SIZE 8192
+
+namespace mozilla {
+
+VolatileBuffer::VolatileBuffer()
+ : mMutex("VolatileBuffer"),
+ mBuf(nullptr),
+ mSize(0),
+ mLockCount(0),
+ mFd(-1) {}
+
+bool VolatileBuffer::Init(size_t aSize, size_t aAlignment) {
+ MOZ_ASSERT(!mSize && !mBuf, "Init called twice");
+ MOZ_ASSERT(!(aAlignment % sizeof(void*)),
+ "Alignment must be multiple of pointer size");
+
+ mSize = aSize;
+ if (aSize < MIN_VOLATILE_ALLOC_SIZE) {
+ goto heap_alloc;
+ }
+
+ mFd = mozilla::android::ashmem_create(nullptr, mSize);
+ if (mFd < 0) {
+ goto heap_alloc;
+ }
+
+ mBuf = mmap(nullptr, mSize, PROT_READ | PROT_WRITE, MAP_SHARED, mFd, 0);
+ if (mBuf != MAP_FAILED) {
+ return true;
+ }
+
+heap_alloc:
+ mBuf = nullptr;
+ if (mFd >= 0) {
+ close(mFd);
+ mFd = -1;
+ }
+
+#ifdef MOZ_MEMORY
+ posix_memalign(&mBuf, aAlignment, aSize);
+#else
+ mBuf = memalign(aAlignment, aSize);
+#endif
+ return !!mBuf;
+}
+
+VolatileBuffer::~VolatileBuffer() {
+ MOZ_ASSERT(mLockCount == 0, "Being destroyed with non-zero lock count?");
+
+ if (OnHeap()) {
+ free(mBuf);
+ } else {
+ munmap(mBuf, mSize);
+ close(mFd);
+ }
+}
+
+bool VolatileBuffer::Lock(void** aBuf) {
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(mBuf, "Attempting to lock an uninitialized VolatileBuffer");
+
+ *aBuf = mBuf;
+ if (++mLockCount > 1 || OnHeap()) {
+ return true;
+ }
+
+ // Zero offset and zero length means we want to pin/unpin the entire thing.
+ struct ashmem_pin pin = {0, 0};
+ return ioctl(mFd, ASHMEM_PIN, &pin) == ASHMEM_NOT_PURGED;
+}
+
+void VolatileBuffer::Unlock() {
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(mLockCount > 0, "VolatileBuffer unlocked too many times!");
+ if (--mLockCount || OnHeap()) {
+ return;
+ }
+
+ struct ashmem_pin pin = {0, 0};
+ ioctl(mFd, ASHMEM_UNPIN, &pin);
+}
+
+bool VolatileBuffer::OnHeap() const { return mFd < 0; }
+
+size_t VolatileBuffer::HeapSizeOfExcludingThis(
+ MallocSizeOf aMallocSizeOf) const {
+ return OnHeap() ? aMallocSizeOf(mBuf) : 0;
+}
+
+size_t VolatileBuffer::NonHeapSizeOfExcludingThis() const {
+ if (OnHeap()) {
+ return 0;
+ }
+
+ return (mSize + (PAGE_SIZE - 1)) & PAGE_MASK;
+}
+
+} // namespace mozilla
diff --git a/memory/volatile/VolatileBufferFallback.cpp b/memory/volatile/VolatileBufferFallback.cpp
new file mode 100644
index 0000000000..d4311788ff
--- /dev/null
+++ b/memory/volatile/VolatileBufferFallback.cpp
@@ -0,0 +1,67 @@
+/* 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/. */
+
+#include "VolatileBuffer.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/mozalloc.h"
+
+#ifdef MOZ_MEMORY
+int posix_memalign(void** memptr, size_t alignment, size_t size);
+#endif
+
+namespace mozilla {
+
+VolatileBuffer::VolatileBuffer()
+ : mMutex("VolatileBuffer"), mBuf(nullptr), mSize(0), mLockCount(0) {}
+
+bool VolatileBuffer::Init(size_t aSize, size_t aAlignment) {
+ MOZ_ASSERT(!mSize && !mBuf, "Init called twice");
+ MOZ_ASSERT(!(aAlignment % sizeof(void*)),
+ "Alignment must be multiple of pointer size");
+
+ mSize = aSize;
+#if defined(MOZ_MEMORY) || defined(HAVE_POSIX_MEMALIGN)
+ if (posix_memalign(&mBuf, aAlignment, aSize) != 0) {
+ return false;
+ }
+#else
+# error "No memalign implementation found"
+#endif
+ return !!mBuf;
+}
+
+VolatileBuffer::~VolatileBuffer() {
+ MOZ_ASSERT(mLockCount == 0, "Being destroyed with non-zero lock count?");
+
+ free(mBuf);
+}
+
+bool VolatileBuffer::Lock(void** aBuf) {
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(mBuf, "Attempting to lock an uninitialized VolatileBuffer");
+
+ *aBuf = mBuf;
+ mLockCount++;
+
+ return true;
+}
+
+void VolatileBuffer::Unlock() {
+ MutexAutoLock lock(mMutex);
+
+ mLockCount--;
+ MOZ_ASSERT(mLockCount >= 0, "VolatileBuffer unlocked too many times!");
+}
+
+bool VolatileBuffer::OnHeap() const { return true; }
+
+size_t VolatileBuffer::HeapSizeOfExcludingThis(
+ MallocSizeOf aMallocSizeOf) const {
+ return aMallocSizeOf(mBuf);
+}
+
+size_t VolatileBuffer::NonHeapSizeOfExcludingThis() const { return 0; }
+
+} // namespace mozilla
diff --git a/memory/volatile/VolatileBufferOSX.cpp b/memory/volatile/VolatileBufferOSX.cpp
new file mode 100644
index 0000000000..c6299d1db0
--- /dev/null
+++ b/memory/volatile/VolatileBufferOSX.cpp
@@ -0,0 +1,105 @@
+/* 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/. */
+
+#include "VolatileBuffer.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/mozalloc.h"
+
+#include <mach/mach.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#define MIN_VOLATILE_ALLOC_SIZE 8192
+
+namespace mozilla {
+
+VolatileBuffer::VolatileBuffer()
+ : mMutex("VolatileBuffer"),
+ mBuf(nullptr),
+ mSize(0),
+ mLockCount(0),
+ mHeap(false) {}
+
+bool VolatileBuffer::Init(size_t aSize, size_t aAlignment) {
+ MOZ_ASSERT(!mSize && !mBuf, "Init called twice");
+ MOZ_ASSERT(!(aAlignment % sizeof(void*)),
+ "Alignment must be multiple of pointer size");
+
+ mSize = aSize;
+
+ kern_return_t ret = 0;
+ if (aSize < MIN_VOLATILE_ALLOC_SIZE) {
+ goto heap_alloc;
+ }
+
+ ret = vm_allocate(mach_task_self(), (vm_address_t*)&mBuf, mSize,
+ VM_FLAGS_PURGABLE | VM_FLAGS_ANYWHERE);
+ if (ret == KERN_SUCCESS) {
+ return true;
+ }
+
+heap_alloc:
+ (void)posix_memalign(&mBuf, aAlignment, aSize);
+ mHeap = true;
+ return !!mBuf;
+}
+
+VolatileBuffer::~VolatileBuffer() {
+ MOZ_ASSERT(mLockCount == 0, "Being destroyed with non-zero lock count?");
+
+ if (OnHeap()) {
+ free(mBuf);
+ } else {
+ vm_deallocate(mach_task_self(), (vm_address_t)mBuf, mSize);
+ }
+}
+
+bool VolatileBuffer::Lock(void** aBuf) {
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(mBuf, "Attempting to lock an uninitialized VolatileBuffer");
+
+ *aBuf = mBuf;
+ if (++mLockCount > 1 || OnHeap()) {
+ return true;
+ }
+
+ int state = VM_PURGABLE_NONVOLATILE;
+ kern_return_t ret = vm_purgable_control(mach_task_self(), (vm_address_t)mBuf,
+ VM_PURGABLE_SET_STATE, &state);
+ return ret == KERN_SUCCESS && !(state & VM_PURGABLE_EMPTY);
+}
+
+void VolatileBuffer::Unlock() {
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(mLockCount > 0, "VolatileBuffer unlocked too many times!");
+ if (--mLockCount || OnHeap()) {
+ return;
+ }
+
+ int state = VM_PURGABLE_VOLATILE | VM_VOLATILE_GROUP_DEFAULT;
+ DebugOnly<kern_return_t> ret = vm_purgable_control(
+ mach_task_self(), (vm_address_t)mBuf, VM_PURGABLE_SET_STATE, &state);
+ MOZ_ASSERT(ret == KERN_SUCCESS, "Failed to set buffer as purgable");
+}
+
+bool VolatileBuffer::OnHeap() const { return mHeap; }
+
+size_t VolatileBuffer::HeapSizeOfExcludingThis(
+ MallocSizeOf aMallocSizeOf) const {
+ return OnHeap() ? aMallocSizeOf(mBuf) : 0;
+}
+
+size_t VolatileBuffer::NonHeapSizeOfExcludingThis() const {
+ if (OnHeap()) {
+ return 0;
+ }
+
+ unsigned long pagemask = getpagesize() - 1;
+ return (mSize + pagemask) & ~pagemask;
+}
+
+} // namespace mozilla
diff --git a/memory/volatile/VolatileBufferWindows.cpp b/memory/volatile/VolatileBufferWindows.cpp
new file mode 100644
index 0000000000..fbd4eec07a
--- /dev/null
+++ b/memory/volatile/VolatileBufferWindows.cpp
@@ -0,0 +1,130 @@
+/* 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/. */
+
+#include "VolatileBuffer.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/mozalloc.h"
+
+#include <windows.h>
+
+#ifdef MOZ_MEMORY
+extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size);
+#endif
+
+#ifndef MEM_RESET_UNDO
+# define MEM_RESET_UNDO 0x1000000
+#endif
+
+#define MIN_VOLATILE_ALLOC_SIZE 8192
+
+namespace mozilla {
+
+VolatileBuffer::VolatileBuffer()
+ : mMutex("VolatileBuffer"),
+ mBuf(nullptr),
+ mSize(0),
+ mLockCount(0),
+ mHeap(false),
+ mFirstLock(true) {}
+
+bool VolatileBuffer::Init(size_t aSize, size_t aAlignment) {
+ MOZ_ASSERT(!mSize && !mBuf, "Init called twice");
+ MOZ_ASSERT(!(aAlignment % sizeof(void*)),
+ "Alignment must be multiple of pointer size");
+
+ mSize = aSize;
+ if (aSize < MIN_VOLATILE_ALLOC_SIZE) {
+ goto heap_alloc;
+ }
+
+ mBuf = VirtualAllocEx(GetCurrentProcess(), nullptr, mSize,
+ MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ if (mBuf) {
+ return true;
+ }
+
+heap_alloc:
+#ifdef MOZ_MEMORY
+ posix_memalign(&mBuf, aAlignment, aSize);
+#else
+ mBuf = _aligned_malloc(aSize, aAlignment);
+#endif
+ mHeap = true;
+ return !!mBuf;
+}
+
+VolatileBuffer::~VolatileBuffer() {
+ MOZ_ASSERT(mLockCount == 0, "Being destroyed with non-zero lock count?");
+
+ if (OnHeap()) {
+#ifdef MOZ_MEMORY
+ free(mBuf);
+#else
+ _aligned_free(mBuf);
+#endif
+ } else {
+ VirtualFreeEx(GetCurrentProcess(), mBuf, 0, MEM_RELEASE);
+ }
+}
+
+bool VolatileBuffer::Lock(void** aBuf) {
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(mBuf, "Attempting to lock an uninitialized VolatileBuffer");
+
+ *aBuf = mBuf;
+ if (++mLockCount > 1 || OnHeap()) {
+ return true;
+ }
+
+ // MEM_RESET_UNDO's behavior is undefined when called on memory that
+ // hasn't been MEM_RESET.
+ if (mFirstLock) {
+ mFirstLock = false;
+ return true;
+ }
+
+ void* addr = VirtualAllocEx(GetCurrentProcess(), mBuf, mSize, MEM_RESET_UNDO,
+ PAGE_READWRITE);
+ return !!addr;
+}
+
+void VolatileBuffer::Unlock() {
+ MutexAutoLock lock(mMutex);
+
+ MOZ_ASSERT(mLockCount > 0, "VolatileBuffer unlocked too many times!");
+ if (--mLockCount || OnHeap()) {
+ return;
+ }
+
+ DebugOnly<void*> addr = VirtualAllocEx(GetCurrentProcess(), mBuf, mSize,
+ MEM_RESET, PAGE_READWRITE);
+ MOZ_ASSERT(addr, "Failed to MEM_RESET");
+}
+
+bool VolatileBuffer::OnHeap() const { return mHeap; }
+
+size_t VolatileBuffer::HeapSizeOfExcludingThis(
+ MallocSizeOf aMallocSizeOf) const {
+ if (OnHeap()) {
+#ifdef MOZ_MEMORY
+ return aMallocSizeOf(mBuf);
+#else
+ return mSize;
+#endif
+ }
+
+ return 0;
+}
+
+size_t VolatileBuffer::NonHeapSizeOfExcludingThis() const {
+ if (OnHeap()) {
+ return 0;
+ }
+
+ return (mSize + 4095) & ~4095;
+}
+
+} // namespace mozilla
diff --git a/memory/volatile/moz.build b/memory/volatile/moz.build
new file mode 100644
index 0000000000..9cfe43e5d0
--- /dev/null
+++ b/memory/volatile/moz.build
@@ -0,0 +1,31 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+NoVisibilityFlags()
+
+EXPORTS.mozilla += [
+ "VolatileBuffer.h",
+]
+
+if CONFIG["OS_TARGET"] == "Android":
+ UNIFIED_SOURCES += [
+ "VolatileBufferAshmem.cpp",
+ ]
+elif CONFIG["OS_TARGET"] == "Darwin":
+ UNIFIED_SOURCES += [
+ "VolatileBufferOSX.cpp",
+ ]
+elif CONFIG["OS_TARGET"] == "WINNT":
+ UNIFIED_SOURCES += [
+ "VolatileBufferWindows.cpp",
+ ]
+else:
+ UNIFIED_SOURCES += [
+ "VolatileBufferFallback.cpp",
+ ]
+
+FINAL_LIBRARY = "xul"
+
+TEST_DIRS += ["tests"]
diff --git a/memory/volatile/tests/TestVolatileBuffer.cpp b/memory/volatile/tests/TestVolatileBuffer.cpp
new file mode 100644
index 0000000000..e9e9699ec6
--- /dev/null
+++ b/memory/volatile/tests/TestVolatileBuffer.cpp
@@ -0,0 +1,100 @@
+/* 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/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/VolatileBuffer.h"
+#include <string.h>
+
+#if defined(ANDROID)
+# include <fcntl.h>
+# include <linux/ashmem.h>
+# include <sys/ioctl.h>
+# include <sys/stat.h>
+# include <sys/types.h>
+#elif defined(XP_DARWIN)
+# include <mach/mach.h>
+#endif
+
+using namespace mozilla;
+
+TEST(VolatileBufferTest, HeapVolatileBuffersWork)
+{
+ RefPtr<VolatileBuffer> heapbuf = new VolatileBuffer();
+
+ ASSERT_TRUE(heapbuf)
+ << "Failed to create VolatileBuffer";
+ ASSERT_TRUE(heapbuf->Init(512))
+ << "Failed to initialize VolatileBuffer";
+
+ VolatileBufferPtr<char> ptr(heapbuf);
+
+ EXPECT_FALSE(ptr.WasBufferPurged())
+ << "Buffer should not be purged immediately after initialization";
+ EXPECT_TRUE(ptr) << "Couldn't get pointer from VolatileBufferPtr";
+}
+
+TEST(VolatileBufferTest, RealVolatileBuffersWork)
+{
+ RefPtr<VolatileBuffer> buf = new VolatileBuffer();
+
+ ASSERT_TRUE(buf)
+ << "Failed to create VolatileBuffer";
+ ASSERT_TRUE(buf->Init(16384))
+ << "Failed to initialize VolatileBuffer";
+
+ const char teststr[] = "foobar";
+
+ {
+ VolatileBufferPtr<char> ptr(buf);
+
+ EXPECT_FALSE(ptr.WasBufferPurged())
+ << "Buffer should not be purged immediately after initialization";
+ EXPECT_TRUE(ptr) << "Couldn't get pointer from VolatileBufferPtr";
+
+ {
+ VolatileBufferPtr<char> ptr2(buf);
+
+ EXPECT_FALSE(ptr.WasBufferPurged())
+ << "Failed to lock buffer again while currently locked";
+ ASSERT_TRUE(ptr2)
+ << "Didn't get a pointer on the second lock";
+
+ strcpy(ptr2, teststr);
+ }
+ }
+
+ {
+ VolatileBufferPtr<char> ptr(buf);
+
+ EXPECT_FALSE(ptr.WasBufferPurged())
+ << "Buffer was immediately purged after unlock";
+ EXPECT_STREQ(ptr, teststr) << "Buffer failed to retain data after unlock";
+ }
+
+ // Test purging if we know how to
+#if defined(XP_DARWIN)
+ int state;
+ vm_purgable_control(mach_task_self(), (vm_address_t)NULL,
+ VM_PURGABLE_PURGE_ALL, &state);
+#else
+ return;
+#endif
+
+ EXPECT_GT(buf->NonHeapSizeOfExcludingThis(), 0ul)
+ << "Buffer should not be allocated on heap";
+
+ {
+ VolatileBufferPtr<char> ptr(buf);
+
+ EXPECT_TRUE(ptr.WasBufferPurged())
+ << "Buffer should not be unpurged after forced purge";
+ EXPECT_STRNE(ptr, teststr) << "Purge did not actually purge data";
+ }
+
+ {
+ VolatileBufferPtr<char> ptr(buf);
+
+ EXPECT_FALSE(ptr.WasBufferPurged()) << "Buffer still purged after lock";
+ }
+}
diff --git a/memory/volatile/tests/moz.build b/memory/volatile/tests/moz.build
new file mode 100644
index 0000000000..31201db9d4
--- /dev/null
+++ b/memory/volatile/tests/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES = [
+ "TestVolatileBuffer.cpp",
+]
+
+FINAL_LIBRARY = "xul-gtest"