test: asio.exe for async reads and writes
Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
This commit is contained in:
parent
ce200a4c2d
commit
32be705e4d
4 changed files with 481 additions and 0 deletions
138
build.vc10/asio.vcxproj
Normal file
138
build.vc10/asio.vcxproj
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="4.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>{F08DE7DF-7B11-4ECF-9E6E-DD8C7DB40B27}</ProjectGuid>
|
||||||
|
<RootNamespace>asio</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</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 Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<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>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\tests\asio\asio.c" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
22
build.vc10/asio.vcxproj.filters
Normal file
22
build.vc10/asio.vcxproj.filters
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Resource Files">
|
||||||
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\tests\asio\asio.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
|
@ -13,6 +13,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtirpc", "libtirpc.vcxpro
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nfs_install", "nfs_install.vcxproj", "{A453DC17-BE6B-4271-A020-66E054AB5908}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nfs_install", "nfs_install.vcxproj", "{A453DC17-BE6B-4271-A020-66E054AB5908}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "asio", "asio.vcxproj", "{F08DE7DF-7B11-4ECF-9E6E-DD8C7DB40B27}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Win32 = Debug|Win32
|
Debug|Win32 = Debug|Win32
|
||||||
|
|
@ -69,6 +71,14 @@ Global
|
||||||
{A453DC17-BE6B-4271-A020-66E054AB5908}.Release|Win32.Build.0 = Release|Win32
|
{A453DC17-BE6B-4271-A020-66E054AB5908}.Release|Win32.Build.0 = Release|Win32
|
||||||
{A453DC17-BE6B-4271-A020-66E054AB5908}.Release|x64.ActiveCfg = Release|x64
|
{A453DC17-BE6B-4271-A020-66E054AB5908}.Release|x64.ActiveCfg = Release|x64
|
||||||
{A453DC17-BE6B-4271-A020-66E054AB5908}.Release|x64.Build.0 = Release|x64
|
{A453DC17-BE6B-4271-A020-66E054AB5908}.Release|x64.Build.0 = Release|x64
|
||||||
|
{F08DE7DF-7B11-4ECF-9E6E-DD8C7DB40B27}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
|
{F08DE7DF-7B11-4ECF-9E6E-DD8C7DB40B27}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
|
{F08DE7DF-7B11-4ECF-9E6E-DD8C7DB40B27}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{F08DE7DF-7B11-4ECF-9E6E-DD8C7DB40B27}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{F08DE7DF-7B11-4ECF-9E6E-DD8C7DB40B27}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
|
{F08DE7DF-7B11-4ECF-9E6E-DD8C7DB40B27}.Release|Win32.Build.0 = Release|Win32
|
||||||
|
{F08DE7DF-7B11-4ECF-9E6E-DD8C7DB40B27}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{F08DE7DF-7B11-4ECF-9E6E-DD8C7DB40B27}.Release|x64.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
||||||
311
tests/asio/asio.c
Normal file
311
tests/asio/asio.c
Normal file
|
|
@ -0,0 +1,311 @@
|
||||||
|
/* Copyright (c) 2010
|
||||||
|
* The Regents of the University of Michigan
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* Permission is granted to use, copy and redistribute this software
|
||||||
|
* for noncommercial education and research purposes, so long as no
|
||||||
|
* fee is charged, and so long as the name of the University of Michigan
|
||||||
|
* is not used in any advertising or publicity pertaining to the use
|
||||||
|
* or distribution of this software without specific, written prior
|
||||||
|
* authorization. Permission to modify or otherwise create derivative
|
||||||
|
* works of this software is not granted.
|
||||||
|
*
|
||||||
|
* This software is provided as is, without representation or warranty
|
||||||
|
* of any kind either express or implied, including without limitation
|
||||||
|
* the implied warranties of merchantability, fitness for a particular
|
||||||
|
* purpose, or noninfringement. The Regents of the University of
|
||||||
|
* Michigan shall not be liable for any damages, including special,
|
||||||
|
* indirect, incidental, or consequential damages, with respect to any
|
||||||
|
* claim arising out of or in connection with the use of the software,
|
||||||
|
* even if it has been or is hereafter advised of the possibility of
|
||||||
|
* such damages.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <tchar.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
|
||||||
|
static void PrintErrorMessage(
|
||||||
|
IN DWORD dwError)
|
||||||
|
{
|
||||||
|
LPTSTR lpMsgBuf = NULL;
|
||||||
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
|
||||||
|
NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPTSTR)&lpMsgBuf, 0, NULL);
|
||||||
|
_fputts(lpMsgBuf, stderr);
|
||||||
|
LocalFree(lpMsgBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SIZE_T parse_buffer_size(LPCTSTR str)
|
||||||
|
{
|
||||||
|
PTCHAR end;
|
||||||
|
SIZE_T value = _tcstoul(str, &end, 0);
|
||||||
|
|
||||||
|
if (value == ULONG_MAX && errno == ERANGE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (end) switch (*end) {
|
||||||
|
case 0: break;
|
||||||
|
case TEXT('g'): case TEXT('G'): value <<= 10;
|
||||||
|
case TEXT('m'): case TEXT('M'): value <<= 10;
|
||||||
|
case TEXT('k'): case TEXT('K'): value <<= 10; break;
|
||||||
|
default: value = 0; break;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD resize_file(
|
||||||
|
HANDLE file,
|
||||||
|
SIZE_T length)
|
||||||
|
{
|
||||||
|
LARGE_INTEGER pointer;
|
||||||
|
DWORD pos, status = NO_ERROR;
|
||||||
|
|
||||||
|
// move to the new eof
|
||||||
|
pointer.QuadPart = length;
|
||||||
|
pos = SetFilePointer(file, pointer.LowPart, &pointer.HighPart, FILE_BEGIN);
|
||||||
|
if (pos == INVALID_SET_FILE_POINTER) {
|
||||||
|
status = GetLastError();
|
||||||
|
if (status) {
|
||||||
|
_ftprintf(stderr, TEXT("SetFilePointer() failed to move to ")
|
||||||
|
TEXT("position %u with %d: "), length, status);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the new file size
|
||||||
|
if (!SetEndOfFile(file)) {
|
||||||
|
status = GetLastError();
|
||||||
|
_ftprintf(stderr, TEXT("SetEndOfFile() failed with %d: "), status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore the original file position
|
||||||
|
pos = SetFilePointer(file, 0, 0, FILE_BEGIN);
|
||||||
|
if (pos == INVALID_SET_FILE_POINTER) {
|
||||||
|
status = GetLastError();
|
||||||
|
_ftprintf(stderr, TEXT("SetFilePointer() failed to restore ")
|
||||||
|
TEXT("the original position with %d: "), status);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct async_job {
|
||||||
|
OVERLAPPED overlapped;
|
||||||
|
LPVOID buffer;
|
||||||
|
DWORD started;
|
||||||
|
DWORD status;
|
||||||
|
DWORD transferred;
|
||||||
|
};
|
||||||
|
|
||||||
|
// make asynchronous calls to WriteFile()
|
||||||
|
static DWORD jobs_write(
|
||||||
|
HANDLE file,
|
||||||
|
struct async_job *jobs,
|
||||||
|
DWORD chunk,
|
||||||
|
DWORD count)
|
||||||
|
{
|
||||||
|
DWORD i, status = NO_ERROR;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
// start the job
|
||||||
|
jobs[i].started = GetTickCount();
|
||||||
|
if (WriteFile(file, jobs[i].buffer, chunk, &jobs[i].transferred, &jobs[i].overlapped)) {
|
||||||
|
jobs[i].status = NO_ERROR;
|
||||||
|
_tprintf(TEXT("job %u wrote %u bytes synchronously in %u ticks\n"),
|
||||||
|
i+1, jobs[i].transferred, GetTickCount() - jobs[i].started);
|
||||||
|
} else {
|
||||||
|
jobs[i].status = GetLastError();
|
||||||
|
if (jobs[i].status != ERROR_IO_PENDING) {
|
||||||
|
status = jobs[i].status;
|
||||||
|
_ftprintf(stderr, TEXT("WriteFile() failed with %d: "), status);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tprintf(TEXT("started write job %u\n"), i+1);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make asynchronous calls to ReadFile()
|
||||||
|
static DWORD jobs_read(
|
||||||
|
HANDLE file,
|
||||||
|
struct async_job *jobs,
|
||||||
|
DWORD chunk,
|
||||||
|
DWORD count)
|
||||||
|
{
|
||||||
|
DWORD i, status = NO_ERROR;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
jobs[i].started = GetTickCount();
|
||||||
|
if (ReadFile(file, jobs[i].buffer, chunk, &jobs[i].transferred, &jobs[i].overlapped)) {
|
||||||
|
jobs[i].status = NO_ERROR;
|
||||||
|
_tprintf(TEXT("job %u read %u bytes synchronously in %u ticks\n"),
|
||||||
|
i+1, jobs[i].transferred, GetTickCount() - jobs[i].started);
|
||||||
|
} else {
|
||||||
|
jobs[i].status = GetLastError();
|
||||||
|
if (jobs[i].status != ERROR_IO_PENDING) {
|
||||||
|
status = jobs[i].status;
|
||||||
|
_ftprintf(stderr, TEXT("ReadFile() failed with %d: "), status);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tprintf(TEXT("started read job %u\n"), i+1);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// use GetOverlappedResult() to wait for all jobs to finish
|
||||||
|
static DWORD jobs_wait(
|
||||||
|
HANDLE file,
|
||||||
|
struct async_job *jobs,
|
||||||
|
DWORD count)
|
||||||
|
{
|
||||||
|
DWORD i, status = NO_ERROR;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
if (jobs[i].status != ERROR_IO_PENDING)
|
||||||
|
continue;
|
||||||
|
if (!GetOverlappedResult(file, &jobs[i].overlapped, &jobs[i].transferred, TRUE)) {
|
||||||
|
status = GetLastError();
|
||||||
|
_ftprintf(stderr, TEXT("GetOverlappedResult() failed with %d: "), status);
|
||||||
|
}
|
||||||
|
_tprintf(TEXT("job %u transferred %u bytes in %u ticks\n"),
|
||||||
|
i+1, jobs[i].transferred, GetTickCount() - jobs[i].started);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD jobs_run(
|
||||||
|
LPCTSTR filename,
|
||||||
|
PBYTE buffer,
|
||||||
|
SIZE_T length,
|
||||||
|
DWORD count)
|
||||||
|
{
|
||||||
|
HANDLE file;
|
||||||
|
struct async_job jobs[MAXIMUM_WAIT_OBJECTS] = { 0 };
|
||||||
|
const DWORD chunk = (DWORD)(length / count);
|
||||||
|
DWORD i, status = NO_ERROR;
|
||||||
|
|
||||||
|
// open or create the file; specify FILE_FLAG_OVERLAPPED for async io
|
||||||
|
file = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
|
||||||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL);
|
||||||
|
if (file == INVALID_HANDLE_VALUE) {
|
||||||
|
status = GetLastError();
|
||||||
|
_ftprintf(stderr, TEXT("CreateFile('%s') failed with %d: "), filename, status);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
switch (GetLastError()) {
|
||||||
|
case ERROR_ALREADY_EXISTS:
|
||||||
|
_ftprintf(stderr, TEXT("Opened existing file '%s'.\n"), filename);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_ftprintf(stderr, TEXT("Created file '%s'.\n"), filename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the file length first; writes that extend the file must be synchronous
|
||||||
|
status = resize_file(file, length);
|
||||||
|
if (status)
|
||||||
|
goto out_close;
|
||||||
|
|
||||||
|
// create a separate event for each job, or GetOverlappedResult() will mix them up
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
jobs[i].overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
|
if (jobs[i].overlapped.hEvent == INVALID_HANDLE_VALUE) {
|
||||||
|
status = GetLastError();
|
||||||
|
_ftprintf(stderr, TEXT("CreateEvent() failed with %d: "), status);
|
||||||
|
goto out_events;
|
||||||
|
}
|
||||||
|
jobs[i].buffer = buffer + i * chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start writes and wait for them to finish
|
||||||
|
status = jobs_write(file, jobs, chunk, count);
|
||||||
|
if (status) goto out_events;
|
||||||
|
status = jobs_wait(file, jobs, count);
|
||||||
|
if (status) goto out_events;
|
||||||
|
|
||||||
|
// start reads and wait for them to finish
|
||||||
|
status = jobs_read(file, jobs, chunk, count);
|
||||||
|
if (status) goto out_events;
|
||||||
|
status = jobs_wait(file, jobs, count);
|
||||||
|
if (status) goto out_events;
|
||||||
|
|
||||||
|
out_events:
|
||||||
|
// close the event handles
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
CloseHandle(jobs[i].overlapped.hEvent);
|
||||||
|
out_close:
|
||||||
|
CloseHandle(file);
|
||||||
|
out:
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BLOCK_SIZE 512
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
|
||||||
|
DWORD __cdecl _tmain(DWORD argc, LPTSTR argv[])
|
||||||
|
{
|
||||||
|
DWORD status = NO_ERROR;
|
||||||
|
LPCTSTR filename;
|
||||||
|
SIZE_T bytes;
|
||||||
|
DWORD count;
|
||||||
|
PBYTE buffer;
|
||||||
|
|
||||||
|
// parse the command line
|
||||||
|
if (argc < 4) {
|
||||||
|
_tprintf(TEXT("Usage: %s <filename> <bytes> <threads>\n"), argv[0]);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
filename = argv[1];
|
||||||
|
|
||||||
|
bytes = parse_buffer_size(argv[2]);
|
||||||
|
if (bytes == 0) {
|
||||||
|
_tprintf(TEXT("Invalid value for bytes: %s\n"), argv[2]);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
// must be a multiple of the page size for VirtualAlloc()
|
||||||
|
if (bytes % PAGE_SIZE) {
|
||||||
|
_tprintf(TEXT("bytes %llu must be a multiple of %u\n"), bytes, PAGE_SIZE);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = _ttoi(argv[3]);
|
||||||
|
if (count <= 0 || count > MAXIMUM_WAIT_OBJECTS) {
|
||||||
|
_tprintf(TEXT("Invalid value for threads: %s\n"), argv[3]);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
// chunk size must be a multiple of the block size for FILE_FLAG_NO_BUFFERING
|
||||||
|
if ((bytes / count) % BLOCK_SIZE) {
|
||||||
|
_tprintf(TEXT("io size (bytes/threads) %llu must be a multiple of %u\n"), bytes / count, BLOCK_SIZE);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (bytes/count > ULONG_MAX) {
|
||||||
|
_tprintf(TEXT("io size (bytes/threads) %llu must fit in 32 bits for WriteFile()\n"), bytes / count);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate the buffer; VirtualAlloc guarantees page alignment for FILE_FLAG_NO_BUFFERING
|
||||||
|
buffer = VirtualAlloc(NULL, bytes, MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
status = GetLastError();
|
||||||
|
_ftprintf(stderr, TEXT("VirtualAlloc(%llu) failed with %d: "), bytes, status);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = jobs_run(filename, buffer, bytes, count);
|
||||||
|
|
||||||
|
// free the buffer
|
||||||
|
if (!VirtualFree(buffer, 0, MEM_RELEASE)) {
|
||||||
|
status = GetLastError();
|
||||||
|
_ftprintf(stderr, TEXT("VirtualFree(%p) failed with %d: "), buffer, status);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
if (status) PrintErrorMessage(status);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue