--- /dev/null
+// Copyright (c) 2009-2013 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "allocators.h"
+
+#ifdef WIN32
+#if (_WIN32_WINNT != _WIN32_WINNT_WIN7)
+#define _WIN32_WINNT 0x601
+#endif
+#define WIN32_LEAN_AND_MEAN 1
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <windows.h>
+// This is used to attempt to keep keying material out of swap
+// Note that VirtualLock does not provide this as a guarantee on Windows,
+// but, in practice, memory that has been VirtualLock'd almost never gets written to
+// the pagefile except in rare circumstances where memory is extremely low.
+#else
+#include <sys/mman.h>
+#include <climits> // for PAGESIZE
+#include <unistd.h> // for sysconf
+#endif
+
+LockedPageManager* LockedPageManager::_instance = nullptr;
+std::once_flag LockedPageManager::init_flag{};
+
+/** Determine system page size in bytes */
+static inline size_t GetSystemPageSize()
+{
+ size_t page_size;
+#if defined(WIN32)
+ SYSTEM_INFO sSysInfo;
+ GetSystemInfo(&sSysInfo);
+ page_size = sSysInfo.dwPageSize;
+#elif defined(PAGESIZE) // defined in limits.h
+ page_size = PAGESIZE;
+#else // assume some POSIX OS
+ page_size = sysconf(_SC_PAGESIZE);
+#endif
+ return page_size;
+}
+
+bool MemoryPageLocker::Lock(const void *addr, size_t len)
+{
+#ifdef WIN32
+ return VirtualLock(const_cast<void*>(addr), len) != 0;
+#else
+ return mlock(addr, len) == 0;
+#endif
+}
+
+bool MemoryPageLocker::Unlock(const void *addr, size_t len)
+{
+#ifdef WIN32
+ return VirtualUnlock(const_cast<void*>(addr), len) != 0;
+#else
+ return munlock(addr, len) == 0;
+#endif
+}
+
+LockedPageManager::LockedPageManager(): LockedPageManagerBase<MemoryPageLocker>(GetSystemPageSize())
+{
+}
#include <map>
#include <vector>
-#ifdef WIN32
-#if (_WIN32_WINNT != _WIN32_WINNT_WIN7)
-#define _WIN32_WINNT 0x601
-#endif
-#define WIN32_LEAN_AND_MEAN 1
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-#include <windows.h>
-// This is used to attempt to keep keying material out of swap
-// Note that VirtualLock does not provide this as a guarantee on Windows,
-// but, in practice, memory that has been VirtualLock'd almost never gets written to
-// the pagefile except in rare circumstances where memory is extremely low.
-#else
-#include <sys/mman.h>
-#include <climits> // for PAGESIZE
-#include <unistd.h> // for sysconf
-#endif
-
/**
* Thread-safe class to keep track of locked (ie, non-swappable) memory pages.
*
page_mask = ~(page_size - 1);
}
+ ~LockedPageManagerBase()
+ {
+ assert(this->GetLockedPageCount() == 0);
+ }
+
// For all pages in affected range, increase lock count
void LockRange(void *p, size_t size)
{
Histogram histogram;
};
-/** Determine system page size in bytes */
-static inline size_t GetSystemPageSize()
-{
- size_t page_size;
-#if defined(WIN32)
- SYSTEM_INFO sSysInfo;
- GetSystemInfo(&sSysInfo);
- page_size = sSysInfo.dwPageSize;
-#elif defined(PAGESIZE) // defined in limits.h
- page_size = PAGESIZE;
-#else // assume some POSIX OS
- page_size = sysconf(_SC_PAGESIZE);
-#endif
- return page_size;
-}
-
/**
* OS-dependent memory page locking/unlocking.
* Defined as policy class to make stubbing for test possible.
/** Lock memory pages.
* addr and len must be a multiple of the system page size
*/
- bool Lock(const void *addr, size_t len)
- {
-#ifdef WIN32
- return VirtualLock(const_cast<void*>(addr), len) != 0;
-#else
- return mlock(addr, len) == 0;
-#endif
- }
+ bool Lock(const void *addr, size_t len);
/** Unlock memory pages.
* addr and len must be a multiple of the system page size
*/
- bool Unlock(const void *addr, size_t len)
- {
-#ifdef WIN32
- return VirtualUnlock(const_cast<void*>(addr), len) != 0;
-#else
- return munlock(addr, len) == 0;
-#endif
- }
+ bool Unlock(const void *addr, size_t len);
};
/**
* Singleton class to keep track of locked (ie, non-swappable) memory pages, for use in
* std::allocator templates.
- */
+ *
+ * Some implementations of the STL allocate memory in some constructors (i.e., see
+ * MSVC's vector<T> implementation where it allocates 1 byte of memory in the allocator.)
+ * Due to the unpredictable order of static initializers, we have to make sure the
+ * LockedPageManager instance exists before any other STL-based objects that use
+ * secure_allocator are created. So instead of having LockedPageManager also be
+ * static-intialized, it is created on demand.
+*/
class LockedPageManager: public LockedPageManagerBase<MemoryPageLocker>
{
public:
- static LockedPageManager instance; // instantiated in util.cpp
+ static LockedPageManager& Instance()
+ {
+ std::call_once(LockedPageManager::init_flag, LockedPageManager::CreateInstance);
+ return *LockedPageManager::_instance;
+ }
private:
- LockedPageManager():
- LockedPageManagerBase<MemoryPageLocker>(GetSystemPageSize())
- {}
+ LockedPageManager();
+
+ static void CreateInstance()
+ {
+ // Using a local static instance guarantees that the object is initialized
+ // when it's first needed and also deinitialized after all objects that use
+ // it are done with it. I can think of one unlikely scenario where we may
+ // have a static deinitialization order/problem, but the check in
+ // LockedPageManagerBase's destructor helps us detect if that ever happens.
+ static LockedPageManager instance;
+ LockedPageManager::_instance = &instance;
+ }
+
+ static LockedPageManager* _instance;
+ static std::once_flag init_flag;
};
//
template<typename _Other> struct rebind
{ typedef secure_allocator<_Other> other; };
- T* allocate(std::size_t n, const void *hint = 0)
+ T* allocate(std::size_t n, const void *hint = nullptr)
{
T *p;
p = std::allocator<T>::allocate(n, hint);
- if (p != NULL)
- LockedPageManager::instance.LockRange(p, sizeof(T) * n);
+ if (p != nullptr)
+ LockedPageManager::Instance().LockRange(p, sizeof(T) * n);
return p;
}
void deallocate(T* p, std::size_t n)
{
- if (p != NULL)
+ if (p != nullptr)
{
OPENSSL_cleanse(p, sizeof(T) * n);
- LockedPageManager::instance.UnlockRange(p, sizeof(T) * n);
+ LockedPageManager::Instance().UnlockRange(p, sizeof(T) * n);
}
std::allocator<T>::deallocate(p, n);
}
void deallocate(T* p, std::size_t n)
{
- if (p != NULL)
+ if (p != nullptr)
OPENSSL_cleanse(p, sizeof(T) * n);
std::allocator<T>::deallocate(p, n);
}