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 / unbufcp2.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  19KB  |  574 lines

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