From 32be705e4d82062fa9c34028121cd25631180919 Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Wed, 2 Mar 2011 12:50:34 -0500 Subject: [PATCH] test: asio.exe for async reads and writes Signed-off-by: Casey Bodley --- build.vc10/asio.vcxproj | 138 ++++++++++++++ build.vc10/asio.vcxproj.filters | 22 +++ build.vc10/ms-nfs41-client.sln | 10 + tests/asio/asio.c | 311 ++++++++++++++++++++++++++++++++ 4 files changed, 481 insertions(+) create mode 100644 build.vc10/asio.vcxproj create mode 100644 build.vc10/asio.vcxproj.filters create mode 100644 tests/asio/asio.c diff --git a/build.vc10/asio.vcxproj b/build.vc10/asio.vcxproj new file mode 100644 index 0000000..1541da8 --- /dev/null +++ b/build.vc10/asio.vcxproj @@ -0,0 +1,138 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {F08DE7DF-7B11-4ECF-9E6E-DD8C7DB40B27} + asio + + + + Application + true + MultiByte + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\ + + + $(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName)\ + + + + Level3 + Disabled + + + true + + + + + Level3 + Disabled + + + true + + + + + Level3 + MaxSpeed + true + true + + + true + true + true + + + + + Level3 + MaxSpeed + true + true + + + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/build.vc10/asio.vcxproj.filters b/build.vc10/asio.vcxproj.filters new file mode 100644 index 0000000..4d39551 --- /dev/null +++ b/build.vc10/asio.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/build.vc10/ms-nfs41-client.sln b/build.vc10/ms-nfs41-client.sln index e8cef65..a93f4d2 100644 --- a/build.vc10/ms-nfs41-client.sln +++ b/build.vc10/ms-nfs41-client.sln @@ -13,6 +13,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtirpc", "libtirpc.vcxpro EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nfs_install", "nfs_install.vcxproj", "{A453DC17-BE6B-4271-A020-66E054AB5908}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "asio", "asio.vcxproj", "{F08DE7DF-7B11-4ECF-9E6E-DD8C7DB40B27}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution 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|x64.ActiveCfg = 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 GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tests/asio/asio.c b/tests/asio/asio.c new file mode 100644 index 0000000..178079d --- /dev/null +++ b/tests/asio/asio.c @@ -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 +#include +#include +#include + + +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 \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; +}