提交 b186c277 编写于 作者: E Enrico Giordani

[Fix] FreeHeapBlock should check if the addr is in the redis heap.

Since the forked process allocates the memory from the system heap,
it must verify if the address is in the system heap or in the
redis heap before freeing it.
Changed dictRehash to NOOP when called by the forked process to avoid
extra processing that is not required when the forked process is
saving the dataset.
上级 596b71f4
...@@ -97,7 +97,6 @@ allocate a system paging file that will expand up to about (3.5 * physical). ...@@ -97,7 +97,6 @@ allocate a system paging file that will expand up to about (3.5 * physical).
#define QFORK_MAIN_IMPL #define QFORK_MAIN_IMPL
#include "Win32_QFork.h" #include "Win32_QFork.h"
#include "Win32_QFork_impl.h" #include "Win32_QFork_impl.h"
#include "Win32_SmartHandle.h" #include "Win32_SmartHandle.h"
#include "Win32_Service.h" #include "Win32_Service.h"
...@@ -204,6 +203,7 @@ struct heapBlockInfo { ...@@ -204,6 +203,7 @@ struct heapBlockInfo {
struct QForkControl { struct QForkControl {
LPVOID heapStart; LPVOID heapStart;
LPVOID heapEnd;
int maxAvailableBlocks; int maxAvailableBlocks;
int numMappedBlocks; int numMappedBlocks;
int blockSearchStart; int blockSearchStart;
...@@ -225,15 +225,16 @@ QForkControl* g_pQForkControl; ...@@ -225,15 +225,16 @@ QForkControl* g_pQForkControl;
HANDLE g_hQForkControlFileMap; HANDLE g_hQForkControlFileMap;
HANDLE g_hForkedProcess = 0; HANDLE g_hForkedProcess = 0;
int g_ChildExitCode = 0; // For child process int g_ChildExitCode = 0; // For child process
BOOL g_isForkedProcess;
BOOL g_SentinelMode; BOOL g_SentinelMode;
BOOL g_PersistenceDisabled;
/* The system heap is used instead of the system paging file heap if /* If g_IsForkedProcess || g_PersistenceDisabled || g_SentinelMode is true
* one of the following case is true: * memory is not allocated from the memory map heap, instead the system heap
* - Redis is running as a sentinel * is used */
* - the current instance is a forked (child) process BOOL g_BypassMemoryMapOnAlloc;
* - the persistence-available configuration flag value is 'no' */ /* g_HasMemoryMappedHeap is true if g_PersistenceDisabled and g_SentinelMode
BOOL g_UseSystemHeap; * are both false, so it is true for the parent process and the child process
* when persistence is available */
BOOL g_HasMemoryMappedHeap;
bool ReportSpecialSystemErrors(int error) { bool ReportSpecialSystemErrors(int error) {
switch (error) switch (error)
...@@ -453,7 +454,8 @@ BOOL QForkParentInit() { ...@@ -453,7 +454,8 @@ BOOL QForkParentInit() {
IFFAILTHROW(VirtualFree(pHigh, 0, MEM_RELEASE), "QForkMasterInit: VirtualFree failed."); IFFAILTHROW(VirtualFree(pHigh, 0, MEM_RELEASE), "QForkMasterInit: VirtualFree failed.");
// Need to adjust the heap start address to align on allocation granularity offset // Need to adjust the heap start address to align on allocation granularity offset
g_pQForkControl->heapStart = (LPVOID) (((uint64_t) pHigh + cAllocationGranularity) - ((uint64_t) pHigh % cAllocationGranularity)); g_pQForkControl->heapStart = (LPVOID) (((uintptr_t) pHigh + cAllocationGranularity) - ((uintptr_t) pHigh % cAllocationGranularity));
g_pQForkControl->heapEnd = (LPVOID) ((uintptr_t) g_pQForkControl->heapStart + (g_pQForkControl->maxAvailableBlocks + 1) * cAllocationGranularity);
// Reserve the heap memory that will be mapped on demand in AllocHeapBlock() // Reserve the heap memory that will be mapped on demand in AllocHeapBlock()
for (int i = 0; i < g_pQForkControl->maxAvailableBlocks; i++) { for (int i = 0; i < g_pQForkControl->maxAvailableBlocks; i++) {
...@@ -497,21 +499,6 @@ BOOL QForkParentInit() { ...@@ -497,21 +499,6 @@ BOOL QForkParentInit() {
} }
StartupStatus QForkStartup() { StartupStatus QForkStartup() {
HANDLE QForkControlMemoryMapHandle = NULL;
DWORD PPID = 0;
g_isForkedProcess = FALSE;
// TODO: consider moving the argument parsing into ParseCommandLineArguments()
// Child command line looks like: --QFork [QForkControlMemoryMap handle] [parent pid]
if (g_argMap.find(cQFork) != g_argMap.end()) {
g_isForkedProcess = TRUE;
char* endPtr;
QForkControlMemoryMapHandle = (HANDLE) strtoul(g_argMap[cQFork].at(0).at(0).c_str(), &endPtr, 10);
char* end = NULL;
PPID = strtoul(g_argMap[cQFork].at(0).at(1).c_str(), &end, 10);
}
PERFORMANCE_INFORMATION perfinfo; PERFORMANCE_INFORMATION perfinfo;
perfinfo.cb = sizeof(PERFORMANCE_INFORMATION); perfinfo.cb = sizeof(PERFORMANCE_INFORMATION);
if (FALSE == GetPerformanceInfo(&perfinfo, sizeof(PERFORMANCE_INFORMATION))) { if (FALSE == GetPerformanceInfo(&perfinfo, sizeof(PERFORMANCE_INFORMATION))) {
...@@ -521,8 +508,10 @@ StartupStatus QForkStartup() { ...@@ -521,8 +508,10 @@ StartupStatus QForkStartup() {
} }
Globals::pageSize = perfinfo.PageSize; Globals::pageSize = perfinfo.PageSize;
if (g_isForkedProcess) { if (g_IsForkedProcess) {
g_UseSystemHeap = TRUE; // Child command line looks like: --QFork [QForkControlMemoryMap handle] [parent pid]
HANDLE QForkControlMemoryMapHandle = (HANDLE) strtoul(g_argMap[cQFork].at(0).at(0).c_str(), NULL, 10);
DWORD PPID = strtoul(g_argMap[cQFork].at(0).at(1).c_str(), NULL, 10);
return QForkChildInit(QForkControlMemoryMapHandle, PPID) ? StartupStatus::ssCHILD_EXIT : StartupStatus::ssFAILED; return QForkChildInit(QForkControlMemoryMapHandle, PPID) ? StartupStatus::ssCHILD_EXIT : StartupStatus::ssFAILED;
} else { } else {
return QForkParentInit() ? StartupStatus::ssCONTINUE_AS_PARENT : StartupStatus::ssFAILED; return QForkParentInit() ? StartupStatus::ssCONTINUE_AS_PARENT : StartupStatus::ssFAILED;
...@@ -935,7 +924,7 @@ HANDLE CreateBlockMap(int blockIndex) { ...@@ -935,7 +924,7 @@ HANDLE CreateBlockMap(int blockIndex) {
#ifdef USE_DLMALLOC #ifdef USE_DLMALLOC
/* NOTE: The allocateHigh parameter is ignored in this implementation */ /* NOTE: The allocateHigh parameter is ignored in this implementation */
LPVOID AllocHeapBlock(size_t size, BOOL allocateHigh) { LPVOID AllocHeapBlock(size_t size, BOOL allocateHigh) {
if (g_UseSystemHeap) { if (g_BypassMemoryMapOnAlloc) {
return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
} }
...@@ -1001,7 +990,7 @@ LPVOID AllocHeapBlock(size_t size, BOOL allocateHigh) { ...@@ -1001,7 +990,7 @@ LPVOID AllocHeapBlock(size_t size, BOOL allocateHigh) {
#elif USE_JEMALLOC #elif USE_JEMALLOC
LPVOID AllocHeapBlock(LPVOID addr, size_t size, BOOL zero) { LPVOID AllocHeapBlock(LPVOID addr, size_t size, BOOL zero) {
if (g_UseSystemHeap) { if (g_BypassMemoryMapOnAlloc) {
return VirtualAlloc(addr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); return VirtualAlloc(addr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
} }
...@@ -1068,18 +1057,32 @@ LPVOID AllocHeapBlock(LPVOID addr, size_t size, BOOL zero) { ...@@ -1068,18 +1057,32 @@ LPVOID AllocHeapBlock(LPVOID addr, size_t size, BOOL zero) {
#endif #endif
BOOL FreeHeapBlock(LPVOID addr, size_t size) { BOOL FreeHeapBlock(LPVOID addr, size_t size) {
if (g_UseSystemHeap) {
return VirtualFree(addr, 0, MEM_RELEASE);
}
if (size == 0) { if (size == 0) {
return FALSE; return FALSE;
} }
// TODO: check addr is in heap // If g_HasMemoryMappedHeap is FALSE this can only be a system heap address
if (!g_HasMemoryMappedHeap) {
return VirtualFree(addr, 0, MEM_RELEASE);
}
// Check if the address belongs to the memory map heap or to the system heap
BOOL addressInRedisHeap = (addr >= g_pQForkControl->heapStart && addr < g_pQForkControl->heapEnd);
// g_BypassMemoryMapOnAlloc is true for the forked process, in this case
// we need to handle the address differently based on the heap that was
// used to allocate it.
if (g_BypassMemoryMapOnAlloc) {
if (!addressInRedisHeap) {
return VirtualFree(addr, 0, MEM_RELEASE);
} else {
redisLog(REDIS_DEBUG, "FreeHeapBlock: address in memory map heap 0x%p", addr);
}
}
// Check the address alignment and that belongs to the memory map heap
size_t ptrDiff = reinterpret_cast<byte*>(addr) - reinterpret_cast<byte*>(g_pQForkControl->heapStart); size_t ptrDiff = reinterpret_cast<byte*>(addr) - reinterpret_cast<byte*>(g_pQForkControl->heapStart);
if (ptrDiff < 0 || (ptrDiff % cAllocationGranularity) != 0) { if ((ptrDiff % cAllocationGranularity) != 0 || !addressInRedisHeap) {
return FALSE; return FALSE;
} }
...@@ -1104,11 +1107,8 @@ BOOL FreeHeapBlock(LPVOID addr, size_t size) { ...@@ -1104,11 +1107,8 @@ BOOL FreeHeapBlock(LPVOID addr, size_t size) {
} }
BOOL PurgePages(LPVOID addr, size_t length) { BOOL PurgePages(LPVOID addr, size_t length) {
if (g_UseSystemHeap) { // VirtualAlloc is called for all cases regardless the value of
VirtualAlloc(addr, length, MEM_RESET, PAGE_READWRITE); // g_BypassMemoryMapOnAlloc and g_HasMemoryMappedHeap
return TRUE;
}
VirtualAlloc(addr, length, MEM_RESET, PAGE_READWRITE); VirtualAlloc(addr, length, MEM_RESET, PAGE_READWRITE);
return TRUE; return TRUE;
} }
...@@ -1128,16 +1128,35 @@ void SetupLogging() { ...@@ -1128,16 +1128,35 @@ void SetupLogging() {
} }
} }
extern "C" BOOL IsPersistenceDisabled() {
{ if (g_argMap.find(cPersistenceAvailable) != g_argMap.end()) {
BOOL IsPersistenceAvailable() { return (g_argMap[cPersistenceAvailable].at(0).at(0) == cNo);
if (g_argMap.find(cPersistenceAvailable) != g_argMap.end()) { } else {
return (g_argMap[cPersistenceAvailable].at(0).at(0) != cNo); return FALSE;
} else { }
return TRUE; }
}
BOOL IsForkedProcess() {
if (g_argMap.find(cQFork) != g_argMap.end()) {
return TRUE;
} else {
return FALSE;
} }
}
void SetupQForkGlobals(int argc, char* argv[]) {
// To check sentinel mode we use the antirez code to avoid duplicating code
g_SentinelMode = checkForSentinelMode(argc, argv);
g_IsForkedProcess = IsForkedProcess();
g_PersistenceDisabled = IsPersistenceDisabled();
g_BypassMemoryMapOnAlloc = g_IsForkedProcess || g_PersistenceDisabled || g_SentinelMode;
g_HasMemoryMappedHeap = !g_PersistenceDisabled && !g_SentinelMode;
}
extern "C"
{
// The external main() is redefined as redis_main() by Win32_QFork.h. // The external main() is redefined as redis_main() by Win32_QFork.h.
// The CRT will call this replacement main() before the previous main() // The CRT will call this replacement main() before the previous main()
// is invoked so that the QFork allocator can be setup prior to anything // is invoked so that the QFork allocator can be setup prior to anything
...@@ -1146,6 +1165,7 @@ extern "C" ...@@ -1146,6 +1165,7 @@ extern "C"
try { try {
InitTimeFunctions(); InitTimeFunctions();
ParseCommandLineArguments(argc, argv); ParseCommandLineArguments(argc, argv);
SetupQForkGlobals(argc, argv);
SetupLogging(); SetupLogging();
StackTraceInit(); StackTraceInit();
InitThreadControl(); InitThreadControl();
...@@ -1194,13 +1214,6 @@ extern "C" ...@@ -1194,13 +1214,6 @@ extern "C"
return 0; return 0;
} }
BOOL persistenceAvailable = IsPersistenceAvailable();
g_SentinelMode = checkForSentinelMode(argc, argv);
if (g_SentinelMode == TRUE || persistenceAvailable == FALSE) {
g_UseSystemHeap = TRUE;
}
#ifdef USE_DLMALLOC #ifdef USE_DLMALLOC
DLMallocInit(); DLMallocInit();
// Setup memory allocation scheme // Setup memory allocation scheme
...@@ -1220,7 +1233,9 @@ extern "C" ...@@ -1220,7 +1233,9 @@ extern "C"
#elif USE_JEMALLOC #elif USE_JEMALLOC
je_init(); je_init();
#endif #endif
if (persistenceAvailable == TRUE && g_SentinelMode == FALSE) { if (g_PersistenceDisabled || g_SentinelMode) {
return redis_main(argc, argv);
} else {
StartupStatus status = QForkStartup(); StartupStatus status = QForkStartup();
if (status == ssCONTINUE_AS_PARENT) { if (status == ssCONTINUE_AS_PARENT) {
int retval = redis_main(argc, argv); int retval = redis_main(argc, argv);
...@@ -1237,8 +1252,6 @@ extern "C" ...@@ -1237,8 +1252,6 @@ extern "C"
// Unexpected status return // Unexpected status return
return 2; return 2;
} }
} else {
return redis_main(argc, argv);
} }
} }
catch (system_error syserr) { catch (system_error syserr) {
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
extern "C" { extern "C" {
#endif #endif
BOOL g_IsForkedProcess;
typedef enum operationType { typedef enum operationType {
otINVALID = 0, otINVALID = 0,
otRDB = 1, otRDB = 1,
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "Win32_Interop/Win32_Portability.h" #include "Win32_Interop/Win32_Portability.h"
#include "Win32_Interop/Win32_Time.h" #include "Win32_Interop/Win32_Time.h"
#include "Win32_Interop/win32fixes.h" #include "Win32_Interop/win32fixes.h"
extern BOOL g_IsForkedProcess;
#endif #endif
#include "fmacros.h" #include "fmacros.h"
...@@ -250,6 +251,11 @@ int dictExpand(dict *d,PORT_ULONG size) ...@@ -250,6 +251,11 @@ int dictExpand(dict *d,PORT_ULONG size)
* will visit at max N*10 empty buckets in total, otherwise the amount of * will visit at max N*10 empty buckets in total, otherwise the amount of
* work it does would be unbound and the function may block for a long time. */ * work it does would be unbound and the function may block for a long time. */
int dictRehash(dict *d, int n) { int dictRehash(dict *d, int n) {
// On Windows we choose not to execute the dict rehash since it's not
// necessary and it may have a performance impact.
WIN32_ONLY(if (g_IsForkedProcess) return 0;)
int empty_visits = n*10; /* Max number of empty buckets to visit. */ int empty_visits = n*10; /* Max number of empty buckets to visit. */
if (!dictIsRehashing(d)) return 0; if (!dictIsRehashing(d)) return 0;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册