home *** CD-ROM | disk | FTP | other *** search
- /*++
-
- Copyright (c) 1994-1997 Microsoft Corporation
-
- Module Name:
-
- unbufcp1.c
-
- Abstract:
-
- This single-threaded version shows how to multiplex I/O to a number of files with
- a single thread. This is the most efficient mechanism if you do not need the
- asynchronous completion model that the dual-threaded version offers.
-
- Only one thread and one I/O completion port is used. The file handles for the
- source and destination file are both associated with the same port. The
- thread starts off by posting a number of overlapped
- reads from the source file. It then waits on the I/O completion port.
- Whenever a read completes, it immediately turns it around into a write to
- the destination file. Whenever a write completes, it immediately posts the
- next read from the source file.
-
- Thread 1
- |
- |
- kick off a few
- overlapped reads
- |
- |
- ->GetQueuedCompletionStatus(WritePort) <-----------
- | | |
- | |------------------------------- |
- | | | |
- | write has completed, read has completed, |
- | kick off another kick off the write. |
- | read | |
- | | | |
- |____| |_____________|
-
- Author:
-
- John Vert (jvert) 21-Dec-1994
-
- Revision History:
-
- 24-Apr-1995 Brian Sherrell (briansh) - Added platform detection &
- I/O Completion Port creation with INVALID_HANDLE_VALUE.
-
- 22-March-1996 Brian Sherrell (briansh) - Pending I/O bug fix.
-
- 3-Apr-1996 Brian Sherrell (briansh) - Correct versioning logic.
-
- --*/
- #include <windows.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <ctype.h>
-
- //
- // File handles for the copy operation. All read operations are
- // from SourceFile. All write operations are to DestFile.
- //
- HANDLE SourceFile;
- HANDLE DestFile;
-
- //
- //version information
- //
- OSVERSIONINFO ver;
-
- //
- // I/O completion port. All read and writes to the files complete
- // to this port.
- //
- HANDLE IoPort;
-
- //
- // Key values used to determine whether a read or a write
- // has completed.
- //
- #define ReadKey 0
- #define WriteKey 1
-
- //
- // Structure used to track each outstanding I/O. The maximum
- // number of I/Os that will be outstanding at any time is
- // controllable by the MAX_CONCURRENT_IO definition.
- //
-
- #define MAX_CONCURRENT_IO 20
-
- typedef struct _COPY_CHUNK {
- OVERLAPPED Overlapped;
- LPVOID Buffer;
- } COPY_CHUNK, *PCOPY_CHUNK;
-
- COPY_CHUNK CopyChunk[MAX_CONCURRENT_IO];
-
- //
- // Define the size of the buffers used to do the I/O.
- // 64K is a nice number.
- //
- #define BUFFER_SIZE (64*1024)
-
- //
- // The system's page size will always be a multiple of the
- // sector size. Do all I/Os in page-size chunks.
- //
- DWORD PageSize;
-
-
- //
- // Local function prototype
- //
- VOID
- CopyLoop(
- ULARGE_INTEGER FileSize
- );
-
- int
- _CRTAPI1
- main(
- int argc,
- char *argv[],
- char *envp
- )
- {
- ULARGE_INTEGER FileSize;
- ULARGE_INTEGER InitialSize;
- BOOL Success;
- DWORD Status;
- DWORD StartTime, EndTime;
- SYSTEM_INFO SystemInfo;
- HANDLE BufferedHandle;
-
- if (argc != 3) {
- fprintf(stderr, "Usage: %s SourceFile DestinationFile\n", argv[0]);
- exit(1);
- }
-
- //
- //confirm we are running on Windows NT 3.5 or greater, if not, display notice and
- //terminate. Completion ports are only supported on Win32 & Win32s. Creating a
- //Completion port with no handle specified is only supported on NT 3.51, so we need
- //to know what we're running on. Note, Win32s does not support console apps, thats
- //why we exit here if we are not on Windows NT.
- //
- //
- //ver.dwOSVersionInfoSize needs to be set before calling GetVersionInfoEx()
- //
- ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
-
- //
- //Failure here could mean several things 1. On an NT system,
- //it indicates NT version 3.1 because GetVersionEx() is only
- //implemented on NT 3.5. 2. On Windows 3.1 system, it means
- //either Win32s version 1.1 or 1.0 is installed.
- //
- Success = GetVersionEx((LPOSVERSIONINFO) &ver);
-
- if ( (!Success) || //GetVersionEx() failed - see above.
- (ver.dwPlatformId != VER_PLATFORM_WIN32_NT) ) //GetVersionEx() succeeded but we are not on NT.
- {
- MessageBox(NULL,
- "This sample application can only be run on Windows NT. 3.5 or greater\n"
- "This application will now terminate.",
- "UnBufCp1",
- MB_OK |
- MB_ICONSTOP |
- MB_SETFOREGROUND );
- exit( 1 );
- }
-
-
- //
- // Get the system's page size.
- //
- GetSystemInfo(&SystemInfo);
- PageSize = SystemInfo.dwPageSize;
-
- //
- // Open the source file and create the destination file.
- // Use FILE_FLAG_NO_BUFFERING to avoid polluting the
- // system cache with two copies of the same data.
- //
-
- SourceFile = CreateFile(argv[1],
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
- NULL);
- if (SourceFile == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "failed to open %s, error %d\n", argv[1], GetLastError());
- exit(1);
- }
- FileSize.LowPart = GetFileSize(SourceFile, &FileSize.HighPart);
- if ((FileSize.LowPart == 0xffffffff) && (GetLastError() != NO_ERROR)) {
- fprintf(stderr, "GetFileSize failed, error %d\n", GetLastError());
- exit(1);
- }
-
- DestFile = CreateFile(argv[2],
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- CREATE_ALWAYS,
- FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
- SourceFile);
- if (DestFile == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "failed to open %s, error %d\n", argv[2], GetLastError());
- exit(1);
- }
-
- //
- // Extend the destination file so that the filesystem does not
- // turn our asynchronous writes into synchronous ones.
- //
- InitialSize.QuadPart = (FileSize.QuadPart + PageSize - 1) & ~(PageSize-1);
- Status = SetFilePointer(DestFile,
- InitialSize.LowPart,
- (PLONG)&InitialSize.HighPart,
- FILE_BEGIN);
- if ((Status == 0xffffffff) && (GetLastError() != NO_ERROR)) {
- fprintf(stderr, "initial SetFilePointer failed, error %d\n", GetLastError());
- exit(1);
- }
- Success = SetEndOfFile(DestFile);
- if (!Success) {
- fprintf(stderr, "SetEndOfFile failed, error %d\n", GetLastError());
- exit(1);
- }
-
- //
- //In NT 3.51 it is not necessary to specify the FileHandle parameter
- //of CreateIoCompletionPort()--It is legal to specify the FileHandle
- //as INVALID_HANDLE_VALUE. However, for NT 3.5 an overlapped file
- //handle is needed.
- //
- //We know already that we are running on NT, or else we wouldn't have
- //gotten this far, so lets see what version we are running on.
- //
- if (ver.dwMajorVersion == 3 && ver.dwMinorVersion == 50) //we're running on NT 3.5 - Completion Ports exists.
- IoPort = CreateIoCompletionPort(SourceFile, //file handle to associate with I/O completion port.
- NULL, //optional handle to existing I/O completion port.
- ReadKey, //completion key.
- 1); //# of threads allowed to execute concurrently.
- else
- {
- // //we are running on NT 3.51 or greater
- //Create the I/O Completion Port
- //
- IoPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,//file handle to associate with I/O completion port
- NULL, //optional handle to existing I/O completion port
- ReadKey, //completion key
- 1); //# of threads allowed to execute concurrently
-
-
- if (IoPort == NULL) {
- fprintf(stderr, "failed to create ReadPort, error %d\n", GetLastError());
- exit(1);
- }
-
- //
- //If we need to, aka we're running on NT 3.51, let's associate a file handle with the
- //completion port.
- //
-
- IoPort = CreateIoCompletionPort(SourceFile,
- IoPort,
- ReadKey,
- 1);
-
- if (IoPort == NULL)
- {
- fprintf(stderr,
- "failed to create IoPort, error %d\n",
- GetLastError());
-
- exit(1);
-
- }
- }
-
- //
- // Associate the destination file handle with the
- // I/O completion port.
- //
-
- IoPort = CreateIoCompletionPort(DestFile,
- IoPort,
- WriteKey,
- 1);
- if (IoPort == NULL) {
- fprintf(stderr, "failed to create WritePort, error %d\n", GetLastError());
- exit(1);
- }
-
-
-
- StartTime = GetTickCount();
-
- //
- // Do the copy
- //
- CopyLoop(FileSize);
-
- EndTime = GetTickCount();
-
- CloseHandle(SourceFile);
- CloseHandle(DestFile);
-
- //
- // We need another handle to the destination file that is
- // opened without FILE_FLAG_NO_BUFFERING. This allows us to set
- // the end-of-file marker to a position that is not sector-aligned.
- //
- BufferedHandle = CreateFile(argv[2],
- GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- 0,
- NULL);
- if (BufferedHandle == INVALID_HANDLE_VALUE) {
- fprintf(stderr,
- "failed to open buffered handle to %s, error %d\n",
- argv[2],
- GetLastError());
- exit(1);
- }
-
-
- //
- // Set the destination's file size to the size of the
- // source file, in case the size of the source file was
- // not a multiple of the page size.
- //
- Status = SetFilePointer(BufferedHandle,
- FileSize.LowPart,
- (PLONG)&FileSize.HighPart,
- FILE_BEGIN);
- if ((Status == 0xffffffff) && (GetLastError() != NO_ERROR)) {
- fprintf(stderr, "final SetFilePointer failed, error %d\n", GetLastError());
- exit(1);
- }
- Success = SetEndOfFile(BufferedHandle);
- if (!Success) {
- fprintf(stderr, "SetEndOfFile failed, error %d\n", GetLastError());
- exit(1);
- }
- CloseHandle(BufferedHandle);
-
- printf("%d bytes copied in %.3f seconds\n",
- FileSize.LowPart,
- (float)(EndTime-StartTime)/1000.0);
- printf("%.2f MB/sec\n",
- ((LONGLONG)FileSize.QuadPart/(1024.0*1024.0)) / (((float)(EndTime-StartTime)) / 1000.0));
-
- return(0);
-
- }
-
- VOID
- CopyLoop(
- ULARGE_INTEGER FileSize
- )
- {
- ULARGE_INTEGER ReadPointer;
- BOOL Success;
- DWORD NumberBytes;
- LPOVERLAPPED CompletedOverlapped;
- DWORD Key;
- PCOPY_CHUNK Chunk;
- int PendingIO = 0;
- int i;
-
- //
- // Start reading the file. Kick off MAX_CONCURRENT_IO reads, then just
- // loop waiting for I/O to complete.
- //
- ReadPointer.QuadPart = 0;
-
- for (i=0; i < MAX_CONCURRENT_IO; i++) {
- if (ReadPointer.QuadPart >= FileSize.QuadPart) {
- break;
- }
- //
- // Use VirtualAlloc so we get a page-aligned buffer suitable
- // for unbuffered I/O.
- //
- CopyChunk[i].Buffer = VirtualAlloc(NULL,
- BUFFER_SIZE,
- MEM_COMMIT,
- PAGE_READWRITE);
- if (CopyChunk[i].Buffer == NULL) {
- fprintf(stderr, "VirtualAlloc %d failed, error %d\n",i, GetLastError());
- exit(1);
- }
- CopyChunk[i].Overlapped.Offset = ReadPointer.LowPart;
- CopyChunk[i].Overlapped.OffsetHigh = ReadPointer.HighPart;
- CopyChunk[i].Overlapped.hEvent = NULL; // not needed
-
- Success = ReadFile(SourceFile,
- CopyChunk[i].Buffer,
- BUFFER_SIZE,
- &NumberBytes,
- &CopyChunk[i].Overlapped);
-
- if (!Success && (GetLastError() != ERROR_IO_PENDING)) {
- fprintf(stderr,
- "ReadFile at %lx failed, error %d\n",
- ReadPointer.LowPart,
- GetLastError());
- } else {
- ReadPointer.QuadPart += BUFFER_SIZE;
- ++PendingIO;
- }
- }
-
- //
- // We have started the initial async. reads, enter the main loop.
- // This simply waits until an I/O completes, then issues the next
- // I/O. When a write completes, the next read is issued. When a
- // read completes, the corresponding write is issued.
- //
- while (PendingIO) {
- Success = GetQueuedCompletionStatus(IoPort,
- &NumberBytes,
- &Key,
- &CompletedOverlapped,
- (DWORD)-1);
- if (!Success && (CompletedOverlapped == NULL)) {
- //
- // The call has failed.
- //
- fprintf(stderr,
- "GetQueuedCompletionStatus on the IoPort failed, error %d\n",
- GetLastError());
- exit(1);
- }
- if (!Success) {
- //
- // The call has succeeded, but the initial I/O operation
- // has failed.
- //
- fprintf(stderr,
- "GetQueuedCompletionStatus on the IoPort removed a failed\n"
- "I/O packet, error %d\n",GetLastError());
- //
- // This is probably bad, but what the heck, it's only a demo program,
- // so proceed blindly ahead.
- //
- }
-
- Chunk = (PCOPY_CHUNK)CompletedOverlapped;
-
- if (Key == ReadKey) {
-
- //
- // A read has completed, issue the corresponding write.
- //
-
- //
- // Round the number of bytes to write up to a sector boundary.
- //
- NumberBytes = (NumberBytes + PageSize - 1) & ~(PageSize-1);
-
- Success = WriteFile(DestFile,
- Chunk->Buffer,
- NumberBytes,
- &NumberBytes,
- &Chunk->Overlapped);
-
- if (!Success && (GetLastError() != ERROR_IO_PENDING))
- fprintf(stderr,
- "WriteFile at %lx failed, error %d\n",
- Chunk->Overlapped.Offset,
- GetLastError());
-
- //
- // decrement pending I/O count
- //
- --PendingIO;
-
- } else if (Key == WriteKey) {
-
- //
- // A write has completed, issue the next read.
- //
-
- if (ReadPointer.QuadPart < FileSize.QuadPart) {
- Chunk->Overlapped.Offset = ReadPointer.LowPart;
- Chunk->Overlapped.OffsetHigh = ReadPointer.HighPart;
- ReadPointer.QuadPart += BUFFER_SIZE;
- Success = ReadFile(SourceFile,
- Chunk->Buffer,
- NumberBytes,
- &NumberBytes,
- &Chunk->Overlapped);
- if (!Success && (GetLastError() != ERROR_IO_PENDING)) {
- fprintf(stderr,
- "ReadFile at %lx failed, error %d\n",
- Chunk->Overlapped.Offset,
- GetLastError());
- }
- } else {
- //
- // There are no more reads left to issue, just
- // wait for the pending writes to drain.
- //
- --PendingIO;
- }
- }
- }
- }
-