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;
+}