redis-check-aof and redis-check-rdb tools
- removed separate project for redis-check-aof - both tools are now a copy of redis-server.exe - running given tool calls proper action and causes the application to exit - updated installer Fixes #2
This commit is contained in:
@@ -50,3 +50,4 @@ msvs/Documentation
|
||||
tests/tmp/
|
||||
|
||||
/msvs/.vs/RedisServer/v15
|
||||
/msvs/msi/.vs/RedisMsi/v15/Server/sqlite3
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}</ProjectGuid>
|
||||
<RootNamespace>RedisCheckAof</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<TargetName>redis-check-aof</TargetName>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<TargetName>redis-check-aof</TargetName>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<TargetName>redis-check-aof</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<TargetName>redis-check-aof</TargetName>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_OFF_T_DEFINED;WIN32;_DEBUG;_CONSOLE;LACKS_STDLIB_H;%(PreprocessorDefinitions);NO_QFORKIMPL;_WIN32_REDIS_CHECK_AOF_EXE</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\deps\lua\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)redis-check-aof$(TargetExt)</OutputFile>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<IgnoreSpecificDefaultLibraries>MSVCRT</IgnoreSpecificDefaultLibraries>
|
||||
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<EntryPointSymbol>
|
||||
</EntryPointSymbol>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_OFF_T_DEFINED;WIN32;_DEBUG;_CONSOLE;LACKS_STDLIB_H;%(PreprocessorDefinitions);NO_QFORKIMPL;_WIN32_WINNT=0x0501</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\deps\lua\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<OutputFile>$(OutDir)redis-check-aof$(TargetExt)</OutputFile>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<IgnoreSpecificDefaultLibraries>MSVCRT</IgnoreSpecificDefaultLibraries>
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>_OFF_T_DEFINED;WIN32;_CONSOLE;%(PreprocessorDefinitions);LACKS_STDLIB_H;NO_QFORKIMPL;_WIN32_REDIS_CHECK_AOF_EXE</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\deps\lua\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<OutputFile>$(OutDir)redis-check-aof$(TargetExt)</OutputFile>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>_OFF_T_DEFINED;WIN32;_CONSOLE;%(PreprocessorDefinitions);LACKS_STDLIB_H;NO_QFORKIMPL;_WIN32_WINNT=0x0501</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..\deps\lua\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<OutputFile>$(OutDir)redis-check-aof$(TargetExt)</OutputFile>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\src\redis-check-aof.c" />
|
||||
<ClCompile Include="..\..\src\zmalloc.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Win32_Interop\Win32_Interop.vcxproj">
|
||||
<Project>{8c07f811-c81c-432c-b334-1ae6faecf951}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\zmalloc.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -10,7 +10,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RedisServer", "RedisServer.
|
||||
{13E85053-54B3-487B-8DDB-3430B1C1B3BF} = {13E85053-54B3-487B-8DDB-3430B1C1B3BF}
|
||||
{392BBB91-3934-4A56-AF42-65C5728311E8} = {392BBB91-3934-4A56-AF42-65C5728311E8}
|
||||
{B00D4BB5-44DE-405E-839C-D16F547006CF} = {B00D4BB5-44DE-405E-839C-D16F547006CF}
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A} = {A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hiredis", "hiredis\hiredis.vcxproj", "{13E85053-54B3-487B-8DDB-3430B1C1B3BF}"
|
||||
@@ -20,8 +19,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RedisBenchmark", "RedisBenc
|
||||
{13E85053-54B3-487B-8DDB-3430B1C1B3BF} = {13E85053-54B3-487B-8DDB-3430B1C1B3BF}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RedisCheckAof", "RedisCheckAof\RedisCheckAof.vcxproj", "{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RedisCli", "RedisCli\RedisCli.vcxproj", "{392BBB91-3934-4A56-AF42-65C5728311E8}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{13E85053-54B3-487B-8DDB-3430B1C1B3BF} = {13E85053-54B3-487B-8DDB-3430B1C1B3BF}
|
||||
@@ -95,22 +92,6 @@ Global
|
||||
{B00D4BB5-44DE-405E-839C-D16F547006CF}.Release|x64.Build.0 = Release|x64
|
||||
{B00D4BB5-44DE-405E-839C-D16F547006CF}.Release|x86.ActiveCfg = Release|Win32
|
||||
{B00D4BB5-44DE-405E-839C-D16F547006CF}.Release|x86.Build.0 = Release|Win32
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Debug|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Debug|Mixed Platforms.Build.0 = Release|Win32
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Debug|x64.Build.0 = Debug|x64
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Debug|x86.Build.0 = Debug|Win32
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Release|Win32.Build.0 = Release|Win32
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Release|x64.ActiveCfg = Release|x64
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Release|x64.Build.0 = Release|x64
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Release|x86.ActiveCfg = Release|Win32
|
||||
{A65C2CD6-72A3-441A-AEA3-D754BEA9A86A}.Release|x86.Build.0 = Release|Win32
|
||||
{392BBB91-3934-4A56-AF42-65C5728311E8}.Debug|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{392BBB91-3934-4A56-AF42-65C5728311E8}.Debug|Mixed Platforms.Build.0 = Release|Win32
|
||||
{392BBB91-3934-4A56-AF42-65C5728311E8}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
|
||||
@@ -111,6 +111,12 @@
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
<IgnoreSpecificDefaultLibraries>MSVCRT.lib</IgnoreSpecificDefaultLibraries>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy /Y $(OutputPath)redis-server.exe $(OutputPath)redis-check-rdb.exe
|
||||
copy /Y $(OutputPath)redis-server.pdb $(OutputPath)redis-check-rdb.pdb
|
||||
copy /Y $(OutputPath)redis-server.exe $(OutputPath)redis-check-aof.exe
|
||||
copy /Y $(OutputPath)redis-server.pdb $(OutputPath)redis-check-aof.pdb</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
@@ -155,8 +161,10 @@
|
||||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>
|
||||
</Command>
|
||||
<Command>copy /Y $(OutputPath)redis-server.exe $(OutputPath)redis-check-rdb.exe
|
||||
copy /Y $(OutputPath)redis-server.pdb $(OutputPath)redis-check-rdb.pdb
|
||||
copy /Y $(OutputPath)redis-server.exe $(OutputPath)redis-check-aof.exe
|
||||
copy /Y $(OutputPath)redis-server.pdb $(OutputPath)redis-check-aof.pdb</Command>
|
||||
</PostBuildEvent>
|
||||
<CustomBuildStep>
|
||||
<Command>
|
||||
|
||||
@@ -1,152 +1,155 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Xml;
|
||||
using System.Reflection;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ReleasePackagingTool
|
||||
{
|
||||
class Program
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
static string rootPath;
|
||||
static string versionReplacementText = "CurrentRedisVersion";
|
||||
private static string rootPath;
|
||||
private static string versionReplacementText = "CurrentRedisVersion";
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
#endregion Private Fields
|
||||
|
||||
try
|
||||
{
|
||||
Program p = new Program();
|
||||
#region Private Methods
|
||||
|
||||
string assemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
rootPath = Path.GetFullPath(Path.Combine(assemblyDirectory, @"..\..\..\..\..\"));
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
Program p = new Program();
|
||||
|
||||
string version;
|
||||
version = p.GetRedisVersion();
|
||||
p.UpdateNuSpecFiles(version);
|
||||
p.BuildReleasePackage(version, "x64");
|
||||
|
||||
Console.Write("Release packaging complete.");
|
||||
Environment.ExitCode = 0;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error. Failed to finish release packaging.\n" + ex.ToString());
|
||||
Environment.ExitCode = -1;
|
||||
}
|
||||
}
|
||||
string assemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
rootPath = Path.GetFullPath(Path.Combine(assemblyDirectory, @"..\..\..\..\..\"));
|
||||
|
||||
string GetRedisVersion()
|
||||
{
|
||||
TextReader tr = File.OpenText(Path.Combine(rootPath, @"src\version.h"));
|
||||
string line = tr.ReadLine();
|
||||
int start = line.IndexOf('\"');
|
||||
int last = line.LastIndexOf('\"');
|
||||
return line.Substring(start + 1, last - start - 1);
|
||||
}
|
||||
string version;
|
||||
version = p.GetRedisVersion();
|
||||
p.UpdateNuSpecFiles(version);
|
||||
p.BuildReleasePackage(version, "x64");
|
||||
|
||||
void ForceFileErase(string file)
|
||||
{
|
||||
if (File.Exists(file))
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
Console.Write("Release packaging complete.");
|
||||
Environment.ExitCode = 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error. Failed to finish release packaging.\n" + ex.ToString());
|
||||
Environment.ExitCode = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void CreateTextFileFromTemplate(string templatePath, string documentPath, string toReplace, string replaceWith )
|
||||
{
|
||||
string replacedText;
|
||||
using (TextReader trTemplate = File.OpenText(templatePath) )
|
||||
{
|
||||
string templateText = trTemplate.ReadToEnd();
|
||||
replacedText = templateText.Replace(toReplace, replaceWith);
|
||||
}
|
||||
private void BuildReleasePackage(string version, string platform)
|
||||
{
|
||||
string releasePackageDir = Path.Combine(rootPath, @"msvs\BuildRelease\Redis-" + version + @"\");
|
||||
if (Directory.Exists(releasePackageDir) == false)
|
||||
{
|
||||
Directory.CreateDirectory(releasePackageDir);
|
||||
}
|
||||
|
||||
ForceFileErase(documentPath);
|
||||
string releasePackagePath = Path.Combine(rootPath, releasePackageDir + "Redis-" + platform + "-" + version + ".zip");
|
||||
ForceFileErase(releasePackagePath);
|
||||
|
||||
using (TextWriter twDoc = File.CreateText(documentPath))
|
||||
{
|
||||
twDoc.Write(replacedText);
|
||||
twDoc.Close();
|
||||
}
|
||||
}
|
||||
string executablesRoot = Path.Combine(rootPath, @"msvs\" + platform + @"\Release");
|
||||
List<Tuple<string /*source*/, string /*target name*/>> executableNames = new List<Tuple<string, string>>()
|
||||
{
|
||||
Tuple.Create("redis-benchmark.exe", "redis-benchmark.exe"),
|
||||
Tuple.Create("redis-server.exe", "redis-check-aof.exe"),
|
||||
Tuple.Create("redis-server.exe", "redis-check-rdb.exe"),
|
||||
Tuple.Create("redis-cli.exe", "redis-cli.exe"),
|
||||
Tuple.Create("redis-server.exe", "redis-server.exe"),
|
||||
};
|
||||
List<Tuple<string, string>> symbolNames = new List<Tuple<string, string>>()
|
||||
{
|
||||
Tuple.Create("redis-benchmark.pdb", "redis-benchmark.pdb"),
|
||||
Tuple.Create("redis-server.pdb", "redis-check-aof.pdb"),
|
||||
Tuple.Create("redis-server.pdb", "redis-check-rdb.pdb"),
|
||||
Tuple.Create("redis-cli.pdb","redis-cli.pdb"),
|
||||
Tuple.Create("redis-server.pdb", "redis-server.pdb"),
|
||||
};
|
||||
List<string> dependencyNames = new List<string>()
|
||||
{
|
||||
"EventLog.dll"
|
||||
};
|
||||
string documentsRoot = Path.Combine(rootPath, @"msvs\setups\documentation");
|
||||
List<string> docuementNames = new List<string>()
|
||||
{
|
||||
"Redis on Windows.docx",
|
||||
"Redis on Windows Release Notes.docx",
|
||||
"Windows Service Documentation.docx",
|
||||
"redis.windows.conf",
|
||||
"redis.windows-service.conf"
|
||||
};
|
||||
|
||||
void UpdateNuSpecFiles(string version)
|
||||
{
|
||||
string chocTemplate = Path.Combine(rootPath, @"msvs\setups\chocolatey\template\redis.nuspec.template");
|
||||
string chocDocument = Path.Combine(rootPath, @"msvs\setups\chocolatey\redis.nuspec");
|
||||
CreateTextFileFromTemplate(chocTemplate, chocDocument, versionReplacementText, version);
|
||||
using (ZipArchive archive = ZipFile.Open(releasePackagePath, ZipArchiveMode.Create))
|
||||
{
|
||||
foreach (var executableName in executableNames)
|
||||
{
|
||||
archive.CreateEntryFromFile(Path.Combine(executablesRoot, executableName.Item1), executableName.Item2);
|
||||
}
|
||||
foreach (var symbolName in symbolNames)
|
||||
{
|
||||
archive.CreateEntryFromFile(Path.Combine(executablesRoot, symbolName.Item1), symbolName.Item2);
|
||||
}
|
||||
foreach (string dependencyName in dependencyNames)
|
||||
{
|
||||
archive.CreateEntryFromFile(Path.Combine(executablesRoot, dependencyName), dependencyName);
|
||||
}
|
||||
foreach (string documentName in docuementNames)
|
||||
{
|
||||
archive.CreateEntryFromFile(Path.Combine(documentsRoot, documentName), documentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string nugetTemplate = Path.Combine(rootPath, @"msvs\setups\nuget\template\redis.nuspec.template");
|
||||
string nugetDocument = Path.Combine(rootPath, @"msvs\setups\nuget\redis.nuspec");
|
||||
CreateTextFileFromTemplate(nugetTemplate, nugetDocument, versionReplacementText, version);
|
||||
}
|
||||
private void CreateTextFileFromTemplate(string templatePath, string documentPath, string toReplace, string replaceWith)
|
||||
{
|
||||
string replacedText;
|
||||
using (TextReader trTemplate = File.OpenText(templatePath))
|
||||
{
|
||||
string templateText = trTemplate.ReadToEnd();
|
||||
replacedText = templateText.Replace(toReplace, replaceWith);
|
||||
}
|
||||
|
||||
void BuildReleasePackage(string version, string platform)
|
||||
{
|
||||
string releasePackageDir = Path.Combine(rootPath, @"msvs\BuildRelease\Redis-" + version + @"\");
|
||||
if (Directory.Exists(releasePackageDir) == false)
|
||||
{
|
||||
Directory.CreateDirectory(releasePackageDir);
|
||||
}
|
||||
ForceFileErase(documentPath);
|
||||
|
||||
string releasePackagePath = Path.Combine(rootPath, releasePackageDir + "Redis-" + platform + "-" + version + ".zip");
|
||||
ForceFileErase(releasePackagePath);
|
||||
using (TextWriter twDoc = File.CreateText(documentPath))
|
||||
{
|
||||
twDoc.Write(replacedText);
|
||||
twDoc.Close();
|
||||
}
|
||||
}
|
||||
|
||||
string executablesRoot = Path.Combine(rootPath, @"msvs\" + platform + @"\Release");
|
||||
List<string> executableNames = new List<string>()
|
||||
{
|
||||
"redis-benchmark.exe",
|
||||
"redis-check-aof.exe",
|
||||
"redis-cli.exe",
|
||||
"redis-server.exe"
|
||||
};
|
||||
List<string> symbolNames = new List<string>()
|
||||
{
|
||||
"redis-benchmark.pdb",
|
||||
"redis-check-aof.pdb",
|
||||
"redis-cli.pdb",
|
||||
"redis-server.pdb"
|
||||
};
|
||||
List<string> dependencyNames = new List<string>()
|
||||
{
|
||||
"EventLog.dll"
|
||||
};
|
||||
string documentsRoot = Path.Combine(rootPath, @"msvs\setups\documentation");
|
||||
List<string> docuementNames = new List<string>()
|
||||
{
|
||||
"Redis on Windows.docx",
|
||||
"Redis on Windows Release Notes.docx",
|
||||
"Windows Service Documentation.docx",
|
||||
"redis.windows.conf",
|
||||
"redis.windows-service.conf"
|
||||
};
|
||||
private void ForceFileErase(string file)
|
||||
{
|
||||
if (File.Exists(file))
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
|
||||
using (ZipArchive archive = ZipFile.Open(releasePackagePath, ZipArchiveMode.Create))
|
||||
{
|
||||
foreach (string executableName in executableNames)
|
||||
{
|
||||
archive.CreateEntryFromFile(Path.Combine(executablesRoot, executableName), executableName);
|
||||
}
|
||||
foreach (string symbolName in symbolNames)
|
||||
{
|
||||
archive.CreateEntryFromFile(Path.Combine(executablesRoot, symbolName), symbolName);
|
||||
}
|
||||
foreach (string dependencyName in dependencyNames)
|
||||
{
|
||||
archive.CreateEntryFromFile(Path.Combine(executablesRoot, dependencyName), dependencyName);
|
||||
}
|
||||
foreach (string documentName in docuementNames)
|
||||
{
|
||||
archive.CreateEntryFromFile(Path.Combine(documentsRoot, documentName), documentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
private string GetRedisVersion()
|
||||
{
|
||||
TextReader tr = File.OpenText(Path.Combine(rootPath, @"src\version.h"));
|
||||
string line = tr.ReadLine();
|
||||
int start = line.IndexOf('\"');
|
||||
int last = line.LastIndexOf('\"');
|
||||
return line.Substring(start + 1, last - start - 1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
private void UpdateNuSpecFiles(string version)
|
||||
{
|
||||
string chocTemplate = Path.Combine(rootPath, @"msvs\setups\chocolatey\template\redis.nuspec.template");
|
||||
string chocDocument = Path.Combine(rootPath, @"msvs\setups\chocolatey\redis.nuspec");
|
||||
CreateTextFileFromTemplate(chocTemplate, chocDocument, versionReplacementText, version);
|
||||
|
||||
string nugetTemplate = Path.Combine(rootPath, @"msvs\setups\nuget\template\redis.nuspec.template");
|
||||
string nugetDocument = Path.Combine(rootPath, @"msvs\setups\nuget\redis.nuspec");
|
||||
CreateTextFileFromTemplate(nugetTemplate, nugetDocument, versionReplacementText, version);
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,10 @@
|
||||
<File Source="$(var.BinDir)redis-check-aof.exe" />
|
||||
</Component>
|
||||
|
||||
<Component Id="cmp_redis_check_rdbEXE" Guid="*">
|
||||
<File Source="$(var.BinDir)redis-check-rdb.exe" />
|
||||
</Component>
|
||||
|
||||
<Component Id="cmp_redis_cliEXE" Guid="*">
|
||||
<File Source="$(var.BinDir)redis-cli.exe" />
|
||||
</Component>
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
<File Source="$(var.BinDir)redis-check-aof.pdb" />
|
||||
</Component>
|
||||
|
||||
<Component Id="cmp_redis_check_rdbPDB" Guid="*">
|
||||
<File Source="$(var.BinDir)redis-check-rdb.pdb" />
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
Binary file not shown.
@@ -21,7 +21,7 @@
|
||||
<Product Id="*"
|
||||
Name="Redis on Windows"
|
||||
Language="1033"
|
||||
Version="3.2.100"
|
||||
Version="4.0.2"
|
||||
Manufacturer="MSOpenTech"
|
||||
UpgradeCode="{05410198-7212-4FC4-B7C8-AFEFC3DA0FBC}">
|
||||
<Package InstallerVersion="200"
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
<!--Properties-->
|
||||
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
|
||||
<Property Id="ARPURLINFOABOUT" Value="https://github.com/MSOpenTech/redis" />
|
||||
<Property Id="ARPURLINFOABOUT" Value="https://github.com/tporadowski/redis" />
|
||||
<Property Id="PORT" Value="6379" />
|
||||
<Property Id="ADD_FIREWALL_RULE" Value="1" />
|
||||
<Property Id="FIREWALL_SERVICE_STOPPED" Value="0" />
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<metadata>
|
||||
<id>redis-64</id>
|
||||
<title>Redis 64-bit</title>
|
||||
<version>3.2.100</version>
|
||||
<version>4.0.2</version>
|
||||
<authors>Alexis Campailla, Enrico Giordani, Jonathan Pickett</authors>
|
||||
<owners>Microsoft Open Technologies, Inc.</owners>
|
||||
<description>A porting of Redis on Windows 64-bit.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<metadata>
|
||||
<id>redis-64</id>
|
||||
<title>Redis 64-bit</title>
|
||||
<version>3.2.100</version>
|
||||
<version>4.0.2</version>
|
||||
<authors>Alexis Campailla, Enrico Giordani, Jonathan Pickett</authors>
|
||||
<owners>Microsoft Open Technologies, Inc.</owners>
|
||||
<description>A porting of Redis on Windows 64-bit.
|
||||
|
||||
+263
-220
@@ -20,69 +20,69 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
Redis is an in memory DB. We need to share the redis database with a quasi-forked process so that we can do the RDB and AOF saves
|
||||
without halting the main redis process, or crashing due to code that was never designed to be thread safe. Essentially we need to
|
||||
replicate the COW behavior of fork() on Windows, but we don't actually need a complete fork() implementation. A complete fork()
|
||||
implementation would require subsystem level support to make happen. The following is required to make this quasi-fork scheme work:
|
||||
/*
|
||||
Redis is an in memory DB. We need to share the redis database with a quasi-forked process so that we can do the RDB and AOF saves
|
||||
without halting the main redis process, or crashing due to code that was never designed to be thread safe. Essentially we need to
|
||||
replicate the COW behavior of fork() on Windows, but we don't actually need a complete fork() implementation. A complete fork()
|
||||
implementation would require subsystem level support to make happen. The following is required to make this quasi-fork scheme work:
|
||||
|
||||
DLMalloc (http://g.oswego.edu/dl/html/malloc.html):
|
||||
- replaces malloc/realloc/free, either by manual patching of the zmalloc code in Redis or by patching the CRT routines at link time
|
||||
- partitions space into segments that it allocates from (currently configured as 64MB chunks)
|
||||
- we map/unmap these chunks as requested into a memory map (unmapping allows the system to decide how to reduce the physical memory
|
||||
pressure on system)
|
||||
DLMalloc (http://g.oswego.edu/dl/html/malloc.html):
|
||||
- replaces malloc/realloc/free, either by manual patching of the zmalloc code in Redis or by patching the CRT routines at link time
|
||||
- partitions space into segments that it allocates from (currently configured as 64MB chunks)
|
||||
- we map/unmap these chunks as requested into a memory map (unmapping allows the system to decide how to reduce the physical memory
|
||||
pressure on system)
|
||||
|
||||
DLMallocMemoryMap:
|
||||
- An uncomitted memory map whose size is the total physical memory on the system less some memory for the rest of the system so that
|
||||
we avoid excessive swapping.
|
||||
- This is reserved high in VM space so that it can be mapped at a specific address in the child qforked process (ASLR must be
|
||||
disabled for these processes)
|
||||
- This must be mapped in exactly the same virtual memory space in both forker and forkee.
|
||||
DLMallocMemoryMap:
|
||||
- An uncomitted memory map whose size is the total physical memory on the system less some memory for the rest of the system so that
|
||||
we avoid excessive swapping.
|
||||
- This is reserved high in VM space so that it can be mapped at a specific address in the child qforked process (ASLR must be
|
||||
disabled for these processes)
|
||||
- This must be mapped in exactly the same virtual memory space in both forker and forkee.
|
||||
|
||||
QForkControlMemoryMap:
|
||||
- contains a map of the allocated segments in the DLMallocMemoryMap
|
||||
- contains handles for inter-process synchronization
|
||||
- contains pointers to some of the global data in the parent process if mapped into DLMallocMemoryMap, and a copy of any other
|
||||
required global data
|
||||
QForkControlMemoryMap:
|
||||
- contains a map of the allocated segments in the DLMallocMemoryMap
|
||||
- contains handles for inter-process synchronization
|
||||
- contains pointers to some of the global data in the parent process if mapped into DLMallocMemoryMap, and a copy of any other
|
||||
required global data
|
||||
|
||||
QFork process:
|
||||
- a copy of the parent process with a command line specifying QFork behavior
|
||||
- when a COW operation is requested via an event signal
|
||||
- opens the DLMAllocMemoryMap with PAGE_WRITECOPY
|
||||
- reserve space for DLMAllocMemoryMap at the memory location specified in ControlMemoryMap
|
||||
- locks the DLMalloc segments as specified in QForkControlMemoryMap
|
||||
- maps global data from the QForkControlMEmoryMap into this process
|
||||
- executes the requested operation
|
||||
- unmaps all the mm views (discarding any writes)
|
||||
- signals the parent when the operation is complete
|
||||
QFork process:
|
||||
- a copy of the parent process with a command line specifying QFork behavior
|
||||
- when a COW operation is requested via an event signal
|
||||
- opens the DLMAllocMemoryMap with PAGE_WRITECOPY
|
||||
- reserve space for DLMAllocMemoryMap at the memory location specified in ControlMemoryMap
|
||||
- locks the DLMalloc segments as specified in QForkControlMemoryMap
|
||||
- maps global data from the QForkControlMEmoryMap into this process
|
||||
- executes the requested operation
|
||||
- unmaps all the mm views (discarding any writes)
|
||||
- signals the parent when the operation is complete
|
||||
|
||||
How the parent invokes the QFork process:
|
||||
- protects mapped memory segments with VirtualProtect using PAGE_WRITECOPY (both the allocated portions of DLMAllocMemoryMap and
|
||||
the QForkControlMemoryMap)
|
||||
- QForked process is signaled to process command
|
||||
- Parent waits (asynchronously) until QForked process signals that operation is complete, then as an atomic operation:
|
||||
- signals and waits for the forked process to terminate
|
||||
- resotres protection status on mapped blocks
|
||||
- determines which pages have been modified and copies these to a buffer
|
||||
- unmaps the view of the heap (discarding COW changes form the view)
|
||||
- remaps the view
|
||||
- copies the changes back into the view
|
||||
*/
|
||||
How the parent invokes the QFork process:
|
||||
- protects mapped memory segments with VirtualProtect using PAGE_WRITECOPY (both the allocated portions of DLMAllocMemoryMap and
|
||||
the QForkControlMemoryMap)
|
||||
- QForked process is signaled to process command
|
||||
- Parent waits (asynchronously) until QForked process signals that operation is complete, then as an atomic operation:
|
||||
- signals and waits for the forked process to terminate
|
||||
- resotres protection status on mapped blocks
|
||||
- determines which pages have been modified and copies these to a buffer
|
||||
- unmaps the view of the heap (discarding COW changes form the view)
|
||||
- remaps the view
|
||||
- copies the changes back into the view
|
||||
*/
|
||||
|
||||
/*
|
||||
Not specifying the maxmemory flag will result in the default behavior of: new key generation not bounded by heap usage,
|
||||
and the heap size equal to the size of physical memory.
|
||||
/*
|
||||
Not specifying the maxmemory flag will result in the default behavior of: new key generation not bounded by heap usage,
|
||||
and the heap size equal to the size of physical memory.
|
||||
|
||||
Redis will respect the maxmemory flag by preventing new key creation when the number of bytes allocated in the heap
|
||||
exceeds the level specified by the maxmemory flag. This does not account for heap fragmentation or memory usage by
|
||||
the heap allocator. To allow for this extra space we allow the heap to allocate 10 times the physical memory.
|
||||
Redis will respect the maxmemory flag by preventing new key creation when the number of bytes allocated in the heap
|
||||
exceeds the level specified by the maxmemory flag. This does not account for heap fragmentation or memory usage by
|
||||
the heap allocator. To allow for this extra space we allow the heap to allocate 10 times the physical memory.
|
||||
|
||||
Since the heap is entirely contained in the system paging file, the size of the system paging file needs to be large accordingly.
|
||||
Since the heap is entirely contained in the system paging file, the size of the system paging file needs to be large accordingly.
|
||||
|
||||
During forking the system paging file is used for managing virtual memory sharing and the copy on write pages for both
|
||||
forker and forkee. There must be sufficient system paging space availability for this. By default Windows will dynamically
|
||||
allocate a system paging file that will expand up to about (3.5 * physical).
|
||||
*/
|
||||
During forking the system paging file is used for managing virtual memory sharing and the copy on write pages for both
|
||||
forker and forkee. There must be sufficient system paging space availability for this. By default Windows will dynamically
|
||||
allocate a system paging file that will expand up to about (3.5 * physical).
|
||||
*/
|
||||
|
||||
#include "win32_types.h"
|
||||
#include "Win32_FDAPI.h"
|
||||
@@ -107,11 +107,14 @@ allocate a system paging file that will expand up to about (3.5 * physical).
|
||||
#include "Win32_EventLog.h"
|
||||
|
||||
#ifdef USE_DLMALLOC
|
||||
#include "Win32_dlmalloc.h"
|
||||
#include "Win32_dlmalloc.h"
|
||||
#elif USE_JEMALLOC
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
//#define DEBUG_WITH_PROCMON
|
||||
@@ -133,14 +136,15 @@ BOOL WriteToProcmon(wstring message)
|
||||
0,
|
||||
&nb,
|
||||
NULL);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef PAGE_REVERT_TO_FILE_MAP
|
||||
#define PAGE_REVERT_TO_FILE_MAP 0x80000000 // From Win8.1 SDK
|
||||
#define PAGE_REVERT_TO_FILE_MAP 0x80000000 // From Win8.1 SDK
|
||||
#endif
|
||||
|
||||
#define IFFAILTHROW(a,m) if(!(a)) { throw system_error(GetLastError(), system_category(), m); }
|
||||
@@ -177,19 +181,19 @@ extern "C"
|
||||
}
|
||||
|
||||
#ifdef USE_DLMALLOC
|
||||
const size_t cAllocationGranularity = 1 << 18; // 256KB per heap block (matches large block allocation threshold of dlmalloc)
|
||||
#ifdef _WIN64
|
||||
const int cMaxBlocks = 1 << 22; // 256KB * 4M heap blocks = 1TB
|
||||
#else
|
||||
const int cMaxBlocks = 1 << 12; // 256KB * 4K heap blocks = 1GB
|
||||
#endif
|
||||
const size_t cAllocationGranularity = 1 << 18; // 256KB per heap block (matches large block allocation threshold of dlmalloc)
|
||||
#ifdef _WIN64
|
||||
const int cMaxBlocks = 1 << 22; // 256KB * 4M heap blocks = 1TB
|
||||
#else
|
||||
const int cMaxBlocks = 1 << 12; // 256KB * 4K heap blocks = 1GB
|
||||
#endif
|
||||
#elif USE_JEMALLOC
|
||||
const size_t cAllocationGranularity = 1 << 22; // 4MB per heap block (matches the default allocation threshold of jemalloc)
|
||||
#ifdef _WIN64
|
||||
const int cMaxBlocks = 1 << 18; // 4MB * 256K heap blocks = 1TB
|
||||
#else
|
||||
const int cMaxBlocks = 1 << 8; // 4MB * 256 heap blocks = 1GB
|
||||
#endif
|
||||
const size_t cAllocationGranularity = 1 << 22; // 4MB per heap block (matches the default allocation threshold of jemalloc)
|
||||
#ifdef _WIN64
|
||||
const int cMaxBlocks = 1 << 18; // 4MB * 256K heap blocks = 1TB
|
||||
#else
|
||||
const int cMaxBlocks = 1 << 8; // 4MB * 256 heap blocks = 1GB
|
||||
#endif
|
||||
#endif
|
||||
|
||||
const int cDeadForkWait = 30000;
|
||||
@@ -235,30 +239,32 @@ BOOL g_BypassMemoryMapOnAlloc;
|
||||
* are both false, so it is true for the parent process and the child process
|
||||
* when persistence is available */
|
||||
BOOL g_HasMemoryMappedHeap;
|
||||
//[tporadowski/#2]
|
||||
BOOL g_StartedAsCheckAofOrRdbTool;
|
||||
|
||||
bool ReportSpecialSystemErrors(int error) {
|
||||
switch (error)
|
||||
{
|
||||
case ERROR_NO_SYSTEM_RESOURCES:
|
||||
case ERROR_COMMITMENT_LIMIT:
|
||||
{
|
||||
serverLog(
|
||||
LL_WARNING,
|
||||
"\n"
|
||||
"The Windows version of Redis reserves heap memory from the system paging file\n"
|
||||
"for sharing with the forked process used for persistence operations."
|
||||
"At this time there is insufficient contiguous free space available in the\n"
|
||||
"system paging file. You may increase the size of the system paging file.\n"
|
||||
"Sometimes a reboot will defragment the system paging file sufficiently for\n"
|
||||
"this operation to complete successfully.\n"
|
||||
"\n"
|
||||
"Redis can not continue. Exiting."
|
||||
);
|
||||
RedisEventLog().LogError("Failed to reserves heap memory from the system paging file.");
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
case ERROR_NO_SYSTEM_RESOURCES:
|
||||
case ERROR_COMMITMENT_LIMIT:
|
||||
{
|
||||
serverLog(
|
||||
LL_WARNING,
|
||||
"\n"
|
||||
"The Windows version of Redis reserves heap memory from the system paging file\n"
|
||||
"for sharing with the forked process used for persistence operations."
|
||||
"At this time there is insufficient contiguous free space available in the\n"
|
||||
"system paging file. You may increase the size of the system paging file.\n"
|
||||
"Sometimes a reboot will defragment the system paging file sufficiently for\n"
|
||||
"this operation to complete successfully.\n"
|
||||
"\n"
|
||||
"Redis can not continue. Exiting."
|
||||
);
|
||||
RedisEventLog().LogError("Failed to reserves heap memory from the system paging file.");
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,22 +287,22 @@ BOOL QForkChildInit(HANDLE QForkControlMemoryMapHandle, DWORD ParentProcessID) {
|
||||
SmartHandle dupOperationFailed;
|
||||
|
||||
try {
|
||||
shParent.Assign(
|
||||
shParent.Assign(
|
||||
OpenProcess(SYNCHRONIZE | PROCESS_DUP_HANDLE, TRUE, ParentProcessID),
|
||||
string("Could not open parent process"));
|
||||
|
||||
shQForkControlHeapMap.Assign(shParent, QForkControlMemoryMapHandle);
|
||||
sfvParentQForkControl.Assign(
|
||||
shQForkControlHeapMap,
|
||||
FILE_MAP_COPY,
|
||||
FILE_MAP_COPY,
|
||||
string("Could not map view of QForkControl in child. Is system swap file large enough?"));
|
||||
g_pQForkControl = sfvParentQForkControl;
|
||||
|
||||
// Duplicate handles and stuff into control structure (parent protected by PAGE_WRITECOPY)
|
||||
|
||||
|
||||
dupOperationComplete.Assign(shParent, sfvParentQForkControl->operationComplete);
|
||||
g_pQForkControl->operationComplete = dupOperationComplete;
|
||||
|
||||
|
||||
dupOperationFailed.Assign(shParent, sfvParentQForkControl->operationFailed);
|
||||
g_pQForkControl->operationFailed = dupOperationFailed;
|
||||
|
||||
@@ -314,7 +320,8 @@ BOOL QForkChildInit(HANDLE QForkControlMemoryMapHandle, DWORD ParentProcessID) {
|
||||
cAllocationGranularity,
|
||||
(byte*) g_pQForkControl->heapStart + i * cAllocationGranularity,
|
||||
string("QForkChildInit: could not map heap in forked process"));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
g_pQForkControl->heapBlockList[i].heapMap = NULL;
|
||||
g_pQForkControl->heapBlockList[i].state = BlockState::bsINVALID;
|
||||
}
|
||||
@@ -335,32 +342,34 @@ BOOL QForkChildInit(HANDLE QForkControlMemoryMapHandle, DWORD ParentProcessID) {
|
||||
// Execute requested operation
|
||||
if (g_pQForkControl->typeOfOperation == OperationType::otRDB) {
|
||||
g_ChildExitCode = do_rdbSave(g_pQForkControl->globalData.filename);
|
||||
} else if (g_pQForkControl->typeOfOperation == OperationType::otAOF) {
|
||||
}
|
||||
else if (g_pQForkControl->typeOfOperation == OperationType::otAOF) {
|
||||
int aof_pipe_read_ack = FDAPI_open_osfhandle((intptr_t) g_pQForkControl->globalData.aof_pipe_read_ack_handle, _O_APPEND);
|
||||
int aof_pipe_read_data = FDAPI_open_osfhandle((intptr_t) g_pQForkControl->globalData.aof_pipe_read_data_handle, _O_APPEND);
|
||||
int aof_pipe_write_ack = FDAPI_open_osfhandle((intptr_t) g_pQForkControl->globalData.aof_pipe_write_ack_handle, _O_APPEND);
|
||||
g_ChildExitCode = do_aofSave(g_pQForkControl->globalData.filename,
|
||||
aof_pipe_read_ack,
|
||||
aof_pipe_read_data,
|
||||
aof_pipe_write_ack
|
||||
);
|
||||
} else if (g_pQForkControl->typeOfOperation == OperationType::otSocket) {
|
||||
aof_pipe_read_ack,
|
||||
aof_pipe_read_data,
|
||||
aof_pipe_write_ack
|
||||
);
|
||||
}
|
||||
else if (g_pQForkControl->typeOfOperation == OperationType::otSocket) {
|
||||
LPWSAPROTOCOL_INFO lpProtocolInfo = (LPWSAPROTOCOL_INFO) g_pQForkControl->globalData.protocolInfo;
|
||||
int pipe_write_fd = FDAPI_open_osfhandle((intptr_t) g_pQForkControl->globalData.pipe_write_handle, _O_APPEND);
|
||||
int* fds = (int*) malloc(sizeof(int) * g_pQForkControl->globalData.numfds);
|
||||
for (int i = 0; i < g_pQForkControl->globalData.numfds; i++) {
|
||||
fds[i] = FDAPI_WSASocket(FROM_PROTOCOL_INFO,
|
||||
FROM_PROTOCOL_INFO,
|
||||
FROM_PROTOCOL_INFO,
|
||||
&lpProtocolInfo[i],
|
||||
0,
|
||||
WSA_FLAG_OVERLAPPED);
|
||||
FROM_PROTOCOL_INFO,
|
||||
FROM_PROTOCOL_INFO,
|
||||
&lpProtocolInfo[i],
|
||||
0,
|
||||
WSA_FLAG_OVERLAPPED);
|
||||
}
|
||||
|
||||
g_ChildExitCode = do_socketSave(fds,
|
||||
g_pQForkControl->globalData.numfds,
|
||||
g_pQForkControl->globalData.clientids,
|
||||
pipe_write_fd);
|
||||
g_pQForkControl->globalData.numfds,
|
||||
g_pQForkControl->globalData.clientids,
|
||||
pipe_write_fd);
|
||||
// After the socket replication has finished, close the duplicated sockets.
|
||||
// Failing to close the sockets properly will produce a socket read error
|
||||
// on both the parent process and the slave.
|
||||
@@ -368,7 +377,8 @@ BOOL QForkChildInit(HANDLE QForkControlMemoryMapHandle, DWORD ParentProcessID) {
|
||||
FDAPI_CloseDuplicatedSocket(fds[i]);
|
||||
}
|
||||
free(fds);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
throw runtime_error("unexpected operation type");
|
||||
}
|
||||
|
||||
@@ -378,17 +388,17 @@ BOOL QForkChildInit(HANDLE QForkControlMemoryMapHandle, DWORD ParentProcessID) {
|
||||
g_pQForkControl = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
catch(system_error syserr) {
|
||||
catch (system_error syserr) {
|
||||
if (ReportSpecialSystemErrors(syserr.code().value()) == false) {
|
||||
RedisEventLog().LogError("QForkChildInit: system error. " + string(syserr.what()));
|
||||
serverLog(LL_WARNING, "QForkChildInit: system error caught. error code=0x%08x, message=%s\n", syserr.code().value(), syserr.what());
|
||||
}
|
||||
}
|
||||
catch(runtime_error runerr) {
|
||||
catch (runtime_error runerr) {
|
||||
RedisEventLog().LogError("QForkChildInit: runtime error. " + string(runerr.what()));
|
||||
serverLog(LL_WARNING, "QForkChildInit: runtime error caught. message=%s\n", runerr.what());
|
||||
}
|
||||
|
||||
|
||||
if (g_pQForkControl != NULL) {
|
||||
if (g_pQForkControl->operationFailed != NULL) {
|
||||
SetEvent(g_pQForkControl->operationFailed);
|
||||
@@ -409,8 +419,8 @@ BOOL QForkParentInit() {
|
||||
NULL);
|
||||
IFFAILTHROW(g_hQForkControlFileMap, "QForkMasterInit: CreateFileMapping failed");
|
||||
|
||||
g_pQForkControl = (QForkControl*)MapViewOfFile(
|
||||
g_hQForkControlFileMap,
|
||||
g_pQForkControl = (QForkControl*) MapViewOfFile(
|
||||
g_hQForkControlFileMap,
|
||||
FILE_MAP_ALL_ACCESS,
|
||||
0, 0,
|
||||
0);
|
||||
@@ -438,7 +448,7 @@ BOOL QForkParentInit() {
|
||||
|
||||
// Find a place in the virtual memory space where we can reserve space for
|
||||
// our allocations that is likely to be available in the forked process.
|
||||
LPVOID pHigh = VirtualAllocEx(
|
||||
LPVOID pHigh = VirtualAllocEx(
|
||||
GetCurrentProcess(),
|
||||
NULL,
|
||||
// the +1 is needed since we will align the heap start address
|
||||
@@ -473,21 +483,21 @@ BOOL QForkParentInit() {
|
||||
}
|
||||
|
||||
g_pQForkControl->typeOfOperation = OperationType::otINVALID;
|
||||
g_pQForkControl->operationComplete = CreateEvent(NULL,TRUE,FALSE,NULL);
|
||||
g_pQForkControl->operationComplete = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
IFFAILTHROW(g_pQForkControl->operationComplete, "QForkMasterInit: CreateEvent failed.");
|
||||
|
||||
g_pQForkControl->operationFailed = CreateEvent(NULL,TRUE,FALSE,NULL);
|
||||
g_pQForkControl->operationFailed = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
IFFAILTHROW(g_pQForkControl->operationFailed, "QForkMasterInit: CreateEvent failed.");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
catch(system_error syserr) {
|
||||
catch (system_error syserr) {
|
||||
if (ReportSpecialSystemErrors(syserr.code().value()) == false) {
|
||||
RedisEventLog().LogError("QForkParentInit: system error. " + string(syserr.what()));
|
||||
serverLog(LL_WARNING, "QForkParentInit: system error caught. error code=0x%08x, message=%s\n", syserr.code().value(), syserr.what());
|
||||
}
|
||||
}
|
||||
catch(runtime_error runerr) {
|
||||
catch (runtime_error runerr) {
|
||||
RedisEventLog().LogError("QForkParentInit: runtime error. " + string(runerr.what()));
|
||||
serverLog(LL_WARNING, "QForkParentInit: runtime error caught. message=%s\n", runerr.what());
|
||||
}
|
||||
@@ -513,12 +523,13 @@ StartupStatus QForkStartup() {
|
||||
HANDLE QForkControlMemoryMapHandle = (HANDLE) strtoull(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;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return QForkParentInit() ? StartupStatus::ssCONTINUE_AS_PARENT : StartupStatus::ssFAILED;
|
||||
}
|
||||
}
|
||||
|
||||
void CloseEventHandle(HANDLE * phandle){
|
||||
void CloseEventHandle(HANDLE * phandle) {
|
||||
if (*phandle != NULL) {
|
||||
CloseHandle(*phandle);
|
||||
*phandle = NULL;
|
||||
@@ -552,7 +563,7 @@ BOOL QForkShutdown() {
|
||||
UnmapViewOfFile(g_pQForkControl);
|
||||
g_pQForkControl = NULL;
|
||||
}
|
||||
|
||||
|
||||
CloseEventHandle(&g_hQForkControlFileMap);
|
||||
}
|
||||
|
||||
@@ -574,7 +585,7 @@ void CopyForkOperationData(OperationType type, LPVOID redisData, int redisDataSi
|
||||
if (g_pQForkControl->DLMallocGlobalStateSize > sizeof(g_pQForkControl->DLMallocGlobalState)) {
|
||||
throw runtime_error("DLMalloc global state too large.");
|
||||
}
|
||||
if(GetDLMallocGlobalState(&g_pQForkControl->DLMallocGlobalStateSize, g_pQForkControl->DLMallocGlobalState) != 0) {
|
||||
if (GetDLMallocGlobalState(&g_pQForkControl->DLMallocGlobalStateSize, g_pQForkControl->DLMallocGlobalState) != 0) {
|
||||
throw runtime_error("DLMalloc global state copy failed.");
|
||||
}
|
||||
#endif
|
||||
@@ -582,16 +593,16 @@ void CopyForkOperationData(OperationType type, LPVOID redisData, int redisDataSi
|
||||
// Protect the qfork control map from propagating local changes
|
||||
DWORD oldProtect = 0;
|
||||
IFFAILTHROW(VirtualProtect(g_pQForkControl, sizeof(QForkControl), PAGE_WRITECOPY, &oldProtect),
|
||||
"CopyForkOperationData: VirtualProtect failed for QForkControl");
|
||||
"CopyForkOperationData: VirtualProtect failed for QForkControl");
|
||||
|
||||
// Protect the heap map from propagating local changes
|
||||
for (int i = 0; i < g_pQForkControl->numMappedBlocks; i++) {
|
||||
if (g_pQForkControl->heapBlockList[i].state == BlockState::bsMAPPED_IN_USE) {
|
||||
oldProtect = 0;
|
||||
VirtualProtect((byte*) g_pQForkControl->heapStart + i * cAllocationGranularity,
|
||||
cAllocationGranularity,
|
||||
PAGE_WRITECOPY,
|
||||
&oldProtect);
|
||||
cAllocationGranularity,
|
||||
PAGE_WRITECOPY,
|
||||
&oldProtect);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -599,14 +610,14 @@ void CopyForkOperationData(OperationType type, LPVOID redisData, int redisDataSi
|
||||
void CreateChildProcess(PROCESS_INFORMATION *pi, DWORD dwCreationFlags = 0) {
|
||||
// Ensure events are in the correst state
|
||||
IFFAILTHROW(ResetEvent(g_pQForkControl->operationComplete),
|
||||
"CreateChildProcess: ResetEvent() failed.");
|
||||
"CreateChildProcess: ResetEvent() failed.");
|
||||
IFFAILTHROW(ResetEvent(g_pQForkControl->operationFailed),
|
||||
"CreateChildProcess: ResetEvent() failed.");
|
||||
"CreateChildProcess: ResetEvent() failed.");
|
||||
|
||||
// Launch the "forked" process
|
||||
char fileName[MAX_PATH];
|
||||
IFFAILTHROW(GetModuleFileNameA(NULL, fileName, MAX_PATH),
|
||||
"Failed to get module name.");
|
||||
"Failed to get module name.");
|
||||
|
||||
STARTUPINFOA si;
|
||||
memset(&si, 0, sizeof(STARTUPINFOA));
|
||||
@@ -615,26 +626,26 @@ void CreateChildProcess(PROCESS_INFORMATION *pi, DWORD dwCreationFlags = 0) {
|
||||
memset(arguments, 0, _MAX_PATH);
|
||||
|
||||
sprintf_s(arguments,
|
||||
_MAX_PATH,
|
||||
"\"%s\" --%s %llu %lu --%s \"%s\"",
|
||||
fileName,
|
||||
cQFork.c_str(),
|
||||
(uint64_t) g_hQForkControlFileMap,
|
||||
GetCurrentProcessId(),
|
||||
cLogfile.c_str(),
|
||||
getLogFilename());
|
||||
_MAX_PATH,
|
||||
"\"%s\" --%s %llu %lu --%s \"%s\"",
|
||||
fileName,
|
||||
cQFork.c_str(),
|
||||
(uint64_t) g_hQForkControlFileMap,
|
||||
GetCurrentProcessId(),
|
||||
cLogfile.c_str(),
|
||||
getLogFilename());
|
||||
|
||||
IFFAILTHROW(CreateProcessA(fileName, arguments, NULL, NULL, TRUE, dwCreationFlags, NULL, NULL, &si, pi),
|
||||
"Problem creating slave process");
|
||||
"Problem creating slave process");
|
||||
g_hForkedProcess = pi->hProcess;
|
||||
}
|
||||
|
||||
typedef void (*CHILD_PID_HOOK)(DWORD pid);
|
||||
typedef void(*CHILD_PID_HOOK)(DWORD pid);
|
||||
|
||||
pid_t BeginForkOperation(OperationType type,
|
||||
LPVOID redisData,
|
||||
int redisDataSize,
|
||||
uint8_t *dictHashSeed)
|
||||
LPVOID redisData,
|
||||
int redisDataSize,
|
||||
uint8_t *dictHashSeed)
|
||||
{
|
||||
PROCESS_INFORMATION pi;
|
||||
try {
|
||||
@@ -646,7 +657,8 @@ pid_t BeginForkOperation(OperationType type,
|
||||
BeginForkOperation_Socket_Duplicate(pi.dwProcessId);
|
||||
CopyForkOperationData(type, redisData, redisDataSize, dictHashSeed);
|
||||
ResumeThread(pi.hThread);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
CopyForkOperationData(type, redisData, redisDataSize, dictHashSeed);
|
||||
CreateChildProcess(&pi, 0);
|
||||
}
|
||||
@@ -655,13 +667,13 @@ pid_t BeginForkOperation(OperationType type,
|
||||
|
||||
return pi.dwProcessId;
|
||||
}
|
||||
catch(system_error syserr) {
|
||||
catch (system_error syserr) {
|
||||
serverLog(LL_WARNING, "BeginForkOperation: system error caught. error code=0x%08x, message=%s\n", syserr.code().value(), syserr.what());
|
||||
}
|
||||
catch(runtime_error runerr) {
|
||||
catch (runtime_error runerr) {
|
||||
serverLog(LL_WARNING, "BeginForkOperation: runtime error caught. message=%s\n", runerr.what());
|
||||
}
|
||||
catch(...) {
|
||||
catch (...) {
|
||||
serverLog(LL_WARNING, "BeginForkOperation: other exception caught.\n");
|
||||
}
|
||||
if (pi.hProcess != INVALID_HANDLE_VALUE) {
|
||||
@@ -671,24 +683,24 @@ pid_t BeginForkOperation(OperationType type,
|
||||
}
|
||||
|
||||
pid_t BeginForkOperation_Rdb(char *filename,
|
||||
LPVOID redisData,
|
||||
int redisDataSize,
|
||||
uint8_t *dictHashSeed)
|
||||
LPVOID redisData,
|
||||
int redisDataSize,
|
||||
uint8_t *dictHashSeed)
|
||||
{
|
||||
strcpy_s(g_pQForkControl->globalData.filename, filename);
|
||||
return BeginForkOperation(otRDB, redisData, redisDataSize, dictHashSeed);
|
||||
}
|
||||
|
||||
pid_t BeginForkOperation_Aof(int aof_pipe_write_ack_to_parent,
|
||||
int aof_pipe_read_ack_from_parent,
|
||||
int aof_pipe_read_data_from_parent,
|
||||
char *filename,
|
||||
LPVOID redisData,
|
||||
int redisDataSize,
|
||||
uint8_t *dictHashSeed)
|
||||
int aof_pipe_read_ack_from_parent,
|
||||
int aof_pipe_read_data_from_parent,
|
||||
char *filename,
|
||||
LPVOID redisData,
|
||||
int redisDataSize,
|
||||
uint8_t *dictHashSeed)
|
||||
{
|
||||
HANDLE aof_pipe_write_ack_handle = (HANDLE) FDAPI_get_osfhandle(aof_pipe_write_ack_to_parent);
|
||||
HANDLE aof_pipe_read_ack_handle = (HANDLE) FDAPI_get_osfhandle(aof_pipe_read_ack_from_parent);
|
||||
HANDLE aof_pipe_read_ack_handle = (HANDLE) FDAPI_get_osfhandle(aof_pipe_read_ack_from_parent);
|
||||
HANDLE aof_pipe_read_data_handle = (HANDLE) FDAPI_get_osfhandle(aof_pipe_read_data_from_parent);
|
||||
|
||||
// The handle is already inheritable so there is no need to duplicate it
|
||||
@@ -702,25 +714,25 @@ pid_t BeginForkOperation_Aof(int aof_pipe_write_ack_to_parent,
|
||||
|
||||
void BeginForkOperation_Socket_Duplicate(DWORD dwProcessId) {
|
||||
#ifdef USE_DLMALLOC
|
||||
WSAPROTOCOL_INFO* protocolInfo = (WSAPROTOCOL_INFO*)dlmalloc(sizeof(WSAPROTOCOL_INFO) * g_pQForkControl->globalData.numfds);
|
||||
WSAPROTOCOL_INFO* protocolInfo = (WSAPROTOCOL_INFO*) dlmalloc(sizeof(WSAPROTOCOL_INFO) * g_pQForkControl->globalData.numfds);
|
||||
#elif USE_JEMALLOC
|
||||
WSAPROTOCOL_INFO* protocolInfo = (WSAPROTOCOL_INFO*) je_malloc(sizeof(WSAPROTOCOL_INFO) * g_pQForkControl->globalData.numfds);
|
||||
#endif
|
||||
g_pQForkControl->globalData.protocolInfo = protocolInfo;
|
||||
for(int i = 0; i < g_pQForkControl->globalData.numfds; i++) {
|
||||
for (int i = 0; i < g_pQForkControl->globalData.numfds; i++) {
|
||||
FDAPI_WSADuplicateSocket(g_pQForkControl->globalData.fds[i],
|
||||
dwProcessId,
|
||||
&protocolInfo[i]);
|
||||
dwProcessId,
|
||||
&protocolInfo[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pid_t BeginForkOperation_Socket(int *fds,
|
||||
int numfds,
|
||||
uint64_t *clientids,
|
||||
int pipe_write_fd,
|
||||
LPVOID redisData,
|
||||
int redisDataSize,
|
||||
uint8_t *dictHashSeed)
|
||||
int numfds,
|
||||
uint64_t *clientids,
|
||||
int pipe_write_fd,
|
||||
LPVOID redisData,
|
||||
int redisDataSize,
|
||||
uint8_t *dictHashSeed)
|
||||
{
|
||||
g_pQForkControl->globalData.fds = fds;
|
||||
g_pQForkControl->globalData.numfds = numfds;
|
||||
@@ -754,32 +766,33 @@ OperationStatus GetForkOperationStatus() {
|
||||
SetEvent(g_pQForkControl->operationFailed);
|
||||
}
|
||||
return OperationStatus::osFAILED;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return OperationStatus::osINPROGRESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return OperationStatus::osUNSTARTED;
|
||||
}
|
||||
|
||||
BOOL AbortForkOperation() {
|
||||
try {
|
||||
if( g_hForkedProcess != 0 )
|
||||
if (g_hForkedProcess != 0)
|
||||
{
|
||||
IFFAILTHROW(TerminateProcess(g_hForkedProcess, 1),
|
||||
"EndForkOperation: Killing forked process failed.");
|
||||
"EndForkOperation: Killing forked process failed.");
|
||||
CloseHandle(g_hForkedProcess);
|
||||
g_hForkedProcess = 0;
|
||||
}
|
||||
|
||||
return EndForkOperation(NULL);
|
||||
}
|
||||
catch(system_error syserr) {
|
||||
catch (system_error syserr) {
|
||||
serverLog(LL_WARNING, "AbortForkOperation: 0x%08x - %s\n", syserr.code().value(), syserr.what());
|
||||
// If we can not properly restore fork state, then another fork operation is not possible.
|
||||
// If we can not properly restore fork state, then another fork operation is not possible.
|
||||
exit(1);
|
||||
}
|
||||
catch(exception ex) {
|
||||
catch (exception ex) {
|
||||
serverLog(LL_WARNING, "AbortForkOperation: %s\n", ex.what());
|
||||
exit(1);
|
||||
}
|
||||
@@ -788,15 +801,15 @@ BOOL AbortForkOperation() {
|
||||
|
||||
void RejoinCOWPages(HANDLE mmHandle, byte* mmStart, size_t mmSize) {
|
||||
SmartFileView<byte> copyView(mmHandle, FILE_MAP_WRITE, 0, 0, mmSize,
|
||||
string("RejoinCOWPages: Could not map COW back-copy view."));
|
||||
string("RejoinCOWPages: Could not map COW back-copy view."));
|
||||
|
||||
for (byte* mmAddress = mmStart; mmAddress < mmStart + mmSize; ) {
|
||||
MEMORY_BASIC_INFORMATION memInfo;
|
||||
|
||||
IFFAILTHROW(VirtualQuery(mmAddress, &memInfo, sizeof(memInfo)),
|
||||
"RejoinCOWPages: VirtualQuery failure");
|
||||
"RejoinCOWPages: VirtualQuery failure");
|
||||
|
||||
byte* regionEnd = (byte*)memInfo.BaseAddress + memInfo.RegionSize;
|
||||
byte* regionEnd = (byte*) memInfo.BaseAddress + memInfo.RegionSize;
|
||||
|
||||
if (memInfo.Protect != PAGE_WRITECOPY) {
|
||||
// Copy back only the pages that have been copied on write
|
||||
@@ -808,15 +821,16 @@ void RejoinCOWPages(HANDLE mmHandle, byte* mmStart, size_t mmSize) {
|
||||
|
||||
// If the COWs are not discarded, then there is no way of propagating
|
||||
// changes into subsequent fork operations.
|
||||
#if FALSE
|
||||
#if FALSE
|
||||
// This works when using a memory mapped file but it fails when using
|
||||
// the system paging file.
|
||||
if (WindowsVersion::getInstance().IsAtLeast_6_2()) {
|
||||
// Restores all page protections on the view and culls the COW pages.
|
||||
DWORD oldProtect;
|
||||
IFFAILTHROW(VirtualProtect(mmStart, mmSize, PAGE_READWRITE | PAGE_REVERT_TO_FILE_MAP, &oldProtect),
|
||||
"RejoinCOWPages: COW cull failed");
|
||||
} else
|
||||
"RejoinCOWPages: COW cull failed");
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// Prior to Win8 unmapping the view was the only way to discard the
|
||||
@@ -830,7 +844,7 @@ void RejoinCOWPages(HANDLE mmHandle, byte* mmStart, size_t mmSize) {
|
||||
// problem and exit. This problem does not exist with the code above
|
||||
// in Win8+ as the view is never unmapped.
|
||||
IFFAILTHROW(MapViewOfFileEx(mmHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0, mmStart),
|
||||
"RejoinCOWPages: MapViewOfFileEx failed.");
|
||||
"RejoinCOWPages: MapViewOfFileEx failed.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -839,7 +853,7 @@ BOOL EndForkOperation(int * pExitCode) {
|
||||
if (g_hForkedProcess != 0) {
|
||||
if (WaitForSingleObject(g_hForkedProcess, cDeadForkWait) == WAIT_TIMEOUT) {
|
||||
IFFAILTHROW(TerminateProcess(g_hForkedProcess, 1),
|
||||
"EndForkOperation: Killing forked process failed.");
|
||||
"EndForkOperation: Killing forked process failed.");
|
||||
}
|
||||
|
||||
if (pExitCode != NULL) {
|
||||
@@ -851,9 +865,9 @@ BOOL EndForkOperation(int * pExitCode) {
|
||||
}
|
||||
|
||||
IFFAILTHROW(ResetEvent(g_pQForkControl->operationComplete),
|
||||
"EndForkOperation: ResetEvent() failed.");
|
||||
"EndForkOperation: ResetEvent() failed.");
|
||||
IFFAILTHROW(ResetEvent(g_pQForkControl->operationFailed),
|
||||
"EndForkOperation: ResetEvent() failed.");
|
||||
"EndForkOperation: ResetEvent() failed.");
|
||||
|
||||
// Move the heap local changes back into memory mapped views for next fork operation
|
||||
for (int i = 0; i < g_pQForkControl->numMappedBlocks; i++) {
|
||||
@@ -871,7 +885,7 @@ BOOL EndForkOperation(int * pExitCode) {
|
||||
catch (system_error syserr) {
|
||||
serverLog(LL_WARNING, "EndForkOperation: 0x%08x - %s\n", syserr.code().value(), syserr.what());
|
||||
|
||||
// If we can not properly restore fork state, then another fork operation is not possible.
|
||||
// If we can not properly restore fork state, then another fork operation is not possible.
|
||||
exit(1);
|
||||
}
|
||||
catch (exception ex) {
|
||||
@@ -886,25 +900,25 @@ HANDLE CreateBlockMap(int blockIndex) {
|
||||
// cAllocationGranularity is guaranteed to be < 2^31
|
||||
ASSERT(cAllocationGranularity < (1 << 31));
|
||||
HANDLE map = CreateFileMappingW(INVALID_HANDLE_VALUE,
|
||||
NULL,
|
||||
PAGE_READWRITE,
|
||||
0,
|
||||
cAllocationGranularity,
|
||||
NULL);
|
||||
NULL,
|
||||
PAGE_READWRITE,
|
||||
0,
|
||||
cAllocationGranularity,
|
||||
NULL);
|
||||
IFFAILTHROW(map, "PhysicalMapMemory: CreateFileMapping failed");
|
||||
|
||||
LPVOID addr = (byte*) g_pQForkControl->heapStart + blockIndex * cAllocationGranularity;
|
||||
|
||||
// Free the memory that was reserved in QForkParentInit() before mapping it
|
||||
IFFAILTHROW(VirtualFree(addr, 0, MEM_RELEASE),
|
||||
"PhysicalMapMemory: VirtualFree failed");
|
||||
"PhysicalMapMemory: VirtualFree failed");
|
||||
|
||||
LPVOID realAddr = MapViewOfFileEx(map, FILE_MAP_ALL_ACCESS, 0, 0, 0, addr);
|
||||
IFFAILTHROW(realAddr, "PhysicalMapMemory: MapViewOfFileEx failed");
|
||||
|
||||
DWORD old;
|
||||
IFFAILTHROW(VirtualProtect(realAddr, cAllocationGranularity, PAGE_READWRITE, &old),
|
||||
"PhysicalMapMemory: VirtualProtect failed");
|
||||
"PhysicalMapMemory: VirtualProtect failed");
|
||||
|
||||
return map;
|
||||
}
|
||||
@@ -945,7 +959,8 @@ LPVOID AllocHeapBlock(size_t size, BOOL allocateHigh) {
|
||||
if (g_pQForkControl->heapBlockList[startIdx + i].state == BlockState::bsUNMAPPED ||
|
||||
g_pQForkControl->heapBlockList[startIdx + i].state == BlockState::bsMAPPED_FREE) {
|
||||
contiguousBlocksFound++;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
contiguousBlocksFound = 0;
|
||||
startIdx += i; // restart searching from there
|
||||
break;
|
||||
@@ -969,7 +984,8 @@ LPVOID AllocHeapBlock(size_t size, BOOL allocateHigh) {
|
||||
if (g_pQForkControl->heapBlockList[index].state == BlockState::bsUNMAPPED) {
|
||||
g_pQForkControl->heapBlockList[index].heapMap = CreateBlockMap(index);
|
||||
g_pQForkControl->numMappedBlocks += 1;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// The current block state is bsMAPPED_FREE, therefore it needs to be
|
||||
// zeroed (bsUNMAPPED blocks don't need to be zeroed since newly mapped
|
||||
// blocked have zeroed memory by default)
|
||||
@@ -1011,7 +1027,8 @@ LPVOID AllocHeapBlock(LPVOID addr, size_t size, BOOL zero) {
|
||||
if (g_pQForkControl->heapBlockList[startIdx + i].state == BlockState::bsUNMAPPED ||
|
||||
g_pQForkControl->heapBlockList[startIdx + i].state == BlockState::bsMAPPED_FREE) {
|
||||
contiguousBlocksFound++;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
contiguousBlocksFound = 0;
|
||||
startIdx += i; // restart searching from there
|
||||
break;
|
||||
@@ -1035,7 +1052,8 @@ LPVOID AllocHeapBlock(LPVOID addr, size_t size, BOOL zero) {
|
||||
if (g_pQForkControl->heapBlockList[index].state == BlockState::bsUNMAPPED) {
|
||||
g_pQForkControl->heapBlockList[index].heapMap = CreateBlockMap(index);
|
||||
g_pQForkControl->numMappedBlocks += 1;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// The current block state is bsMAPPED_FREE, therefore it needs to be
|
||||
// zeroed (bsUNMAPPED blocks don't need to be zeroed since newly mapped
|
||||
// blocked have zeroed memory by default)
|
||||
@@ -1070,12 +1088,13 @@ BOOL FreeHeapBlock(LPVOID addr, size_t size) {
|
||||
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
|
||||
// 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 {
|
||||
}
|
||||
else {
|
||||
serverLog(LL_DEBUG, "FreeHeapBlock: address in memory map heap 0x%p", addr);
|
||||
}
|
||||
}
|
||||
@@ -1123,7 +1142,8 @@ void SetupLogging() {
|
||||
RedisEventLog().EnableEventLog(syslogEnabled);
|
||||
if (syslogEnabled) {
|
||||
RedisEventLog().SetEventLogIdentity(syslogIdent.c_str());
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
setLogFile(logFileName.c_str());
|
||||
}
|
||||
}
|
||||
@@ -1131,7 +1151,8 @@ void SetupLogging() {
|
||||
BOOL IsPersistenceDisabled() {
|
||||
if (g_argMap.find(cPersistenceAvailable) != g_argMap.end()) {
|
||||
return (g_argMap[cPersistenceAvailable].at(0).at(0) == cNo);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
@@ -1139,7 +1160,8 @@ BOOL IsPersistenceDisabled() {
|
||||
BOOL IsForkedProcess() {
|
||||
if (g_argMap.find(cQFork) != g_argMap.end()) {
|
||||
return TRUE;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
@@ -1158,17 +1180,34 @@ void SetupQForkGlobals(int argc, char* argv[]) {
|
||||
extern "C"
|
||||
{
|
||||
#include "Win32_PThread.h"
|
||||
extern pthread_mutex_t used_memory_mutex;
|
||||
extern pthread_mutex_t used_memory_mutex;
|
||||
|
||||
// The external main() is redefined as redis_main() by Win32_QFork.h.
|
||||
// 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
|
||||
// Redis will allocate.
|
||||
int main(int argc, char* argv[]) {
|
||||
try {
|
||||
pthread_mutex_init(&used_memory_mutex, NULL);
|
||||
|
||||
//[tporadowski/#2] check if started as "redis-check-rdb" tool
|
||||
string executable(argv[0]);
|
||||
transform(executable.begin(), executable.end(), executable.begin(), ::tolower);
|
||||
//copy lowercase back to argv[0] as later on main function from server.c checks for "redis-check-aof"
|
||||
// or "redis-check-rdb" (using case-sensitive checking)
|
||||
strncpy(argv[0], executable.c_str(), executable.length());
|
||||
|
||||
g_StartedAsCheckAofOrRdbTool = (executable.find("redis-check-rdb") != string::npos)
|
||||
|| (executable.find("redis-check-aof") != string::npos);
|
||||
|
||||
InitTimeFunctions();
|
||||
ParseCommandLineArguments(argc, argv);
|
||||
|
||||
//[tporadowski/#2] when running as "redis-check-rdb"/"redis-check-aof" expected command-line parameter is path to *.rdb/*.aof?
|
||||
// file and not a config file as for "redis-server", so skip this step
|
||||
if (!g_StartedAsCheckAofOrRdbTool) {
|
||||
ParseCommandLineArguments(argc, argv);
|
||||
}
|
||||
|
||||
SetupQForkGlobals(argc, argv);
|
||||
SetupLogging();
|
||||
StackTraceInit();
|
||||
@@ -1200,17 +1239,16 @@ extern pthread_mutex_t used_memory_mutex;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
#ifdef DEBUG_WITH_PROCMON
|
||||
hProcMonDevice =
|
||||
CreateFile(
|
||||
L"\\\\.\\Global\\ProcmonDebugLogger",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
L"\\\\.\\Global\\ProcmonDebugLogger",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
#endif
|
||||
|
||||
// Service commands do not launch an instance of redis directly
|
||||
@@ -1227,7 +1265,8 @@ extern pthread_mutex_t used_memory_mutex;
|
||||
g_realloc = dlrealloc;
|
||||
g_free = dlfree;
|
||||
g_msize = reinterpret_cast<size_t(*)(void*)>(dlmalloc_usable_size);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
g_malloc = malloc;
|
||||
g_calloc = calloc;
|
||||
g_realloc = realloc;
|
||||
@@ -1239,25 +1278,29 @@ extern pthread_mutex_t used_memory_mutex;
|
||||
#endif
|
||||
if (g_PersistenceDisabled || g_SentinelMode) {
|
||||
return redis_main(argc, argv);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
StartupStatus status = QForkStartup();
|
||||
if (status == ssCONTINUE_AS_PARENT) {
|
||||
int retval = redis_main(argc, argv);
|
||||
QForkShutdown();
|
||||
return retval;
|
||||
} else if (status == ssCHILD_EXIT) {
|
||||
}
|
||||
else if (status == ssCHILD_EXIT) {
|
||||
// Child is done - clean up and exit
|
||||
QForkShutdown();
|
||||
return g_ChildExitCode;
|
||||
} else if (status == ssFAILED) {
|
||||
}
|
||||
else if (status == ssFAILED) {
|
||||
// Parent or child failed initialization
|
||||
return 1;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// Unexpected status return
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (system_error syserr) {
|
||||
RedisEventLog().LogError(string("Main: system error. ") + syserr.what());
|
||||
serverLog(LL_WARNING, "main: system error caught. error code=0x%08x, message=%s\n", syserr.code().value(), syserr.what());
|
||||
@@ -1270,5 +1313,5 @@ extern pthread_mutex_t used_memory_mutex;
|
||||
RedisEventLog().LogError(string("Main: an exception occurred. ") + ex.what());
|
||||
serverLog(LL_WARNING, "main: other exception caught.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,76 +29,76 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
BOOL g_IsForkedProcess;
|
||||
BOOL g_IsForkedProcess;
|
||||
|
||||
typedef enum operationType {
|
||||
otINVALID = 0,
|
||||
otRDB = 1,
|
||||
otAOF = 2,
|
||||
otSocket = 3
|
||||
} OperationType;
|
||||
typedef enum operationType {
|
||||
otINVALID = 0,
|
||||
otRDB = 1,
|
||||
otAOF = 2,
|
||||
otSocket = 3
|
||||
} OperationType;
|
||||
|
||||
typedef enum operationStatus {
|
||||
osUNSTARTED = 0,
|
||||
osINPROGRESS = 1,
|
||||
osCOMPLETE = 2,
|
||||
osFAILED = 3
|
||||
} OperationStatus;
|
||||
typedef enum operationStatus {
|
||||
osUNSTARTED = 0,
|
||||
osINPROGRESS = 1,
|
||||
osCOMPLETE = 2,
|
||||
osFAILED = 3
|
||||
} OperationStatus;
|
||||
|
||||
typedef enum startupStatus {
|
||||
ssFAILED = 0, // Something went wrong, exit program with error.
|
||||
ssCONTINUE_AS_PARENT = 1, // Parent qfork initialization complete, continue as parent instance. Call QForkShutdown when exiting.
|
||||
ssCHILD_EXIT = 2 // Child completed operation. Call QForkShutdown and exit.
|
||||
} StartupStatus;
|
||||
typedef enum startupStatus {
|
||||
ssFAILED = 0, // Something went wrong, exit program with error.
|
||||
ssCONTINUE_AS_PARENT = 1, // Parent qfork initialization complete, continue as parent instance. Call QForkShutdown when exiting.
|
||||
ssCHILD_EXIT = 2 // Child completed operation. Call QForkShutdown and exit.
|
||||
} StartupStatus;
|
||||
|
||||
// For parent process use only
|
||||
pid_t BeginForkOperation_Rdb(
|
||||
char* fileName,
|
||||
LPVOID redisData,
|
||||
int sizeOfRedisData,
|
||||
uint8_t *dictHashSeed);
|
||||
// For parent process use only
|
||||
pid_t BeginForkOperation_Rdb(
|
||||
char* fileName,
|
||||
LPVOID redisData,
|
||||
int sizeOfRedisData,
|
||||
uint8_t *dictHashSeed);
|
||||
|
||||
pid_t BeginForkOperation_Aof(
|
||||
int aof_pipe_write_ack_to_parent,
|
||||
int aof_pipe_read_ack_from_parent,
|
||||
int aof_pipe_read_data_from_parent,
|
||||
char* fileName,
|
||||
LPVOID redisData,
|
||||
int sizeOfRedisData,
|
||||
uint8_t *dictHashSeed);
|
||||
pid_t BeginForkOperation_Aof(
|
||||
int aof_pipe_write_ack_to_parent,
|
||||
int aof_pipe_read_ack_from_parent,
|
||||
int aof_pipe_read_data_from_parent,
|
||||
char* fileName,
|
||||
LPVOID redisData,
|
||||
int sizeOfRedisData,
|
||||
uint8_t *dictHashSeed);
|
||||
|
||||
pid_t BeginForkOperation_Socket(
|
||||
int *fds,
|
||||
int numfds,
|
||||
uint64_t *clientids,
|
||||
int pipe_write_fd,
|
||||
LPVOID redisData,
|
||||
int sizeOfRedisData,
|
||||
uint8_t *dictHashSeed);
|
||||
pid_t BeginForkOperation_Socket(
|
||||
int *fds,
|
||||
int numfds,
|
||||
uint64_t *clientids,
|
||||
int pipe_write_fd,
|
||||
LPVOID redisData,
|
||||
int sizeOfRedisData,
|
||||
uint8_t *dictHashSeed);
|
||||
|
||||
void BeginForkOperation_Socket_Duplicate(DWORD dwProcessId);
|
||||
void BeginForkOperation_Socket_Duplicate(DWORD dwProcessId);
|
||||
|
||||
OperationStatus GetForkOperationStatus();
|
||||
BOOL EndForkOperation(int * pExitCode);
|
||||
BOOL AbortForkOperation();
|
||||
OperationStatus GetForkOperationStatus();
|
||||
BOOL EndForkOperation(int * pExitCode);
|
||||
BOOL AbortForkOperation();
|
||||
|
||||
#ifdef USE_DLMALLOC
|
||||
LPVOID AllocHeapBlock(size_t size, BOOL allocateHigh);
|
||||
// for no persistence optimization/feature when using dlmalloc
|
||||
extern void*(*g_malloc)(size_t);
|
||||
extern void*(*g_calloc)(size_t, size_t);
|
||||
extern void*(*g_realloc)(void*, size_t);
|
||||
extern void(*g_free)(void*);
|
||||
extern size_t(*g_msize)(void*);
|
||||
LPVOID AllocHeapBlock(size_t size, BOOL allocateHigh);
|
||||
// for no persistence optimization/feature when using dlmalloc
|
||||
extern void*(*g_malloc)(size_t);
|
||||
extern void*(*g_calloc)(size_t, size_t);
|
||||
extern void*(*g_realloc)(void*, size_t);
|
||||
extern void(*g_free)(void*);
|
||||
extern size_t(*g_msize)(void*);
|
||||
#elif USE_JEMALLOC
|
||||
LPVOID AllocHeapBlock(LPVOID addr, size_t size, BOOL zero);
|
||||
BOOL PurgePages(LPVOID addr, size_t length);
|
||||
LPVOID AllocHeapBlock(LPVOID addr, size_t size, BOOL zero);
|
||||
BOOL PurgePages(LPVOID addr, size_t length);
|
||||
#endif
|
||||
BOOL FreeHeapBlock(LPVOID addr, size_t size);
|
||||
BOOL FreeHeapBlock(LPVOID addr, size_t size);
|
||||
|
||||
#ifndef NO_QFORKIMPL
|
||||
#ifdef QFORK_MAIN_IMPL
|
||||
int redis_main(int argc, char** argv);
|
||||
int redis_main(int argc, char** argv);
|
||||
#else
|
||||
#define main redis_main
|
||||
#endif
|
||||
|
||||
@@ -24,12 +24,13 @@
|
||||
#define WIN32_INTEROP_WSIOCP_H
|
||||
|
||||
#include "win32_wsiocp2.h"
|
||||
#include "../ae.h"
|
||||
|
||||
/* structs and functions for using IOCP with windows sockets */
|
||||
/* structs and functions for using IOCP with windows sockets */
|
||||
|
||||
/* structure used for async write requests.
|
||||
* contains overlapped, WSABuf, and callback info
|
||||
* NOTE: OVERLAPPED must be first member */
|
||||
/* structure used for async write requests.
|
||||
* contains overlapped, WSABuf, and callback info
|
||||
* NOTE: OVERLAPPED must be first member */
|
||||
typedef struct asendreq {
|
||||
OVERLAPPED ov;
|
||||
WSABUF wbuf;
|
||||
@@ -48,7 +49,6 @@ typedef struct aacceptreq {
|
||||
struct aacceptreq *next;
|
||||
} aacceptreq;
|
||||
|
||||
|
||||
/* per socket information */
|
||||
typedef struct iocpSockState {
|
||||
int masks;
|
||||
|
||||
@@ -30,13 +30,17 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Win32_Interop\win32_types_hiredis.h"
|
||||
#endif
|
||||
#include "ae.h"
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef _WIN32
|
||||
#include <sys/types.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/timeb.h>
|
||||
#include "../../src/Win32_Interop/Win32_FDAPI.h"
|
||||
#include "../../src/Win32_Interop/Win32_Service.h"
|
||||
#include "../src/Win32_Interop/Win32_FDAPI.h"
|
||||
#include "../src/Win32_Interop/Win32_Service.h"
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
@@ -47,27 +51,26 @@
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "ae.h"
|
||||
#include "zmalloc.h"
|
||||
#include "config.h"
|
||||
|
||||
/* Include the best multiplexing layer supported by this system.
|
||||
* The following should be ordered by performances, descending. */
|
||||
/* Include the best multiplexing layer supported by this system.
|
||||
* The following should be ordered by performances, descending. */
|
||||
#ifdef _WIN32
|
||||
#include "ae_wsiocp.c"
|
||||
#else
|
||||
#ifdef HAVE_EVPORT
|
||||
#include "ae_evport.c"
|
||||
#else
|
||||
#ifdef HAVE_EPOLL
|
||||
#include "ae_epoll.c"
|
||||
#else
|
||||
#ifdef HAVE_KQUEUE
|
||||
#include "ae_kqueue.c"
|
||||
#else
|
||||
#include "ae_select.c"
|
||||
#endif
|
||||
#endif
|
||||
#ifdef HAVE_EPOLL
|
||||
#include "ae_epoll.c"
|
||||
#else
|
||||
#ifdef HAVE_KQUEUE
|
||||
#include "ae_kqueue.c"
|
||||
#else
|
||||
#include "ae_select.c"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -120,15 +123,15 @@ int aeResizeSetSize(aeEventLoop *eventLoop, int setsize) {
|
||||
|
||||
if (setsize == eventLoop->setsize) return AE_OK;
|
||||
if (eventLoop->maxfd >= setsize) return AE_ERR;
|
||||
if (aeApiResize(eventLoop,setsize) == -1) return AE_ERR;
|
||||
if (aeApiResize(eventLoop, setsize) == -1) return AE_ERR;
|
||||
|
||||
eventLoop->events = zrealloc(eventLoop->events,sizeof(aeFileEvent)*setsize);
|
||||
eventLoop->fired = zrealloc(eventLoop->fired,sizeof(aeFiredEvent)*setsize);
|
||||
eventLoop->events = zrealloc(eventLoop->events, sizeof(aeFileEvent)*setsize);
|
||||
eventLoop->fired = zrealloc(eventLoop->fired, sizeof(aeFiredEvent)*setsize);
|
||||
eventLoop->setsize = setsize;
|
||||
|
||||
/* Make sure that if we created new slots, they are initialized with
|
||||
* an AE_NONE mask. */
|
||||
for (i = eventLoop->maxfd+1; i < setsize; i++)
|
||||
for (i = eventLoop->maxfd + 1; i < setsize; i++)
|
||||
eventLoop->events[i].mask = AE_NONE;
|
||||
return AE_OK;
|
||||
}
|
||||
@@ -145,7 +148,7 @@ void aeStop(aeEventLoop *eventLoop) {
|
||||
}
|
||||
|
||||
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
|
||||
aeFileProc *proc, void *clientData)
|
||||
aeFileProc *proc, void *clientData)
|
||||
{
|
||||
if (fd >= eventLoop->setsize) {
|
||||
errno = ERANGE;
|
||||
@@ -176,7 +179,7 @@ void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
|
||||
/* Update the max fd */
|
||||
int j;
|
||||
|
||||
for (j = eventLoop->maxfd-1; j >= 0; j--)
|
||||
for (j = eventLoop->maxfd - 1; j >= 0; j--)
|
||||
if (eventLoop->events[j].mask != AE_NONE) break;
|
||||
eventLoop->maxfd = j;
|
||||
}
|
||||
@@ -203,7 +206,7 @@ static void aeGetTime(PORT_LONG *seconds, PORT_LONG *milliseconds)
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
*seconds = tv.tv_sec;
|
||||
*milliseconds = tv.tv_usec/1000;
|
||||
*milliseconds = tv.tv_usec / 1000;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -211,10 +214,10 @@ static void aeAddMillisecondsToNow(PORT_LONGLONG milliseconds, PORT_LONG *sec, P
|
||||
PORT_LONG cur_sec, cur_ms, when_sec, when_ms;
|
||||
|
||||
aeGetTime(&cur_sec, &cur_ms);
|
||||
when_sec = (PORT_LONG) (cur_sec + milliseconds/1000);
|
||||
when_ms = cur_ms + milliseconds%1000;
|
||||
when_sec = (PORT_LONG) (cur_sec + milliseconds / 1000);
|
||||
when_ms = cur_ms + milliseconds % 1000;
|
||||
if (when_ms >= 1000) {
|
||||
when_sec ++;
|
||||
when_sec++;
|
||||
when_ms -= 1000;
|
||||
}
|
||||
*sec = when_sec;
|
||||
@@ -222,8 +225,8 @@ static void aeAddMillisecondsToNow(PORT_LONGLONG milliseconds, PORT_LONG *sec, P
|
||||
}
|
||||
|
||||
PORT_LONGLONG aeCreateTimeEvent(aeEventLoop *eventLoop, PORT_LONGLONG milliseconds,
|
||||
aeTimeProc *proc, void *clientData,
|
||||
aeEventFinalizerProc *finalizerProc)
|
||||
aeTimeProc *proc, void *clientData,
|
||||
aeEventFinalizerProc *finalizerProc)
|
||||
{
|
||||
PORT_LONGLONG id = eventLoop->timeEventNextId++;
|
||||
aeTimeEvent *te;
|
||||
@@ -231,7 +234,7 @@ PORT_LONGLONG aeCreateTimeEvent(aeEventLoop *eventLoop, PORT_LONGLONG millisecon
|
||||
te = zmalloc(sizeof(*te));
|
||||
if (te == NULL) return AE_ERR;
|
||||
te->id = id;
|
||||
aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);
|
||||
aeAddMillisecondsToNow(milliseconds, &te->when_sec, &te->when_ms);
|
||||
te->timeProc = proc;
|
||||
te->finalizerProc = finalizerProc;
|
||||
te->clientData = clientData;
|
||||
@@ -243,7 +246,7 @@ PORT_LONGLONG aeCreateTimeEvent(aeEventLoop *eventLoop, PORT_LONGLONG millisecon
|
||||
int aeDeleteTimeEvent(aeEventLoop *eventLoop, PORT_LONGLONG id)
|
||||
{
|
||||
aeTimeEvent *te = eventLoop->timeEventHead;
|
||||
while(te) {
|
||||
while (te) {
|
||||
if (te->id == id) {
|
||||
te->id = AE_DELETED_EVENT_ID;
|
||||
return AE_OK;
|
||||
@@ -269,10 +272,10 @@ static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)
|
||||
aeTimeEvent *te = eventLoop->timeEventHead;
|
||||
aeTimeEvent *nearest = NULL;
|
||||
|
||||
while(te) {
|
||||
while (te) {
|
||||
if (!nearest || te->when_sec < nearest->when_sec ||
|
||||
(te->when_sec == nearest->when_sec &&
|
||||
te->when_ms < nearest->when_ms))
|
||||
(te->when_sec == nearest->when_sec &&
|
||||
te->when_ms < nearest->when_ms))
|
||||
nearest = te;
|
||||
te = te->next;
|
||||
}
|
||||
@@ -296,7 +299,7 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
* indefinitely, and practice suggests it is. */
|
||||
if (now < eventLoop->lastTime) {
|
||||
te = eventLoop->timeEventHead;
|
||||
while(te) {
|
||||
while (te) {
|
||||
te->when_sec = 0;
|
||||
te = te->next;
|
||||
}
|
||||
@@ -305,8 +308,8 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
|
||||
prev = NULL;
|
||||
te = eventLoop->timeEventHead;
|
||||
maxId = eventLoop->timeEventNextId-1;
|
||||
while(te) {
|
||||
maxId = eventLoop->timeEventNextId - 1;
|
||||
while (te) {
|
||||
PORT_LONG now_sec, now_ms;
|
||||
PORT_LONGLONG id;
|
||||
|
||||
@@ -343,8 +346,9 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
retval = te->timeProc(eventLoop, id, te->clientData);
|
||||
processed++;
|
||||
if (retval != AE_NOMORE) {
|
||||
aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
|
||||
} else {
|
||||
aeAddMillisecondsToNow(retval, &te->when_sec, &te->when_ms);
|
||||
}
|
||||
else {
|
||||
te->id = AE_DELETED_EVENT_ID;
|
||||
}
|
||||
}
|
||||
@@ -402,24 +406,27 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
||||
/* How many milliseconds we need to wait for the next
|
||||
* time event to fire? */
|
||||
PORT_LONGLONG ms =
|
||||
(shortest->when_sec - now_sec)*1000 +
|
||||
(shortest->when_sec - now_sec) * 1000 +
|
||||
shortest->when_ms - now_ms;
|
||||
|
||||
if (ms > 0) {
|
||||
tvp->tv_sec = ms/1000;
|
||||
tvp->tv_usec = (ms % 1000)*1000;
|
||||
} else {
|
||||
tvp->tv_sec = ms / 1000;
|
||||
tvp->tv_usec = (ms % 1000) * 1000;
|
||||
}
|
||||
else {
|
||||
tvp->tv_sec = 0;
|
||||
tvp->tv_usec = 0;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
/* If we have to check for events but need to return
|
||||
* ASAP because of AE_DONT_WAIT we need to set the timeout
|
||||
* to zero */
|
||||
if (flags & AE_DONT_WAIT) {
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
tvp = &tv;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
/* Otherwise we can block */
|
||||
tvp = NULL; /* wait forever */
|
||||
}
|
||||
@@ -439,16 +446,16 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
||||
int fd = eventLoop->fired[j].fd;
|
||||
int rfired = 0;
|
||||
|
||||
/* note the fe->mask & mask & ... code: maybe an already processed
|
||||
* event removed an element that fired and we still didn't
|
||||
* processed, so we check if the event is still valid. */
|
||||
/* note the fe->mask & mask & ... code: maybe an already processed
|
||||
* event removed an element that fired and we still didn't
|
||||
* processed, so we check if the event is still valid. */
|
||||
if (fe->mask & mask & AE_READABLE) {
|
||||
rfired = 1;
|
||||
fe->rfileProc(eventLoop,fd,fe->clientData,mask);
|
||||
fe->rfileProc(eventLoop, fd, fe->clientData, mask);
|
||||
}
|
||||
if (fe->mask & mask & AE_WRITABLE) {
|
||||
if (!rfired || fe->wfileProc != fe->rfileProc)
|
||||
fe->wfileProc(eventLoop,fd,fe->clientData,mask);
|
||||
fe->wfileProc(eventLoop, fd, fe->clientData, mask);
|
||||
}
|
||||
processed++;
|
||||
}
|
||||
@@ -471,13 +478,15 @@ int aeWait(int fd, int mask, PORT_LONGLONG milliseconds) {
|
||||
if (mask & AE_READABLE) pfd.events |= POLLIN;
|
||||
if (mask & AE_WRITABLE) pfd.events |= POLLOUT;
|
||||
|
||||
if ((retval = poll(&pfd, 1, (int)milliseconds))== 1) { WIN_PORT_FIX /* cast (int) */
|
||||
if (pfd.revents & POLLIN) retmask |= AE_READABLE;
|
||||
if ((retval = poll(&pfd, 1, (int) milliseconds)) == 1) {
|
||||
WIN_PORT_FIX /* cast (int) */
|
||||
if (pfd.revents & POLLIN) retmask |= AE_READABLE;
|
||||
if (pfd.revents & POLLOUT) retmask |= AE_WRITABLE;
|
||||
if (pfd.revents & POLLERR) retmask |= AE_WRITABLE;
|
||||
if (pfd.revents & POLLERR) retmask |= AE_WRITABLE;
|
||||
if (pfd.revents & POLLHUP) retmask |= AE_WRITABLE;
|
||||
return retmask;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
@@ -487,7 +496,7 @@ void aeMain(aeEventLoop *eventLoop) {
|
||||
while (!eventLoop->stop) {
|
||||
if (eventLoop->beforesleep != NULL)
|
||||
eventLoop->beforesleep(eventLoop);
|
||||
aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
|
||||
aeProcessEvents(eventLoop, AE_ALL_EVENTS | AE_CALL_AFTER_SLEEP);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,4 +510,4 @@ void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep
|
||||
|
||||
void aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep) {
|
||||
eventLoop->aftersleep = aftersleep;
|
||||
}
|
||||
}
|
||||
+132
-117
@@ -29,7 +29,7 @@
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "../../src/Win32_Interop/Win32_Portability.h"
|
||||
#include "../src/Win32_Interop/Win32_Portability.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -39,13 +39,13 @@
|
||||
#include "zmalloc.h"
|
||||
#include "endianconv.h"
|
||||
|
||||
/* Note that these encodings are ordered, so:
|
||||
* INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */
|
||||
/* Note that these encodings are ordered, so:
|
||||
* INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */
|
||||
#define INTSET_ENC_INT16 (sizeof(int16_t))
|
||||
#define INTSET_ENC_INT32 (sizeof(int32_t))
|
||||
#define INTSET_ENC_INT64 (sizeof(int64_t))
|
||||
|
||||
/* Return the required encoding for the provided value. */
|
||||
/* Return the required encoding for the provided value. */
|
||||
static uint8_t _intsetValueEncoding(int64_t v) {
|
||||
if (v < INT32_MIN || v > INT32_MAX)
|
||||
return INTSET_ENC_INT64;
|
||||
@@ -62,15 +62,17 @@ static int64_t _intsetGetEncoded(intset *is, int pos, uint8_t enc) {
|
||||
int16_t v16;
|
||||
|
||||
if (enc == INTSET_ENC_INT64) {
|
||||
memcpy(&v64,((int64_t*)is->contents)+pos,sizeof(v64));
|
||||
memcpy(&v64, ((int64_t*) is->contents) + pos, sizeof(v64));
|
||||
memrev64ifbe(&v64);
|
||||
return v64;
|
||||
} else if (enc == INTSET_ENC_INT32) {
|
||||
memcpy(&v32,((int32_t*)is->contents)+pos,sizeof(v32));
|
||||
}
|
||||
else if (enc == INTSET_ENC_INT32) {
|
||||
memcpy(&v32, ((int32_t*) is->contents) + pos, sizeof(v32));
|
||||
memrev32ifbe(&v32);
|
||||
return v32;
|
||||
} else {
|
||||
memcpy(&v16,((int16_t*)is->contents)+pos,sizeof(v16));
|
||||
}
|
||||
else {
|
||||
memcpy(&v16, ((int16_t*) is->contents) + pos, sizeof(v16));
|
||||
memrev16ifbe(&v16);
|
||||
return v16;
|
||||
}
|
||||
@@ -78,7 +80,7 @@ static int64_t _intsetGetEncoded(intset *is, int pos, uint8_t enc) {
|
||||
|
||||
/* Return the value at pos, using the configured encoding. */
|
||||
static int64_t _intsetGet(intset *is, int pos) {
|
||||
return _intsetGetEncoded(is,pos,intrev32ifbe(is->encoding));
|
||||
return _intsetGetEncoded(is, pos, intrev32ifbe(is->encoding));
|
||||
}
|
||||
|
||||
/* Set the value at pos, using the configured encoding. */
|
||||
@@ -86,14 +88,16 @@ static void _intsetSet(intset *is, int pos, int64_t value) {
|
||||
uint32_t encoding = intrev32ifbe(is->encoding);
|
||||
|
||||
if (encoding == INTSET_ENC_INT64) {
|
||||
((int64_t*)is->contents)[pos] = value;
|
||||
memrev64ifbe(((int64_t*)is->contents)+pos);
|
||||
} else if (encoding == INTSET_ENC_INT32) {
|
||||
((int32_t*)is->contents)[pos] = (int32_t)value; WIN_PORT_FIX /* cast (int32_t) */
|
||||
memrev32ifbe(((int32_t*)is->contents)+pos);
|
||||
} else {
|
||||
((int16_t*)is->contents)[pos] = (int16_t)value; WIN_PORT_FIX /* cast (int16_t) */
|
||||
memrev16ifbe(((int16_t*)is->contents)+pos);
|
||||
((int64_t*) is->contents)[pos] = value;
|
||||
memrev64ifbe(((int64_t*) is->contents) + pos);
|
||||
}
|
||||
else if (encoding == INTSET_ENC_INT32) {
|
||||
((int32_t*) is->contents)[pos] = (int32_t) value; WIN_PORT_FIX /* cast (int32_t) */
|
||||
memrev32ifbe(((int32_t*) is->contents) + pos);
|
||||
}
|
||||
else {
|
||||
((int16_t*) is->contents)[pos] = (int16_t) value; WIN_PORT_FIX /* cast (int16_t) */
|
||||
memrev16ifbe(((int16_t*) is->contents) + pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +112,7 @@ intset *intsetNew(void) {
|
||||
/* Resize the intset */
|
||||
static intset *intsetResize(intset *is, uint32_t len) {
|
||||
uint32_t size = len*intrev32ifbe(is->encoding);
|
||||
is = zrealloc(is,sizeof(intset)+size);
|
||||
is = zrealloc(is, sizeof(intset) + size);
|
||||
return is;
|
||||
}
|
||||
|
||||
@@ -117,33 +121,37 @@ static intset *intsetResize(intset *is, uint32_t len) {
|
||||
* the value is not present in the intset and sets "pos" to the position
|
||||
* where "value" can be inserted. */
|
||||
static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
|
||||
int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;
|
||||
int min = 0, max = intrev32ifbe(is->length) - 1, mid = -1;
|
||||
int64_t cur = -1;
|
||||
|
||||
/* The value can never be found when the set is empty */
|
||||
if (intrev32ifbe(is->length) == 0) {
|
||||
if (pos) *pos = 0;
|
||||
return 0;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
/* Check for the case where we know we cannot find the value,
|
||||
* but do know the insert position. */
|
||||
if (value > _intsetGet(is,intrev32ifbe(is->length)-1)) {
|
||||
if (value > _intsetGet(is, intrev32ifbe(is->length) - 1)) {
|
||||
if (pos) *pos = intrev32ifbe(is->length);
|
||||
return 0;
|
||||
} else if (value < _intsetGet(is,0)) {
|
||||
}
|
||||
else if (value < _intsetGet(is, 0)) {
|
||||
if (pos) *pos = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
while(max >= min) {
|
||||
mid = ((unsigned int)min + (unsigned int)max) >> 1;
|
||||
cur = _intsetGet(is,mid);
|
||||
while (max >= min) {
|
||||
mid = ((unsigned int) min + (unsigned int) max) >> 1;
|
||||
cur = _intsetGet(is, mid);
|
||||
if (value > cur) {
|
||||
min = mid+1;
|
||||
} else if (value < cur) {
|
||||
max = mid-1;
|
||||
} else {
|
||||
min = mid + 1;
|
||||
}
|
||||
else if (value < cur) {
|
||||
max = mid - 1;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -151,7 +159,8 @@ static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
|
||||
if (value == cur) {
|
||||
if (pos) *pos = mid;
|
||||
return 1;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (pos) *pos = min;
|
||||
return 0;
|
||||
}
|
||||
@@ -166,42 +175,44 @@ static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
|
||||
|
||||
/* First set new encoding and resize */
|
||||
is->encoding = intrev32ifbe(newenc);
|
||||
is = intsetResize(is,intrev32ifbe(is->length)+1);
|
||||
is = intsetResize(is, intrev32ifbe(is->length) + 1);
|
||||
|
||||
/* Upgrade back-to-front so we don't overwrite values.
|
||||
* Note that the "prepend" variable is used to make sure we have an empty
|
||||
* space at either the beginning or the end of the intset. */
|
||||
while(length--)
|
||||
_intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));
|
||||
while (length--)
|
||||
_intsetSet(is, length + prepend, _intsetGetEncoded(is, length, curenc));
|
||||
|
||||
/* Set the value at the beginning or the end. */
|
||||
if (prepend)
|
||||
_intsetSet(is,0,value);
|
||||
_intsetSet(is, 0, value);
|
||||
else
|
||||
_intsetSet(is,intrev32ifbe(is->length),value);
|
||||
is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
|
||||
_intsetSet(is, intrev32ifbe(is->length), value);
|
||||
is->length = intrev32ifbe(intrev32ifbe(is->length) + 1);
|
||||
return is;
|
||||
}
|
||||
|
||||
static void intsetMoveTail(intset *is, uint32_t from, uint32_t to) {
|
||||
void *src, *dst;
|
||||
uint32_t bytes = intrev32ifbe(is->length)-from;
|
||||
uint32_t bytes = intrev32ifbe(is->length) - from;
|
||||
uint32_t encoding = intrev32ifbe(is->encoding);
|
||||
|
||||
if (encoding == INTSET_ENC_INT64) {
|
||||
src = (int64_t*)is->contents+from;
|
||||
dst = (int64_t*)is->contents+to;
|
||||
src = (int64_t*) is->contents + from;
|
||||
dst = (int64_t*) is->contents + to;
|
||||
bytes *= sizeof(int64_t);
|
||||
} else if (encoding == INTSET_ENC_INT32) {
|
||||
src = (int32_t*)is->contents+from;
|
||||
dst = (int32_t*)is->contents+to;
|
||||
}
|
||||
else if (encoding == INTSET_ENC_INT32) {
|
||||
src = (int32_t*) is->contents + from;
|
||||
dst = (int32_t*) is->contents + to;
|
||||
bytes *= sizeof(int32_t);
|
||||
} else {
|
||||
src = (int16_t*)is->contents+from;
|
||||
dst = (int16_t*)is->contents+to;
|
||||
}
|
||||
else {
|
||||
src = (int16_t*) is->contents + from;
|
||||
dst = (int16_t*) is->contents + to;
|
||||
bytes *= sizeof(int16_t);
|
||||
}
|
||||
memmove(dst,src,bytes);
|
||||
memmove(dst, src, bytes);
|
||||
}
|
||||
|
||||
/* Insert an integer in the intset */
|
||||
@@ -215,22 +226,23 @@ intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
|
||||
* because it lies outside the range of existing values. */
|
||||
if (valenc > intrev32ifbe(is->encoding)) {
|
||||
/* This always succeeds, so we don't need to curry *success. */
|
||||
return intsetUpgradeAndAdd(is,value);
|
||||
} else {
|
||||
return intsetUpgradeAndAdd(is, value);
|
||||
}
|
||||
else {
|
||||
/* Abort if the value is already present in the set.
|
||||
* This call will populate "pos" with the right position to insert
|
||||
* the value when it cannot be found. */
|
||||
if (intsetSearch(is,value,&pos)) {
|
||||
if (intsetSearch(is, value, &pos)) {
|
||||
if (success) *success = 0;
|
||||
return is;
|
||||
}
|
||||
|
||||
is = intsetResize(is,intrev32ifbe(is->length)+1);
|
||||
if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
|
||||
is = intsetResize(is, intrev32ifbe(is->length) + 1);
|
||||
if (pos < intrev32ifbe(is->length)) intsetMoveTail(is, pos, pos + 1);
|
||||
}
|
||||
|
||||
_intsetSet(is,pos,value);
|
||||
is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
|
||||
_intsetSet(is, pos, value);
|
||||
is->length = intrev32ifbe(intrev32ifbe(is->length) + 1);
|
||||
return is;
|
||||
}
|
||||
|
||||
@@ -240,16 +252,16 @@ intset *intsetRemove(intset *is, int64_t value, int *success) {
|
||||
uint32_t pos;
|
||||
if (success) *success = 0;
|
||||
|
||||
if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,&pos)) {
|
||||
if (valenc <= intrev32ifbe(is->encoding) && intsetSearch(is, value, &pos)) {
|
||||
uint32_t len = intrev32ifbe(is->length);
|
||||
|
||||
/* We know we can delete */
|
||||
if (success) *success = 1;
|
||||
|
||||
/* Overwrite value with tail and update length */
|
||||
if (pos < (len-1)) intsetMoveTail(is,pos+1,pos);
|
||||
is = intsetResize(is,len-1);
|
||||
is->length = intrev32ifbe(len-1);
|
||||
if (pos < (len - 1)) intsetMoveTail(is, pos + 1, pos);
|
||||
is = intsetResize(is, len - 1);
|
||||
is->length = intrev32ifbe(len - 1);
|
||||
}
|
||||
return is;
|
||||
}
|
||||
@@ -257,19 +269,19 @@ intset *intsetRemove(intset *is, int64_t value, int *success) {
|
||||
/* Determine whether a value belongs to this set */
|
||||
uint8_t intsetFind(intset *is, int64_t value) {
|
||||
uint8_t valenc = _intsetValueEncoding(value);
|
||||
return valenc <= intrev32ifbe(is->encoding) && intsetSearch(is,value,NULL);
|
||||
return valenc <= intrev32ifbe(is->encoding) && intsetSearch(is, value, NULL);
|
||||
}
|
||||
|
||||
/* Return random member */
|
||||
int64_t intsetRandom(intset *is) {
|
||||
return _intsetGet(is,rand()%intrev32ifbe(is->length));
|
||||
return _intsetGet(is, rand() % intrev32ifbe(is->length));
|
||||
}
|
||||
|
||||
/* Get the value at the given position. When this position is
|
||||
* out of range the function returns 0, when in range it returns 1. */
|
||||
uint8_t intsetGet(intset *is, uint32_t pos, int64_t *value) {
|
||||
if (pos < intrev32ifbe(is->length)) {
|
||||
*value = _intsetGet(is,pos);
|
||||
*value = _intsetGet(is, pos);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -282,7 +294,7 @@ uint32_t intsetLen(const intset *is) {
|
||||
|
||||
/* Return intset blob size in bytes. */
|
||||
size_t intsetBlobLen(intset *is) {
|
||||
return sizeof(intset)+intrev32ifbe(is->length)*intrev32ifbe(is->encoding);
|
||||
return sizeof(intset) + intrev32ifbe(is->length)*intrev32ifbe(is->encoding);
|
||||
}
|
||||
|
||||
#ifdef REDIS_TEST
|
||||
@@ -292,7 +304,7 @@ size_t intsetBlobLen(intset *is) {
|
||||
#if 0
|
||||
static void intsetRepr(intset *is) {
|
||||
for (uint32_t i = 0; i < intrev32ifbe(is->length); i++) {
|
||||
printf("%lld\n", (uint64_t)_intsetGet(is,i));
|
||||
printf("%lld\n", (uint64_t) _intsetGet(is, i));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
@@ -309,45 +321,48 @@ static void ok(void) {
|
||||
|
||||
static PORT_LONGLONG usec(void) {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,NULL);
|
||||
return (((PORT_LONGLONG)tv.tv_sec)*1000000)+tv.tv_usec;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (((PORT_LONGLONG) tv.tv_sec) * 1000000) + tv.tv_usec;
|
||||
}
|
||||
|
||||
#define assert(_e) ((_e)?(void)0:(_assert(#_e,__FILE__,__LINE__),exit(1)))
|
||||
static void _assert(char *estr, char *file, int line) {
|
||||
printf("\n\n=== ASSERTION FAILED ===\n");
|
||||
printf("==> %s:%d '%s' is not true\n",file,line,estr);
|
||||
printf("==> %s:%d '%s' is not true\n", file, line, estr);
|
||||
}
|
||||
|
||||
static intset *createSet(int bits, int size) {
|
||||
uint64_t mask = (1<<bits)-1;
|
||||
uint64_t mask = (1 << bits) - 1;
|
||||
uint64_t value;
|
||||
intset *is = intsetNew();
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (bits > 32) {
|
||||
value = (rand()*rand()) & mask;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
value = rand() & mask;
|
||||
}
|
||||
is = intsetAdd(is,value,NULL);
|
||||
is = intsetAdd(is, value, NULL);
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
static void checkConsistency(intset *is) {
|
||||
for (uint32_t i = 0; i < (intrev32ifbe(is->length)-1); i++) {
|
||||
for (uint32_t i = 0; i < (intrev32ifbe(is->length) - 1); i++) {
|
||||
uint32_t encoding = intrev32ifbe(is->encoding);
|
||||
|
||||
if (encoding == INTSET_ENC_INT16) {
|
||||
int16_t *i16 = (int16_t*)is->contents;
|
||||
assert(i16[i] < i16[i+1]);
|
||||
} else if (encoding == INTSET_ENC_INT32) {
|
||||
int32_t *i32 = (int32_t*)is->contents;
|
||||
assert(i32[i] < i32[i+1]);
|
||||
} else {
|
||||
int64_t *i64 = (int64_t*)is->contents;
|
||||
assert(i64[i] < i64[i+1]);
|
||||
int16_t *i16 = (int16_t*) is->contents;
|
||||
assert(i16[i] < i16[i + 1]);
|
||||
}
|
||||
else if (encoding == INTSET_ENC_INT32) {
|
||||
int32_t *i32 = (int32_t*) is->contents;
|
||||
assert(i32[i] < i32[i + 1]);
|
||||
}
|
||||
else {
|
||||
int64_t *i64 = (int64_t*) is->contents;
|
||||
assert(i64[i] < i64[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -372,18 +387,18 @@ int intsetTest(int argc, char **argv) {
|
||||
assert(_intsetValueEncoding(-2147483649) == INTSET_ENC_INT64);
|
||||
assert(_intsetValueEncoding(+2147483648) == INTSET_ENC_INT64);
|
||||
assert(_intsetValueEncoding(-9223372036854775808ull) ==
|
||||
INTSET_ENC_INT64);
|
||||
INTSET_ENC_INT64);
|
||||
assert(_intsetValueEncoding(+9223372036854775807ull) ==
|
||||
INTSET_ENC_INT64);
|
||||
INTSET_ENC_INT64);
|
||||
ok();
|
||||
}
|
||||
|
||||
printf("Basic adding: "); {
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,5,&success); assert(success);
|
||||
is = intsetAdd(is,6,&success); assert(success);
|
||||
is = intsetAdd(is,4,&success); assert(success);
|
||||
is = intsetAdd(is,4,&success); assert(!success);
|
||||
is = intsetAdd(is, 5, &success); assert(success);
|
||||
is = intsetAdd(is, 6, &success); assert(success);
|
||||
is = intsetAdd(is, 4, &success); assert(success);
|
||||
is = intsetAdd(is, 4, &success); assert(!success);
|
||||
ok();
|
||||
}
|
||||
|
||||
@@ -391,7 +406,7 @@ int intsetTest(int argc, char **argv) {
|
||||
uint32_t inserts = 0;
|
||||
is = intsetNew();
|
||||
for (i = 0; i < 1024; i++) {
|
||||
is = intsetAdd(is,rand()%0x800,&success);
|
||||
is = intsetAdd(is, rand() % 0x800, &success);
|
||||
if (success) inserts++;
|
||||
}
|
||||
assert(intrev32ifbe(is->length) == inserts);
|
||||
@@ -401,63 +416,63 @@ int intsetTest(int argc, char **argv) {
|
||||
|
||||
printf("Upgrade from int16 to int32: "); {
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,32,NULL);
|
||||
is = intsetAdd(is, 32, NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
|
||||
is = intsetAdd(is,65535,NULL);
|
||||
is = intsetAdd(is, 65535, NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
|
||||
assert(intsetFind(is,32));
|
||||
assert(intsetFind(is,65535));
|
||||
assert(intsetFind(is, 32));
|
||||
assert(intsetFind(is, 65535));
|
||||
checkConsistency(is);
|
||||
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,32,NULL);
|
||||
is = intsetAdd(is, 32, NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
|
||||
is = intsetAdd(is,-65535,NULL);
|
||||
is = intsetAdd(is, -65535, NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
|
||||
assert(intsetFind(is,32));
|
||||
assert(intsetFind(is,-65535));
|
||||
assert(intsetFind(is, 32));
|
||||
assert(intsetFind(is, -65535));
|
||||
checkConsistency(is);
|
||||
ok();
|
||||
}
|
||||
|
||||
printf("Upgrade from int16 to int64: "); {
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,32,NULL);
|
||||
is = intsetAdd(is, 32, NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
|
||||
is = intsetAdd(is,4294967295,NULL);
|
||||
is = intsetAdd(is, 4294967295, NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
|
||||
assert(intsetFind(is,32));
|
||||
assert(intsetFind(is,4294967295));
|
||||
assert(intsetFind(is, 32));
|
||||
assert(intsetFind(is, 4294967295));
|
||||
checkConsistency(is);
|
||||
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,32,NULL);
|
||||
is = intsetAdd(is, 32, NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT16);
|
||||
is = intsetAdd(is,-4294967295,NULL);
|
||||
is = intsetAdd(is, -4294967295, NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
|
||||
assert(intsetFind(is,32));
|
||||
assert(intsetFind(is,-4294967295));
|
||||
assert(intsetFind(is, 32));
|
||||
assert(intsetFind(is, -4294967295));
|
||||
checkConsistency(is);
|
||||
ok();
|
||||
}
|
||||
|
||||
printf("Upgrade from int32 to int64: "); {
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,65535,NULL);
|
||||
is = intsetAdd(is, 65535, NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
|
||||
is = intsetAdd(is,4294967295,NULL);
|
||||
is = intsetAdd(is, 4294967295, NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
|
||||
assert(intsetFind(is,65535));
|
||||
assert(intsetFind(is,4294967295));
|
||||
assert(intsetFind(is, 65535));
|
||||
assert(intsetFind(is, 4294967295));
|
||||
checkConsistency(is);
|
||||
|
||||
is = intsetNew();
|
||||
is = intsetAdd(is,65535,NULL);
|
||||
is = intsetAdd(is, 65535, NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT32);
|
||||
is = intsetAdd(is,-4294967295,NULL);
|
||||
is = intsetAdd(is, -4294967295, NULL);
|
||||
assert(intrev32ifbe(is->encoding) == INTSET_ENC_INT64);
|
||||
assert(intsetFind(is,65535));
|
||||
assert(intsetFind(is,-4294967295));
|
||||
assert(intsetFind(is, 65535));
|
||||
assert(intsetFind(is, -4294967295));
|
||||
checkConsistency(is);
|
||||
ok();
|
||||
}
|
||||
@@ -466,13 +481,13 @@ int intsetTest(int argc, char **argv) {
|
||||
PORT_LONG num = 100000, size = 10000;
|
||||
int i, bits = 20;
|
||||
PORT_LONGLONG start;
|
||||
is = createSet(bits,size);
|
||||
is = createSet(bits, size);
|
||||
checkConsistency(is);
|
||||
|
||||
start = usec();
|
||||
for (i = 0; i < num; i++) intsetSearch(is,rand() % ((1<<bits)-1),NULL);
|
||||
for (i = 0; i < num; i++) intsetSearch(is, rand() % ((1 << bits) - 1), NULL);
|
||||
printf("%ld lookups, %ld element set, %lldusec\n",
|
||||
num,size,usec()-start);
|
||||
num, size, usec() - start);
|
||||
}
|
||||
|
||||
printf("Stress add+delete: "); {
|
||||
@@ -480,12 +495,12 @@ int intsetTest(int argc, char **argv) {
|
||||
is = intsetNew();
|
||||
for (i = 0; i < 0xffff; i++) {
|
||||
v1 = rand() % 0xfff;
|
||||
is = intsetAdd(is,v1,NULL);
|
||||
assert(intsetFind(is,v1));
|
||||
is = intsetAdd(is, v1, NULL);
|
||||
assert(intsetFind(is, v1));
|
||||
|
||||
v2 = rand() % 0xfff;
|
||||
is = intsetRemove(is,v2,NULL);
|
||||
assert(!intsetFind(is,v2));
|
||||
is = intsetRemove(is, v2, NULL);
|
||||
assert(!intsetFind(is, v2));
|
||||
}
|
||||
checkConsistency(is);
|
||||
ok();
|
||||
@@ -493,4 +508,4 @@ int intsetTest(int argc, char **argv) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1014,8 +1014,13 @@ int writeToClient(int fd, client *c, int handler_installed) {
|
||||
nwritten = 0;
|
||||
}
|
||||
else {
|
||||
#ifdef _WIN32
|
||||
serverLog(LL_VERBOSE,
|
||||
"Error writing to client: %s", wsa_strerror(errno));
|
||||
#else
|
||||
serverLog(LL_VERBOSE,
|
||||
"Error writing to client: %s", strerror(errno));
|
||||
#endif
|
||||
freeClient(c);
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
+47
-49
@@ -60,8 +60,8 @@ static char error[1024];
|
||||
static off_t epos;
|
||||
|
||||
int consumeNewline(char *buf) {
|
||||
if (strncmp(buf,"\r\n",2) != 0) {
|
||||
ERROR("Expected \\r\\n, got: %02x%02x",buf[0],buf[1]);
|
||||
if (strncmp(buf, "\r\n", 2) != 0) {
|
||||
ERROR("Expected \\r\\n, got: %02x%02x", buf[0], buf[1]);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@@ -70,14 +70,14 @@ int consumeNewline(char *buf) {
|
||||
int readLong(FILE *fp, char prefix, PORT_LONG *target) {
|
||||
char buf[128], *eptr;
|
||||
epos = ftello(fp);
|
||||
if (fgets(buf,sizeof(buf),fp) == NULL) {
|
||||
if (fgets(buf, sizeof(buf), fp) == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (buf[0] != prefix) {
|
||||
ERROR("Expected prefix '%c', got: '%c'",prefix,buf[0]);
|
||||
ERROR("Expected prefix '%c', got: '%c'", prefix, buf[0]);
|
||||
return 0;
|
||||
}
|
||||
*target = strtol(buf+1,&eptr,10);
|
||||
*target = strtol(buf + 1, &eptr, 10);
|
||||
return consumeNewline(eptr);
|
||||
}
|
||||
|
||||
@@ -86,8 +86,8 @@ int readBytes(FILE *fp, char *target, PORT_LONG length) {
|
||||
epos = ftello(fp);
|
||||
real = (PORT_LONG) fread(target, 1, length, fp);
|
||||
if (real != length) {
|
||||
ERROR("Expected to read %Id bytes, got %Id bytes",length,real); WIN_PORT_FIX /* %ld -> %Id */
|
||||
return 0;
|
||||
ERROR("Expected to read %Id bytes, got %Id bytes", length, real); WIN_PORT_FIX /* %ld -> %Id */
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -95,25 +95,25 @@ int readBytes(FILE *fp, char *target, PORT_LONG length) {
|
||||
int readString(FILE *fp, char** target) {
|
||||
PORT_LONG len;
|
||||
*target = NULL;
|
||||
if (!readLong(fp,'$',&len)) {
|
||||
if (!readLong(fp, '$', &len)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Increase length to also consume \r\n */
|
||||
len += 2;
|
||||
*target = (char*)zmalloc(len);
|
||||
if (!readBytes(fp,*target,len)) {
|
||||
*target = (char*) zmalloc(len);
|
||||
if (!readBytes(fp, *target, len)) {
|
||||
return 0;
|
||||
}
|
||||
if (!consumeNewline(*target+len-2)) {
|
||||
if (!consumeNewline(*target + len - 2)) {
|
||||
return 0;
|
||||
}
|
||||
(*target)[len-2] = '\0';
|
||||
(*target)[len - 2] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
int readArgc(FILE *fp, PORT_LONG *target) {
|
||||
return readLong(fp,'*',target);
|
||||
return readLong(fp, '*', target);
|
||||
}
|
||||
|
||||
off_t process(FILE *fp) {
|
||||
@@ -122,19 +122,20 @@ off_t process(FILE *fp) {
|
||||
int i, multi = 0;
|
||||
char *str;
|
||||
|
||||
while(1) {
|
||||
if (!multi) pos = (off_t)ftello(fp);
|
||||
while (1) {
|
||||
if (!multi) pos = (off_t) ftello(fp);
|
||||
if (!readArgc(fp, &argc)) break;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (!readString(fp,&str)) break;
|
||||
if (!readString(fp, &str)) break;
|
||||
if (i == 0) {
|
||||
if (strcasecmp(str, "multi") == 0) {
|
||||
if (multi++) {
|
||||
ERROR("Unexpected MULTI");
|
||||
break;
|
||||
}
|
||||
} else if (strcasecmp(str, "exec") == 0) {
|
||||
}
|
||||
else if (strcasecmp(str, "exec") == 0) {
|
||||
if (--multi) {
|
||||
ERROR("Unexpected EXEC");
|
||||
break;
|
||||
@@ -161,7 +162,6 @@ off_t process(FILE *fp) {
|
||||
}
|
||||
|
||||
int redis_check_aof_main(int argc, char **argv) {
|
||||
|
||||
char *filename;
|
||||
int fix = 0;
|
||||
#ifdef _WIN32
|
||||
@@ -174,28 +174,31 @@ int redis_check_aof_main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
printf("Usage: %s [--fix] <file.aof>\n", argv[0]);
|
||||
exit(1);
|
||||
} else if (argc == 2) {
|
||||
}
|
||||
else if (argc == 2) {
|
||||
filename = argv[1];
|
||||
} else if (argc == 3) {
|
||||
if (strcmp(argv[1],"--fix") != 0) {
|
||||
}
|
||||
else if (argc == 3) {
|
||||
if (strcmp(argv[1], "--fix") != 0) {
|
||||
printf("Invalid argument: %s\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
filename = argv[2];
|
||||
fix = 1;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
printf("Invalid arguments\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
FILE *fp = fopen(filename,IF_WIN32("r+b","r+"));
|
||||
FILE *fp = fopen(filename, IF_WIN32("r+b", "r+"));
|
||||
if (fp == NULL) {
|
||||
printf("Cannot open file: %s\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct redis_stat sb;
|
||||
if (redis_fstat(fileno(fp),&sb) == -1) {
|
||||
if (redis_fstat(fileno(fp), &sb) == -1) {
|
||||
printf("Cannot stat file: %s\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
@@ -206,63 +209,58 @@ int redis_check_aof_main(int argc, char **argv) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
//TODO: _WIN32
|
||||
#ifndef _WIN32
|
||||
/* This AOF file may have an RDB preamble. Check this to start, and if this
|
||||
* is the case, start processing the RDB part. */
|
||||
if (size >= 8) { /* There must be at least room for the RDB header. */
|
||||
char sig[5];
|
||||
int has_preamble = fread(sig,sizeof(sig),1,fp) == 1 &&
|
||||
memcmp(sig,"REDIS",sizeof(sig)) == 0;
|
||||
int has_preamble = fread(sig, sizeof(sig), 1, fp) == 1 &&
|
||||
memcmp(sig, "REDIS", sizeof(sig)) == 0;
|
||||
rewind(fp);
|
||||
if (has_preamble) {
|
||||
printf("The AOF appears to start with an RDB preamble.\n"
|
||||
"Checking the RDB preamble to start:\n");
|
||||
if (redis_check_rdb_main(argc,argv,fp) == C_ERR) {
|
||||
"Checking the RDB preamble to start:\n");
|
||||
if (redis_check_rdb_main(argc, argv, fp) == C_ERR) {
|
||||
printf("RDB preamble of AOF file is not sane, aborting.\n");
|
||||
exit(1);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
printf("RDB preamble is OK, proceeding with AOF tail...\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
off_t pos = process(fp);
|
||||
off_t diff = size-pos;
|
||||
off_t diff = size - pos;
|
||||
printf("AOF analyzed: size=%lld, ok_up_to=%lld, diff=%lld\n",
|
||||
(PORT_LONGLONG) size, (PORT_LONGLONG) pos, (PORT_LONGLONG) diff);
|
||||
if (diff > 0) {
|
||||
if (fix) {
|
||||
char buf[2];
|
||||
printf("This will shrink the AOF from %lld bytes, with %lld bytes, to %lld bytes\n",(PORT_LONGLONG)size,(PORT_LONGLONG)diff,(PORT_LONGLONG)pos);
|
||||
printf("This will shrink the AOF from %lld bytes, with %lld bytes, to %lld bytes\n", (PORT_LONGLONG) size, (PORT_LONGLONG) diff, (PORT_LONGLONG) pos);
|
||||
printf("Continue? [y/N]: ");
|
||||
if (fgets(buf,sizeof(buf),stdin) == NULL ||
|
||||
strncasecmp(buf,"y",1) != 0) {
|
||||
printf("Aborting...\n");
|
||||
exit(1);
|
||||
if (fgets(buf, sizeof(buf), stdin) == NULL ||
|
||||
strncasecmp(buf, "y", 1) != 0) {
|
||||
printf("Aborting...\n");
|
||||
exit(1);
|
||||
}
|
||||
if (ftruncate(fileno(fp), pos) == -1) {
|
||||
printf("Failed to truncate AOF\n");
|
||||
exit(1);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
printf("Successfully truncated AOF\n");
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
printf("AOF is not valid. "
|
||||
"Use the --fix option to try fixing it.\n");
|
||||
"Use the --fix option to try fixing it.\n");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
printf("AOF is valid\n");
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _WIN32_REDIS_CHECK_AOF_EXE
|
||||
int main(int argc, char **argv) {
|
||||
return redis_check_aof_main(argc, argv);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
+50
-42
@@ -29,6 +29,11 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Win32_Interop/Win32_Portability.h"
|
||||
#ifdef _WIN32_REDIS_CHECK_RDB_EXE
|
||||
#include "Win32_Interop/Win32_FDAPI.h"
|
||||
#include "Win32_Interop/Win32_ThreadControl.h"
|
||||
#include "Win32_Interop/Win32_QFork.h"
|
||||
#endif
|
||||
#include "Win32_Interop/win32_types.h"
|
||||
#include "Win32_Interop/Win32_Error.h"
|
||||
#include "Win32_Interop/win32fixes.h"
|
||||
@@ -48,10 +53,10 @@ struct {
|
||||
rio *rio;
|
||||
robj *key; /* Current key we are reading. */
|
||||
int key_type; /* Current key type if != -1. */
|
||||
unsigned long keys; /* Number of keys processed. */
|
||||
unsigned long expires; /* Number of keys with an expire. */
|
||||
unsigned long already_expired; /* Number of keys already expired. */
|
||||
int doing; /* The state while reading the RDB. */
|
||||
PORT_ULONG keys; /* Number of keys processed. */ WIN_PORT_FIX
|
||||
PORT_ULONG expires; /* Number of keys with an expire. */ WIN_PORT_FIX
|
||||
PORT_ULONG already_expired; /* Number of keys already expired. */ WIN_PORT_FIX
|
||||
int doing; /* The state while reading the RDB. */
|
||||
int error_set; /* True if error is populated. */
|
||||
char error[1024];
|
||||
} rdbstate;
|
||||
@@ -98,8 +103,8 @@ char *rdb_type_string[] = {
|
||||
/* Show a few stats collected into 'rdbstate' */
|
||||
void rdbShowGenericInfo(void) {
|
||||
printf("[info] %Iu keys read\n", rdbstate.keys); WIN_PORT_FIX /* %lu -> %Iu */
|
||||
printf("[info] %Iu expires\n", rdbstate.expires); WIN_PORT_FIX /* %lu -> %Iu */
|
||||
printf("[info] %Iu already expired\n", rdbstate.already_expired); WIN_PORT_FIX /* %lu -> %Iu */
|
||||
printf("[info] %Iu expires\n", rdbstate.expires); WIN_PORT_FIX /* %lu -> %Iu */
|
||||
printf("[info] %Iu already expired\n", rdbstate.already_expired); WIN_PORT_FIX /* %lu -> %Iu */
|
||||
}
|
||||
|
||||
/* Called on RDB errors. Provides details about the RDB and the offset
|
||||
@@ -114,19 +119,19 @@ void rdbCheckError(const char *fmt, ...) {
|
||||
|
||||
printf("--- RDB ERROR DETECTED ---\n");
|
||||
printf("[offset %llu] %s\n",
|
||||
(unsigned long long) (rdbstate.rio ?
|
||||
(PORT_ULONGLONG) (rdbstate.rio ? WIN_PORT_FIX
|
||||
rdbstate.rio->processed_bytes : 0), msg);
|
||||
printf("[additional info] While doing: %s\n",
|
||||
rdb_check_doing_string[rdbstate.doing]);
|
||||
if (rdbstate.key)
|
||||
printf("[additional info] Reading key '%s'\n",
|
||||
(char*)rdbstate.key->ptr);
|
||||
(char*) rdbstate.key->ptr);
|
||||
if (rdbstate.key_type != -1)
|
||||
printf("[additional info] Reading type %d (%s)\n",
|
||||
rdbstate.key_type,
|
||||
((unsigned)rdbstate.key_type <
|
||||
sizeof(rdb_type_string)/sizeof(char*)) ?
|
||||
rdb_type_string[rdbstate.key_type] : "unknown");
|
||||
((unsigned) rdbstate.key_type <
|
||||
sizeof(rdb_type_string) / sizeof(char*)) ?
|
||||
rdb_type_string[rdbstate.key_type] : "unknown");
|
||||
rdbShowGenericInfo();
|
||||
}
|
||||
|
||||
@@ -140,7 +145,7 @@ void rdbCheckInfo(const char *fmt, ...) {
|
||||
va_end(ap);
|
||||
|
||||
printf("[offset %llu] %s\n",
|
||||
(unsigned long long) (rdbstate.rio ?
|
||||
(PORT_ULONGLONG) (rdbstate.rio ?
|
||||
rdbstate.rio->processed_bytes : 0), msg);
|
||||
}
|
||||
|
||||
@@ -193,25 +198,25 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
|
||||
static rio rdb; /* Pointed by global struct riostate. */
|
||||
|
||||
int closefile = (fp == NULL);
|
||||
if (fp == NULL && (fp = fopen(rdbfilename,IF_WIN32("rb","r"))) == NULL) return 1;
|
||||
if (fp == NULL && (fp = fopen(rdbfilename, IF_WIN32("rb", "r"))) == NULL) return 1;
|
||||
|
||||
rioInitWithFile(&rdb,fp);
|
||||
rioInitWithFile(&rdb, fp);
|
||||
rdbstate.rio = &rdb;
|
||||
rdb.update_cksum = rdbLoadProgressCallback;
|
||||
if (rioRead(&rdb,buf,9) == 0) goto eoferr;
|
||||
if (rioRead(&rdb, buf, 9) == 0) goto eoferr;
|
||||
buf[9] = '\0';
|
||||
if (memcmp(buf,"REDIS",5) != 0) {
|
||||
if (memcmp(buf, "REDIS", 5) != 0) {
|
||||
rdbCheckError("Wrong signature trying to load DB from file");
|
||||
return 1;
|
||||
}
|
||||
rdbver = atoi(buf+5);
|
||||
rdbver = atoi(buf + 5);
|
||||
if (rdbver < 1 || rdbver > RDB_VERSION) {
|
||||
rdbCheckError("Can't handle RDB format version %d",rdbver);
|
||||
rdbCheckError("Can't handle RDB format version %d", rdbver);
|
||||
return 1;
|
||||
}
|
||||
|
||||
startLoading(fp);
|
||||
while(1) {
|
||||
while (1) {
|
||||
robj *key, *val;
|
||||
expiretime = -1;
|
||||
|
||||
@@ -232,7 +237,8 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
|
||||
/* the EXPIRETIME opcode specifies time in seconds, so convert
|
||||
* into milliseconds. */
|
||||
expiretime *= 1000;
|
||||
} else if (type == RDB_OPCODE_EXPIRETIME_MS) {
|
||||
}
|
||||
else if (type == RDB_OPCODE_EXPIRETIME_MS) {
|
||||
/* EXPIRETIME_MS: milliseconds precision expire times introduced
|
||||
* with RDB v3. Like EXPIRETIME but no with more precision. */
|
||||
rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE;
|
||||
@@ -240,27 +246,31 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
|
||||
/* We read the time so we need to read the object type again. */
|
||||
rdbstate.doing = RDB_CHECK_DOING_READ_TYPE;
|
||||
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
|
||||
} else if (type == RDB_OPCODE_EOF) {
|
||||
}
|
||||
else if (type == RDB_OPCODE_EOF) {
|
||||
/* EOF: End of file, exit the main loop. */
|
||||
break;
|
||||
} else if (type == RDB_OPCODE_SELECTDB) {
|
||||
}
|
||||
else if (type == RDB_OPCODE_SELECTDB) {
|
||||
/* SELECTDB: Select the specified database. */
|
||||
rdbstate.doing = RDB_CHECK_DOING_READ_LEN;
|
||||
if ((dbid = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
|
||||
if ((dbid = rdbLoadLen(&rdb, NULL)) == RDB_LENERR)
|
||||
goto eoferr;
|
||||
rdbCheckInfo("Selecting DB ID %d", dbid);
|
||||
continue; /* Read type again. */
|
||||
} else if (type == RDB_OPCODE_RESIZEDB) {
|
||||
}
|
||||
else if (type == RDB_OPCODE_RESIZEDB) {
|
||||
/* RESIZEDB: Hint about the size of the keys in the currently
|
||||
* selected data base, in order to avoid useless rehashing. */
|
||||
uint64_t db_size, expires_size;
|
||||
rdbstate.doing = RDB_CHECK_DOING_READ_LEN;
|
||||
if ((db_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
|
||||
if ((db_size = rdbLoadLen(&rdb, NULL)) == RDB_LENERR)
|
||||
goto eoferr;
|
||||
if ((expires_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
|
||||
if ((expires_size = rdbLoadLen(&rdb, NULL)) == RDB_LENERR)
|
||||
goto eoferr;
|
||||
continue; /* Read type again. */
|
||||
} else if (type == RDB_OPCODE_AUX) {
|
||||
}
|
||||
else if (type == RDB_OPCODE_AUX) {
|
||||
/* AUX: generic string-string fields. Use to add state to RDB
|
||||
* which is backward compatible. Implementations of RDB loading
|
||||
* are requierd to skip AUX fields they don't understand.
|
||||
@@ -272,11 +282,12 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
|
||||
if ((auxval = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
|
||||
|
||||
rdbCheckInfo("AUX FIELD %s = '%s'",
|
||||
(char*)auxkey->ptr, (char*)auxval->ptr);
|
||||
(char*) auxkey->ptr, (char*) auxval->ptr);
|
||||
decrRefCount(auxkey);
|
||||
decrRefCount(auxval);
|
||||
continue; /* Read type again. */
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (!rdbIsObjectType(type)) {
|
||||
rdbCheckError("Invalid object type: %d", type);
|
||||
return 1;
|
||||
@@ -291,7 +302,7 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
|
||||
rdbstate.keys++;
|
||||
/* Read value */
|
||||
rdbstate.doing = RDB_CHECK_DOING_READ_OBJECT_VALUE;
|
||||
if ((val = rdbLoadObject(type,&rdb)) == NULL) goto eoferr;
|
||||
if ((val = rdbLoadObject(type, &rdb)) == NULL) goto eoferr;
|
||||
/* Check if the key already expired. This function is used when loading
|
||||
* an RDB file from disk, either at startup, or when an RDB was
|
||||
* received from the master. In the latter case, the master is
|
||||
@@ -310,13 +321,15 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
|
||||
uint64_t cksum, expected = rdb.cksum;
|
||||
|
||||
rdbstate.doing = RDB_CHECK_DOING_CHECK_SUM;
|
||||
if (rioRead(&rdb,&cksum,8) == 0) goto eoferr;
|
||||
if (rioRead(&rdb, &cksum, 8) == 0) goto eoferr;
|
||||
memrev64ifbe(&cksum);
|
||||
if (cksum == 0) {
|
||||
rdbCheckInfo("RDB file was saved with checksum disabled: no check performed.");
|
||||
} else if (cksum != expected) {
|
||||
}
|
||||
else if (cksum != expected) {
|
||||
rdbCheckError("RDB CRC error");
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
rdbCheckInfo("Checksum OK");
|
||||
}
|
||||
}
|
||||
@@ -327,7 +340,8 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
|
||||
eoferr: /* unexpected end of file is handled here with a fatal exit */
|
||||
if (rdbstate.error_set) {
|
||||
rdbCheckError(rdbstate.error);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
rdbCheckError("Unexpected EOF reading RDB file");
|
||||
}
|
||||
return 1;
|
||||
@@ -359,17 +373,11 @@ int redis_check_rdb_main(int argc, char **argv, FILE *fp) {
|
||||
rdbCheckMode = 1;
|
||||
rdbCheckInfo("Checking RDB file %s", argv[1]);
|
||||
POSIX_ONLY(rdbCheckSetupSignals();)
|
||||
int retval = redis_check_rdb(argv[1],fp);
|
||||
int retval = redis_check_rdb(argv[1], fp);
|
||||
if (retval == 0) {
|
||||
rdbCheckInfo("\\o/ RDB looks OK! \\o/");
|
||||
rdbShowGenericInfo();
|
||||
}
|
||||
if (fp) return (retval == 0) ? C_OK : C_ERR;
|
||||
exit(retval);
|
||||
}
|
||||
|
||||
#ifdef _WIN32_REDIS_CHECK_RDB_EXE
|
||||
int main(int argc, char **argv) {
|
||||
return redis_check_rdb_main(argc, argv, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
+690
-629
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user