home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / winbase / io / winnt / unbufcpy / unbufcp1.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  18KB  |  518 lines

  1. /*++
  2.  
  3. Copyright (c) 1994-1997  Microsoft Corporation
  4.  
  5. Module Name:
  6.  
  7.     unbufcp1.c
  8.  
  9. Abstract:
  10.  
  11.     This single-threaded version shows how to multiplex I/O to a number of files with 
  12.     a single thread. This is the most efficient mechanism if you do not need the 
  13.     asynchronous completion model that the dual-threaded version offers.
  14.  
  15.     Only one thread and one I/O completion port is used. The file handles for the
  16.     source and destination file are both associated with the same port.  The
  17.     thread starts off by posting a number of overlapped
  18.     reads from the source file.  It then waits on the I/O completion port.
  19.     Whenever a read completes, it immediately turns it around into a write to
  20.     the destination file. Whenever a write completes, it immediately posts the
  21.     next read from the source file.
  22.  
  23.     Thread 1
  24.        |
  25.        |
  26.     kick off a few
  27.     overlapped reads
  28.        |
  29.        |
  30.   ->GetQueuedCompletionStatus(WritePort) <-----------
  31.   |    |                                            |
  32.   |    |-------------------------------             |
  33.   |    |                              |             |
  34.   | write has completed,      read has completed,   |
  35.   | kick off another          kick off the write.   |
  36.   | read                              |             |
  37.   |    |                              |             |
  38.   |____|                              |_____________|
  39.  
  40. Author:
  41.  
  42.     John Vert (jvert) 21-Dec-1994
  43.  
  44. Revision History:
  45.   
  46.     24-Apr-1995       Brian Sherrell (briansh) - Added platform detection & 
  47.                       I/O Completion Port creation with INVALID_HANDLE_VALUE.
  48.  
  49.     22-March-1996     Brian Sherrell (briansh) - Pending I/O bug fix.
  50.  
  51.      3-Apr-1996       Brian Sherrell (briansh) - Correct versioning logic.
  52.  
  53. --*/
  54. #include <windows.h>
  55. #include <stdio.h>
  56. #include <stdlib.h>
  57. #include <ctype.h>
  58.  
  59. //
  60. // File handles for the copy operation. All read operations are
  61. // from SourceFile. All write operations are to DestFile.
  62. //
  63. HANDLE SourceFile;
  64. HANDLE DestFile;
  65.  
  66. //
  67. //version information
  68. //
  69. OSVERSIONINFO     ver;
  70.  
  71. //
  72. // I/O completion port. All read and writes to the files complete
  73. // to this port.
  74. //
  75. HANDLE IoPort;
  76.  
  77. //
  78. // Key values used to determine whether a read or a write
  79. // has completed.
  80. //
  81. #define ReadKey 0
  82. #define WriteKey 1
  83.  
  84. //
  85. // Structure used to track each outstanding I/O. The maximum
  86. // number of I/Os that will be outstanding at any time is
  87. // controllable by the MAX_CONCURRENT_IO definition.
  88. //
  89.  
  90. #define MAX_CONCURRENT_IO 20
  91.  
  92. typedef struct _COPY_CHUNK {
  93.     OVERLAPPED Overlapped;
  94.     LPVOID Buffer;
  95. } COPY_CHUNK, *PCOPY_CHUNK;
  96.  
  97. COPY_CHUNK CopyChunk[MAX_CONCURRENT_IO];
  98.  
  99. //
  100. // Define the size of the buffers used to do the I/O.
  101. // 64K is a nice number.
  102. //
  103. #define BUFFER_SIZE (64*1024)
  104.  
  105. //
  106. // The system's page size will always be a multiple of the
  107. // sector size. Do all I/Os in page-size chunks.
  108. //
  109. DWORD PageSize;
  110.  
  111.  
  112. //
  113. // Local function prototype
  114. //
  115. VOID
  116. CopyLoop(
  117.     ULARGE_INTEGER FileSize
  118.     );
  119.  
  120. int
  121. _CRTAPI1
  122. main(
  123.     int argc,
  124.     char *argv[],
  125.     char *envp
  126.     )
  127. {
  128.     ULARGE_INTEGER FileSize;
  129.     ULARGE_INTEGER InitialSize;
  130.     BOOL Success;
  131.     DWORD Status;
  132.     DWORD StartTime, EndTime;
  133.     SYSTEM_INFO SystemInfo;
  134.     HANDLE BufferedHandle;
  135.     
  136.     if (argc != 3) {
  137.         fprintf(stderr, "Usage: %s SourceFile DestinationFile\n", argv[0]);
  138.         exit(1);
  139.     }
  140.  
  141.     //
  142.     //confirm we are running on Windows NT 3.5 or greater, if not, display notice and 
  143.     //terminate.  Completion ports are only supported on Win32 & Win32s.  Creating a 
  144.     //Completion port with no handle specified is only supported on NT 3.51, so we need 
  145.     //to know what we're running on.  Note, Win32s does not support console apps, thats 
  146.     //why we exit here if we are not on Windows NT.
  147.     //
  148.     //
  149.     //ver.dwOSVersionInfoSize needs to be set before calling GetVersionInfoEx()
  150.     //
  151.     ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  152.  
  153.     //
  154.     //Failure here could mean several things 1. On an NT system, 
  155.     //it indicates NT version 3.1 because GetVersionEx() is only 
  156.     //implemented on NT 3.5.  2. On Windows 3.1 system, it means 
  157.     //either Win32s version 1.1 or 1.0 is installed.
  158.     //
  159.     Success = GetVersionEx((LPOSVERSIONINFO) &ver);
  160.  
  161.     if ( (!Success) ||                                   //GetVersionEx() failed - see above.
  162.          (ver.dwPlatformId != VER_PLATFORM_WIN32_NT) )   //GetVersionEx() succeeded but we are not on NT.
  163.       {
  164.        MessageBox(NULL,
  165.                   "This sample application can only be run on Windows NT. 3.5 or greater\n"
  166.                   "This application will now terminate.",
  167.                   "UnBufCp1",
  168.                   MB_OK           | 
  169.                   MB_ICONSTOP     | 
  170.                   MB_SETFOREGROUND );
  171.        exit( 1 );
  172.       }
  173.  
  174.  
  175.     //
  176.     // Get the system's page size.
  177.     //
  178.     GetSystemInfo(&SystemInfo);
  179.     PageSize = SystemInfo.dwPageSize;
  180.  
  181.     //
  182.     // Open the source file and create the destination file.
  183.     // Use FILE_FLAG_NO_BUFFERING to avoid polluting the
  184.     // system cache with two copies of the same data.
  185.     //
  186.  
  187.     SourceFile = CreateFile(argv[1],
  188.                             GENERIC_READ | GENERIC_WRITE,
  189.                             FILE_SHARE_READ,
  190.                             NULL,
  191.                             OPEN_EXISTING,
  192.                             FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
  193.                             NULL);
  194.     if (SourceFile == INVALID_HANDLE_VALUE) {
  195.         fprintf(stderr, "failed to open %s, error %d\n", argv[1], GetLastError());
  196.         exit(1);
  197.     }
  198.     FileSize.LowPart = GetFileSize(SourceFile, &FileSize.HighPart);
  199.     if ((FileSize.LowPart == 0xffffffff) && (GetLastError() != NO_ERROR)) {
  200.         fprintf(stderr, "GetFileSize failed, error %d\n", GetLastError());
  201.         exit(1);
  202.     }
  203.  
  204.     DestFile = CreateFile(argv[2],
  205.                           GENERIC_READ | GENERIC_WRITE,
  206.                           FILE_SHARE_READ | FILE_SHARE_WRITE,
  207.                           NULL,
  208.                           CREATE_ALWAYS,
  209.                           FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
  210.                           SourceFile);
  211.     if (DestFile == INVALID_HANDLE_VALUE) {
  212.         fprintf(stderr, "failed to open %s, error %d\n", argv[2], GetLastError());
  213.         exit(1);
  214.     }
  215.  
  216.     //
  217.     // Extend the destination file so that the filesystem does not
  218.     // turn our asynchronous writes into synchronous ones.
  219.     //
  220.     InitialSize.QuadPart = (FileSize.QuadPart + PageSize - 1) & ~(PageSize-1);
  221.     Status = SetFilePointer(DestFile,
  222.                             InitialSize.LowPart,
  223.                             (PLONG)&InitialSize.HighPart,
  224.                             FILE_BEGIN);
  225.     if ((Status == 0xffffffff) && (GetLastError() != NO_ERROR)) {
  226.         fprintf(stderr, "initial SetFilePointer failed, error %d\n", GetLastError());
  227.         exit(1);
  228.     }
  229.     Success = SetEndOfFile(DestFile);
  230.     if (!Success) {
  231.         fprintf(stderr, "SetEndOfFile failed, error %d\n", GetLastError());
  232.         exit(1);
  233.     }
  234.  
  235.     //
  236.     //In NT 3.51 it is not necessary to specify the FileHandle parameter
  237.     //of CreateIoCompletionPort()--It is legal to specify the FileHandle
  238.     //as INVALID_HANDLE_VALUE.  However, for NT 3.5 an overlapped file
  239.     //handle is needed.
  240.     //
  241.     //We know already that we are running on NT, or else we wouldn't have
  242.     //gotten this far, so lets see what version we are running on.
  243.     //
  244.     if (ver.dwMajorVersion == 3 && ver.dwMinorVersion == 50) //we're running on NT 3.5 - Completion Ports exists.
  245.       IoPort = CreateIoCompletionPort(SourceFile,            //file handle to associate with I/O completion port.                       
  246.                                       NULL,                  //optional handle to existing I/O completion port.
  247.                                       ReadKey,               //completion key.
  248.                                       1);                    //# of threads allowed to execute concurrently.
  249.     else                                                 
  250.         {
  251.           //                                                 //we are running on NT 3.51 or greater      
  252.           //Create the I/O Completion Port
  253.           //
  254.           IoPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,//file handle to associate with I/O completion port
  255.                                           NULL,                //optional handle to existing I/O completion port
  256.                                           ReadKey,             //completion key
  257.                                           1);                  //# of threads allowed to execute concurrently
  258.   
  259.    
  260.           if (IoPort == NULL) {
  261.             fprintf(stderr, "failed to create ReadPort, error %d\n", GetLastError());
  262.             exit(1);
  263.           }
  264.  
  265.          //
  266.          //If we need to, aka we're running on NT 3.51, let's associate a file handle with the
  267.          //completion port.
  268.          //
  269.     
  270.          IoPort = CreateIoCompletionPort(SourceFile,
  271.                                          IoPort,
  272.                                          ReadKey,            
  273.                                          1);
  274.  
  275.          if (IoPort == NULL)
  276.            {
  277.              fprintf(stderr,
  278.                      "failed to create IoPort, error %d\n", 
  279.                      GetLastError());
  280.  
  281.              exit(1);
  282.  
  283.            }
  284.         }
  285.     
  286.     //
  287.     // Associate the destination file handle with the
  288.     // I/O completion port.
  289.     //
  290.     
  291.     IoPort = CreateIoCompletionPort(DestFile,
  292.                                     IoPort,
  293.                                     WriteKey,
  294.                                     1);
  295.     if (IoPort == NULL) {
  296.         fprintf(stderr, "failed to create WritePort, error %d\n", GetLastError());
  297.         exit(1);
  298.     }
  299.  
  300.     
  301.  
  302.     StartTime = GetTickCount();
  303.  
  304.     //
  305.     // Do the copy
  306.     //
  307.     CopyLoop(FileSize);
  308.  
  309.     EndTime = GetTickCount();
  310.  
  311.     CloseHandle(SourceFile);
  312.     CloseHandle(DestFile);
  313.  
  314.     //
  315.     // We need another handle to the destination file that is
  316.     // opened without FILE_FLAG_NO_BUFFERING. This allows us to set
  317.     // the end-of-file marker to a position that is not sector-aligned.
  318.     //
  319.     BufferedHandle = CreateFile(argv[2],
  320.                                 GENERIC_WRITE,
  321.                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
  322.                                 NULL,
  323.                                 OPEN_EXISTING,
  324.                                 0,
  325.                                 NULL);
  326.     if (BufferedHandle == INVALID_HANDLE_VALUE) {
  327.         fprintf(stderr,
  328.                 "failed to open buffered handle to %s, error %d\n",
  329.                 argv[2],
  330.                 GetLastError());
  331.         exit(1);
  332.     }
  333.  
  334.  
  335.     //
  336.     // Set the destination's file size to the size of the
  337.     // source file, in case the size of the source file was
  338.     // not a multiple of the page size.
  339.     //
  340.     Status = SetFilePointer(BufferedHandle,
  341.                             FileSize.LowPart,
  342.                             (PLONG)&FileSize.HighPart,
  343.                             FILE_BEGIN);
  344.     if ((Status == 0xffffffff) && (GetLastError() != NO_ERROR)) {
  345.         fprintf(stderr, "final SetFilePointer failed, error %d\n", GetLastError());
  346.         exit(1);
  347.     }
  348.     Success = SetEndOfFile(BufferedHandle);
  349.     if (!Success) {
  350.         fprintf(stderr, "SetEndOfFile failed, error %d\n", GetLastError());
  351.         exit(1);
  352.     }
  353.     CloseHandle(BufferedHandle);
  354.  
  355.     printf("%d bytes copied in %.3f seconds\n",
  356.            FileSize.LowPart,
  357.            (float)(EndTime-StartTime)/1000.0);
  358.     printf("%.2f MB/sec\n",
  359.            ((LONGLONG)FileSize.QuadPart/(1024.0*1024.0)) / (((float)(EndTime-StartTime)) / 1000.0));
  360.  
  361.     return(0);
  362.  
  363. }
  364.  
  365. VOID
  366. CopyLoop(
  367.     ULARGE_INTEGER FileSize
  368.     )
  369. {
  370.     ULARGE_INTEGER ReadPointer;
  371.     BOOL Success;
  372.     DWORD NumberBytes;
  373.     LPOVERLAPPED CompletedOverlapped;
  374.     DWORD Key;
  375.     PCOPY_CHUNK Chunk;
  376.     int PendingIO = 0;
  377.     int i;
  378.  
  379.     //
  380.     // Start reading the file. Kick off MAX_CONCURRENT_IO reads, then just
  381.     // loop waiting for I/O to complete.
  382.     //
  383.     ReadPointer.QuadPart = 0;
  384.  
  385.     for (i=0; i < MAX_CONCURRENT_IO; i++) {
  386.         if (ReadPointer.QuadPart >= FileSize.QuadPart) {
  387.             break;
  388.         }
  389.         //
  390.         // Use VirtualAlloc so we get a page-aligned buffer suitable
  391.         // for unbuffered I/O.
  392.         //
  393.         CopyChunk[i].Buffer = VirtualAlloc(NULL,
  394.                                            BUFFER_SIZE,
  395.                                            MEM_COMMIT,
  396.                                            PAGE_READWRITE);
  397.         if (CopyChunk[i].Buffer == NULL) {
  398.             fprintf(stderr, "VirtualAlloc %d failed, error %d\n",i, GetLastError());
  399.             exit(1);
  400.         }
  401.         CopyChunk[i].Overlapped.Offset = ReadPointer.LowPart;
  402.         CopyChunk[i].Overlapped.OffsetHigh = ReadPointer.HighPart;
  403.         CopyChunk[i].Overlapped.hEvent = NULL;     // not needed
  404.  
  405.         Success = ReadFile(SourceFile,
  406.                            CopyChunk[i].Buffer,
  407.                            BUFFER_SIZE,
  408.                            &NumberBytes,
  409.                            &CopyChunk[i].Overlapped);
  410.  
  411.         if (!Success && (GetLastError() != ERROR_IO_PENDING)) {
  412.             fprintf(stderr,
  413.                     "ReadFile at %lx failed, error %d\n",
  414.                     ReadPointer.LowPart,
  415.                     GetLastError());
  416.         } else {
  417.             ReadPointer.QuadPart += BUFFER_SIZE;
  418.             ++PendingIO;
  419.         }
  420.     }
  421.  
  422.     //
  423.     // We have started the initial async. reads, enter the main loop.
  424.     // This simply waits until an I/O completes, then issues the next
  425.     // I/O.  When a write completes, the next read is issued. When a
  426.     // read completes, the corresponding write is issued.
  427.     //
  428.     while (PendingIO) {
  429.         Success = GetQueuedCompletionStatus(IoPort,
  430.                                             &NumberBytes,
  431.                                             &Key,
  432.                                             &CompletedOverlapped,
  433.                                             (DWORD)-1);
  434.         if (!Success && (CompletedOverlapped == NULL)) {
  435.             //
  436.             // The call has failed.
  437.             //
  438.             fprintf(stderr,
  439.                     "GetQueuedCompletionStatus on the IoPort failed, error %d\n",
  440.                     GetLastError());
  441.             exit(1);
  442.         }
  443.         if (!Success) {
  444.             //
  445.             // The call has succeeded, but the initial I/O operation
  446.             // has failed.
  447.             //
  448.             fprintf(stderr,
  449.                     "GetQueuedCompletionStatus on the IoPort removed a failed\n"
  450.                     "I/O packet, error %d\n",GetLastError());
  451.             //
  452.             // This is probably bad, but what the heck, it's only a demo program,
  453.             // so proceed blindly ahead.
  454.             //
  455.         }
  456.  
  457.         Chunk = (PCOPY_CHUNK)CompletedOverlapped;
  458.  
  459.         if (Key == ReadKey) {
  460.  
  461.             //
  462.             // A read has completed, issue the corresponding write.
  463.             //
  464.  
  465.             //
  466.             // Round the number of bytes to write up to a sector boundary.
  467.             //
  468.             NumberBytes = (NumberBytes + PageSize - 1) & ~(PageSize-1);
  469.  
  470.             Success = WriteFile(DestFile,
  471.                                 Chunk->Buffer,
  472.                                 NumberBytes,
  473.                                 &NumberBytes,
  474.                                 &Chunk->Overlapped);
  475.  
  476.             if (!Success && (GetLastError() != ERROR_IO_PENDING))
  477.                 fprintf(stderr,
  478.                         "WriteFile at %lx failed, error %d\n",
  479.                         Chunk->Overlapped.Offset,
  480.                         GetLastError());
  481.             
  482.             //
  483.             // decrement pending I/O count
  484.             //
  485.             --PendingIO; 
  486.             
  487.         } else if (Key == WriteKey) {
  488.  
  489.             //
  490.             // A write has completed, issue the next read.
  491.             //
  492.  
  493.             if (ReadPointer.QuadPart < FileSize.QuadPart) {
  494.                 Chunk->Overlapped.Offset = ReadPointer.LowPart;
  495.                 Chunk->Overlapped.OffsetHigh = ReadPointer.HighPart;
  496.                 ReadPointer.QuadPart += BUFFER_SIZE;
  497.                 Success = ReadFile(SourceFile,
  498.                                    Chunk->Buffer,
  499.                                    NumberBytes,
  500.                                    &NumberBytes,
  501.                                    &Chunk->Overlapped);
  502.                 if (!Success && (GetLastError() != ERROR_IO_PENDING)) {
  503.                     fprintf(stderr,
  504.                             "ReadFile at %lx failed, error %d\n",
  505.                             Chunk->Overlapped.Offset,
  506.                             GetLastError());
  507.                 }
  508.             } else {
  509.                 //
  510.                 // There are no more reads left to issue, just
  511.                 // wait for the pending writes to drain.
  512.                 //
  513.                 --PendingIO;
  514.             }
  515.         }
  516.     }
  517. }
  518.