home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / netds / winsock / iocomplt / socksrv.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  20KB  |  756 lines

  1.  
  2. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  3. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  4. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  5. // PARTICULAR PURPOSE.
  6. //
  7. // Copyright (C) 1993-1997  Microsoft Corporation.  All Rights Reserved.
  8. //
  9. //  MODULE:   socksrv.c
  10. //
  11. //  PURPOSE:  Handle and Benchmark client transactions.
  12. //
  13. //  FUNCTIONS:
  14. //     main              - SockSrv entry point
  15. //     SortTheBuffer          - Create a sorted copy of a buffer
  16. //     CompleteBenchmark      - Time the client transactions
  17. //     WorkerThread          - Process client requests
  18. //     CreateWorkers          - Create worker threads
  19. //     CreateNetConnections   - Establish client connections
  20. //     ParseSwitch          - Process command line option
  21. //     ShowUsage          - Display usage help
  22. //     Random              - Generate a random # within a given range
  23. //
  24. //
  25.  
  26. #include "socksrv.h"
  27.  
  28. int _CRTAPI1
  29. main (
  30.         int argc,
  31.         char *argv[],
  32.         char *envp[]
  33. )
  34. {
  35.  
  36.    char chChar, *pchChar;
  37.    DWORD lc;
  38.    BOOL b;
  39.    int WriteCount;
  40.  
  41.    //
  42.    // try to get timing more accurate... Avoid context
  43.    // switch that could occur when threads are released
  44.    //
  45.  
  46.    SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
  47.  
  48.    //
  49.    // Figure out how many processors we have to size the minimum
  50.    // number of worker threads and concurrency
  51.    //
  52.  
  53.    GetSystemInfo (&SystemInfo);
  54.  
  55.    fVerbose = FALSE;
  56.    dwNumberOfClients = 1;
  57.    dwNumberOfWorkers = 2 * SystemInfo.dwNumberOfProcessors;
  58.    dwConcurrency = SystemInfo.dwNumberOfProcessors;
  59.    fTcp = TRUE;
  60.    dwWorkIndex = 4;
  61.  
  62.    while (--argc)
  63.    {
  64.       pchChar = *++argv;
  65.       if (*pchChar == '/' || *pchChar == '-')
  66.       {
  67.          while (chChar = *++pchChar)
  68.          {
  69.             ParseSwitch (chChar, &argc, &argv);
  70.          }
  71.       }
  72.    }
  73.  
  74.    //
  75.    // If we are doing file I/O, then create a large file that clients
  76.    // can randomly seek and read from
  77.    //
  78.  
  79.    srand (1);
  80.  
  81.    //
  82.    // Create a new file and make it the correct size
  83.    //
  84.  
  85.    DeleteFile ("socksrv.dat");
  86.  
  87.    hFile = CreateFile (
  88.                          "socksrv.dat",
  89.                          GENERIC_READ | GENERIC_WRITE,
  90.                          FILE_SHARE_READ | FILE_SHARE_WRITE,
  91.                          NULL,
  92.                          OPEN_ALWAYS,
  93.    FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
  94.                          NULL
  95.       );
  96.    if (hFile == INVALID_HANDLE_VALUE)
  97.    {
  98.       fprintf (stderr, "SOCKSRV: Error opening file %d\n", GetLastError ());
  99.       exit (1);
  100.    }
  101.  
  102.    //
  103.    // Initialize the file with random data
  104.    //
  105.  
  106.    ClientData[0].Overlapped.Offset = 0;
  107.    for (WriteCount = 0; WriteCount < NUMBER_OF_WRITES; WriteCount++)
  108.    {
  109.  
  110.       for (lc = 0; lc < SIXTEEN_K; lc++)
  111.       {
  112.          InitialBuffer[lc] = (DWORD) rand ();
  113.       }
  114.  
  115.       b = WriteFile (hFile, InitialBuffer, sizeof (InitialBuffer), &lc, &ClientData[0].Overlapped);
  116.  
  117.       if (!b && GetLastError () != ERROR_IO_PENDING)
  118.       {
  119.          fprintf (stderr, "SOCKSRV: Error in pre-write %d\n", GetLastError ());
  120.          exit (1);
  121.       }
  122.       b = GetOverlappedResult (
  123.                                  hFile,
  124.                                  &ClientData[0].Overlapped,
  125.                                  &lc,
  126.                                  TRUE
  127.          );
  128.       if (!b || lc != sizeof (InitialBuffer))
  129.       {
  130.          fprintf (stderr, "SOCKSRV: Wait for pre-write failed %d\n", GetLastError ());
  131.          exit (1);
  132.       }
  133.       ClientData[0].Overlapped.Offset += lc;
  134.    }
  135.  
  136.    srand (1);
  137.    fprintf (stdout, "SOCKSRV: %2d Clients %2d Workers Concurrency %d Using %s WorkIndex %d\n",
  138.             dwNumberOfClients,
  139.             dwNumberOfWorkers,
  140.             dwConcurrency,
  141.             fTcp ? "TCP" : "IPX",
  142.             dwWorkIndex
  143.       );
  144.  
  145.    if (!CreateNetConnections ())
  146.    {
  147.       exit (1);
  148.    }
  149.  
  150.    if (!CreateWorkers ())
  151.    {
  152.       exit (1);
  153.    }
  154.  
  155.    if (fVerbose)
  156.    {
  157.       fprintf (stdout, "Workers Created, Waiting for Clients to complete\n");
  158.    }
  159.  
  160.    CompleteBenchmark ();
  161.  
  162.    exit (1);
  163.    return 1;
  164. }
  165.  
  166. VOID
  167. WINAPI
  168. CompleteBenchmark (
  169.                      VOID
  170. )
  171. {
  172.    DWORD LowestTicks;
  173.    DWORD HighestTicks;
  174.    DWORD TotalTicks;
  175.    DWORD TotalIterations;
  176.    DWORD TotalBytesTransferred;
  177.    DWORD i;
  178.  
  179.    StartTime = GetTickCount ();
  180.    SetEvent (hBenchmarkStart);
  181.    WaitForSingleObject (hBenchmarkComplete, INFINITE);
  182.    EndTime = GetTickCount ();
  183.  
  184.    LowestTicks = ClientData[0].IoBuffer.u.IAmDone.TotalTicks;
  185.    HighestTicks = ClientData[0].IoBuffer.u.IAmDone.TotalTicks;
  186.    TotalTicks = EndTime - StartTime;
  187.    TotalIterations = 0;
  188.    TotalBytesTransferred = 0;
  189.  
  190.    for (i = 0; i < dwNumberOfClients; i++)
  191.    {
  192.       TotalIterations += ClientData[i].IoBuffer.u.IAmDone.TotalIterations;
  193.       TotalBytesTransferred += ClientData[i].IoBuffer.u.IAmDone.TotalBytesTransferred;
  194.  
  195.       //
  196.       // find fastest client
  197.       //
  198.       if (LowestTicks > ClientData[i].IoBuffer.u.IAmDone.TotalTicks)
  199.       {
  200.          LowestTicks = ClientData[i].IoBuffer.u.IAmDone.TotalTicks;
  201.       }
  202.  
  203.       //
  204.       // find slowest client
  205.       //
  206.       if (HighestTicks < ClientData[i].IoBuffer.u.IAmDone.TotalTicks)
  207.       {
  208.          HighestTicks = ClientData[i].IoBuffer.u.IAmDone.TotalTicks;
  209.       }
  210.    }
  211.  
  212.    fprintf (stdout, "\nSOCKSRV:TPS, %ld (Fastest Client %dms Slowest Client %dms)\n",
  213.             (TotalIterations * 1000) / TotalTicks,
  214.             LowestTicks,
  215.             HighestTicks
  216.       );
  217.  
  218.    if (fVerbose)
  219.    {
  220.       fprintf (stdout, "\n%ld bytes transferred in %ld iterations, time = %ld ms\n",
  221.                TotalBytesTransferred,
  222.                TotalIterations,
  223.                TotalTicks
  224.          );
  225.  
  226.       for (i = 0; i < dwNumberOfWorkers; i++)
  227.       {
  228.          fprintf (stdout, "Thread[%2d] %d Transactions %d Bytes\n",
  229.                   i,
  230.                   ThreadData[i].TotalTransactions,
  231.                   ThreadData[i].TotalBytesTransferred
  232.             );
  233.       }
  234.    }
  235. }
  236.  
  237. BOOL
  238. WINAPI
  239. CreateNetConnections (
  240.                         void
  241. )
  242. {
  243.    DWORD i;
  244.    SOCKET listener;
  245.    INT err;
  246.    WSADATA WsaData;
  247.    DWORD nbytes;
  248.    BOOL b;
  249.  
  250.    err = WSAStartup (0x0101, &WsaData);
  251.    if (err == SOCKET_ERROR)
  252.    {
  253.       fprintf (stdout, "WSAStartup Failed\n");
  254.       return FALSE;
  255.    }
  256.  
  257.    //
  258.    // Open a socket to listen for incoming connections.
  259.    //
  260.  
  261.    if (fTcp)
  262.    {
  263.  
  264.       SOCKADDR_IN localAddr;
  265.  
  266.       listener = socket (AF_INET, SOCK_STREAM, 0);
  267.       if (listener == INVALID_SOCKET)
  268.       {
  269.          fprintf (stdout, "Socket Create Failed\n");
  270.          return FALSE;
  271.       }
  272.  
  273.       //
  274.       // Bind our server to the agreed upon port number.  See
  275.       // commdef.h for the actual port number.
  276.       //
  277.       ZeroMemory (&localAddr, sizeof (localAddr));
  278.       localAddr.sin_port = htons (SERVPORT);
  279.       localAddr.sin_family = AF_INET;
  280.  
  281.       err = bind (listener, (PSOCKADDR) & localAddr, sizeof (localAddr));
  282.       if (err == SOCKET_ERROR)
  283.       {
  284.          fprintf (stdout, "Socket Bind Failed\n");
  285.          if (WSAGetLastError () == WSAEADDRINUSE)
  286.             fprintf (stdout, "The port number may already be in use.\n");
  287.          return FALSE;
  288.       }
  289.  
  290.    }
  291.    else
  292.    {
  293.  
  294.       SOCKADDR_IPX localAddr;
  295.  
  296.       listener = socket (AF_IPX, SOCK_STREAM, NSPROTO_SPX);
  297.       if (listener == INVALID_SOCKET)
  298.       {
  299.          fprintf (stdout, "Socket Create Failed\n");
  300.          return FALSE;
  301.       }
  302.  
  303.       ZeroMemory (&localAddr, sizeof (localAddr));
  304.       localAddr.sa_socket = htons (7);
  305.       localAddr.sa_family = AF_IPX;
  306.  
  307.       err = bind (listener, (PSOCKADDR) & localAddr, sizeof (localAddr));
  308.       if (err == SOCKET_ERROR)
  309.       {
  310.          fprintf (stdout, "Socket Bind Failed\n");
  311.          return FALSE;
  312.       }
  313.    }
  314.  
  315.    //
  316.    // Prepare to accept client connections.  Allow up to 5 pending
  317.    // connections.
  318.    //
  319.    err = listen (listener, 5);
  320.    if (err == SOCKET_ERROR)
  321.    {
  322.       fprintf (stdout, "Socket Listen Failed\n");
  323.       return FALSE;
  324.    }
  325.  
  326.  
  327.    //
  328.    // Only Handle a single Queue
  329.    //
  330.  
  331.    for (i = 0; i < dwNumberOfClients; i++)
  332.    {
  333.  
  334.       //
  335.       // Accept incoming connect requests and create wait events for each.
  336.       //
  337.  
  338.       //
  339.       // If we are doing file I/O, each client will need its own event
  340.       // to use for async file I/O
  341.       //
  342.  
  343.       if (hFile)
  344.       {
  345.          ClientData[i].hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
  346.          if (!ClientData[i].hEvent)
  347.          {
  348.             fprintf (stdout, "Client Event Create Failed\n");
  349.             return FALSE;
  350.          }
  351.       }
  352.  
  353.       ClientData[i].Socket = accept (listener, NULL, NULL);
  354.       if (ClientData[i].Socket == INVALID_SOCKET)
  355.       {
  356.          fprintf (stdout, "Accept Failed\n");
  357.          return FALSE;
  358.       }
  359.  
  360.       if (fVerbose)
  361.       {
  362.          fprintf (stdout, "Client Connection Accepted\n");
  363.       }
  364.  
  365.       //
  366.       // note the 16 says how many concurrent cpu bound threads to allow thru
  367.       // this should be tunable based on the requests. CPU bound requests will
  368.       // really really honor this.
  369.       //
  370.  
  371.       CompletionPort = CreateIoCompletionPort (
  372.                                               (HANDLE) ClientData[i].Socket,
  373.                                                  CompletionPort,
  374.                                                  (DWORD) i,
  375.                                                  dwConcurrency
  376.          );
  377.  
  378.       if (!CompletionPort)
  379.       {
  380.          fprintf (stdout, "CompletionPort Create Failed\n");
  381.          return FALSE;
  382.       }
  383.  
  384.       ClientData[i].Flags = CLIENT_CONNECTED;
  385.  
  386.       //
  387.       // Start off an asynchronous read on the socket.
  388.       //
  389.  
  390.       ClientData[i].Overlapped.hEvent = NULL;
  391.  
  392.       b = ReadFile (
  393.                       (HANDLE) ClientData[i].Socket,
  394.                       &ClientData[i].IoBuffer,
  395.                       sizeof (CLIENT_IO_BUFFER),
  396.                       &nbytes,
  397.                       &ClientData[i].Overlapped
  398.          );
  399.  
  400.       if (!b && GetLastError () != ERROR_IO_PENDING)
  401.       {
  402.          fprintf (stdout, "ReadFile Failed\n");
  403.          return FALSE;
  404.       }
  405.    }
  406.  
  407.    dwActiveClientCount = dwNumberOfClients;
  408.    hBenchmarkComplete = CreateEvent (NULL, TRUE, FALSE, NULL);
  409.    if (!hBenchmarkComplete)
  410.    {
  411.       fprintf (stdout, "Create Benchmark Complete Event Failed\n");
  412.       return FALSE;
  413.    }
  414.    hBenchmarkStart = CreateEvent (NULL, TRUE, FALSE, NULL);
  415.    if (!hBenchmarkStart)
  416.    {
  417.       fprintf (stdout, "Create Benchmark Start Event Failed\n");
  418.       return FALSE;
  419.    }
  420.  
  421.    return TRUE;
  422. }
  423.  
  424. BOOL
  425. WINAPI
  426. CreateWorkers (
  427.                  void
  428. )
  429. {
  430.    DWORD ThreadId;
  431.    HANDLE ThreadHandle;
  432.    DWORD i;
  433.  
  434.    for (i = 0; i < dwNumberOfWorkers; i++)
  435.    {
  436.  
  437.       ThreadHandle = CreateThread (
  438.                                      NULL,
  439.                                      0,
  440.                                      WorkerThread,
  441.                                      &ThreadData[i],
  442.                                      0,
  443.                                      &ThreadId
  444.          );
  445.       if (!ThreadHandle)
  446.       {
  447.          fprintf (stdout, "Create Worker Thread Failed\n");
  448.          return FALSE;
  449.       }
  450.  
  451.       CloseHandle (ThreadHandle);
  452.    }
  453.  
  454.    return TRUE;
  455. }
  456.  
  457. DWORD
  458. WINAPI
  459. WorkerThread (
  460.                 LPVOID WorkContext
  461. )
  462. {
  463.    PPER_THREAD_DATA Me;
  464.    DWORD WorkIndex;
  465.    INT err;
  466.    DWORD ResponseLength;
  467.    BOOL b;
  468.    LPOVERLAPPED lpo;
  469.    DWORD nbytes;
  470.    PPER_CLIENT_DATA CurrentClient;
  471.    CHAR ReadBuffer[CLIENT_OUTBOUND_BUFFER_MAX];
  472.  
  473.    WaitForSingleObject (hBenchmarkStart, INFINITE);
  474.  
  475.    Me = (PPER_THREAD_DATA) WorkContext;
  476.  
  477.    for (;;)
  478.    {
  479.  
  480.       b = GetQueuedCompletionStatus (
  481.                                        CompletionPort,
  482.                                        &nbytes,
  483.                                        &WorkIndex,
  484.                                        &lpo,
  485.                                        INFINITE
  486.          );
  487.  
  488.       if (b || lpo)
  489.       {
  490.  
  491.  
  492.          if (b)
  493.          {
  494.  
  495.             CurrentClient = &ClientData[WorkIndex];
  496.  
  497.             switch (CurrentClient->IoBuffer.MessageType)
  498.             {
  499.             case CLIENT_IO_MT_RETURN_DATA:
  500.  
  501.                //
  502.                // Determine how long a response was desired by the client.
  503.                //
  504.  
  505.                ResponseLength = CurrentClient->IoBuffer.u.ReturnData.ByteCount;
  506.                if (ResponseLength > CLIENT_OUTBOUND_BUFFER_MAX)
  507.                {
  508.                   ResponseLength = CLIENT_OUTBOUND_BUFFER_MAX;
  509.                }
  510.  
  511.                //
  512.                // If we are running in I/O mode, do a random read
  513.                // Otherwise, use fill memory to supply the data
  514.                //
  515.                if (hFile)
  516.                {
  517.  
  518.  
  519.                   CurrentClient->Overlapped.Offset = Random (FILE_SIZE / CLIENT_OUTBOUND_BUFFER_MAX) * CLIENT_OUTBOUND_BUFFER_MAX;
  520.                   CurrentClient->Overlapped.hEvent = CurrentClient->hEvent;
  521.                   b = ReadFile (
  522.                                   hFile,
  523.                                   ReadBuffer,
  524.                                   ResponseLength,
  525.                                   &nbytes,
  526.                                   &CurrentClient->Overlapped
  527.                      );
  528.                   if (!b && GetLastError () != ERROR_IO_PENDING)
  529.                   {
  530.                      fprintf (stderr, "SOCKSRV: Error in client read %d\n", GetLastError ());
  531.                      exit (1);
  532.                   }
  533.                   b = GetOverlappedResult (
  534.                                              hFile,
  535.                                              &CurrentClient->Overlapped,
  536.                                              &nbytes,
  537.                                              TRUE
  538.                      );
  539.                   if (!b)
  540.                   {
  541.                      fprintf (stderr, "SOCKSRV: Wait for pre-write failed %d\n", GetLastError ());
  542.                      exit (1);
  543.                   }
  544.                   CurrentClient->Overlapped.hEvent = NULL;
  545.                }
  546.                else
  547.                {
  548.                   FillMemory (CurrentClient->OutboundBuffer, ResponseLength, 0xfe);
  549.                }
  550.  
  551.                //
  552.                // Simulate a small compute bound workload
  553.                //
  554.                SortTheBuffer ((LPDWORD) CurrentClient->OutboundBuffer, (LPDWORD) ReadBuffer, nbytes >> 2);
  555.  
  556.                //
  557.                // Send a response and post another asynchronous read on the
  558.                // socket.
  559.                //
  560.  
  561.                err = send (CurrentClient->Socket,
  562.                            CurrentClient->OutboundBuffer,
  563.                            ResponseLength,
  564.                            0
  565.                   );
  566.  
  567.                if (err == SOCKET_ERROR)
  568.                {
  569.                   fprintf (stdout, "Send Failed\n");
  570.                   exit (1);
  571.                }
  572.  
  573.                Me->TotalTransactions++;
  574.                Me->TotalBytesTransferred += ResponseLength;
  575.  
  576.                //
  577.                // reprime this client by posting another asynch read
  578.                //
  579.  
  580.                b = ReadFile (
  581.                                (HANDLE) CurrentClient->Socket,
  582.                                &CurrentClient->IoBuffer,
  583.                                sizeof (CLIENT_IO_BUFFER),
  584.                                &nbytes,
  585.                                &CurrentClient->Overlapped
  586.                   );
  587.  
  588.                if (!b && GetLastError () != ERROR_IO_PENDING)
  589.                {
  590.                   fprintf (stdout, "ReadFile Failed\n");
  591.                   exit (1);
  592.                }
  593.                break;
  594.  
  595.             case CLIENT_IO_MT_I_AM_DONE:
  596.                CurrentClient->Flags |= CLIENT_DONE;
  597.                if (fVerbose)
  598.                {
  599.                   fprintf (stdout, "Client Has Completed\n");
  600.                }
  601.                if (!InterlockedDecrement (&dwActiveClientCount))
  602.                {
  603.                   SetEvent (hBenchmarkComplete);
  604.                }
  605.                break;
  606.  
  607.             default:
  608.                fprintf (stdout, "Invalid MessageType %x\n", CurrentClient->IoBuffer.MessageType);
  609.                exit (1);
  610.             }
  611.          }
  612.       }
  613.       else
  614.       {
  615.          fprintf (stdout, "WorkThread Wait Failed\n");
  616.          exit (1);
  617.       }
  618.    }
  619.    return 1;
  620. }
  621.  
  622. VOID
  623. WINAPI
  624. ShowUsage (
  625.              VOID
  626. )
  627. {
  628.    fputs ("usage: SOCKSRV [switches]\n"
  629.           "               [-?] show this message\n"
  630.           "               [-v] verbose output\n"
  631.           "               [-t number-of-threads] specify the number of worker threads\n"
  632.      "               [-c number-of-clients] specify the number of clients\n"
  633.           "               [-p concurrency-value] specify the concurrency\n"
  634.           "               [-i ] use IPX instead of TCP\n"
  635.           "               [-w work-index] specify how much compute to do\n"
  636.           ,stderr);
  637.  
  638.    exit (1);
  639. }
  640.  
  641.  
  642. VOID
  643. WINAPI
  644. ParseSwitch (
  645.                CHAR chSwitch,
  646.                int *pArgc,
  647.                char **pArgv[]
  648. )
  649. {
  650.  
  651.    switch (toupper (chSwitch))
  652.    {
  653.  
  654.    case '?':
  655.       ShowUsage ();
  656.       break;
  657.  
  658.    case 'T':
  659.       if (!--(*pArgc))
  660.       {
  661.          ShowUsage ();
  662.       }
  663.       (*pArgv)++;
  664.       dwNumberOfWorkers = strtoul (*(*pArgv), NULL, 10);
  665.       if (dwNumberOfWorkers > MAXIMUM_NUMBER_OF_WORKERS)
  666.       {
  667.          dwNumberOfWorkers = MAXIMUM_NUMBER_OF_WORKERS;
  668.       }
  669.       break;
  670.  
  671.    case 'C':
  672.       if (!--(*pArgc))
  673.       {
  674.          ShowUsage ();
  675.       }
  676.       (*pArgv)++;
  677.       dwNumberOfClients = strtoul (*(*pArgv), NULL, 10);
  678.       if (dwNumberOfClients > MAXIMUM_NUMBER_OF_CLIENTS)
  679.       {
  680.          dwNumberOfClients = MAXIMUM_NUMBER_OF_CLIENTS;
  681.       }
  682.       break;
  683.  
  684.    case 'P':
  685.       if (!--(*pArgc))
  686.       {
  687.          ShowUsage ();
  688.       }
  689.       (*pArgv)++;
  690.       dwConcurrency = strtoul (*(*pArgv), NULL, 10);
  691.       break;
  692.  
  693.    case 'W':
  694.       if (!--(*pArgc))
  695.       {
  696.          ShowUsage ();
  697.       }
  698.       (*pArgv)++;
  699.       dwWorkIndex = strtoul (*(*pArgv), NULL, 10);
  700.       break;
  701.  
  702.    case 'V':
  703.       fVerbose = TRUE;
  704.       break;
  705.  
  706.    case 'I':
  707.       fTcp = FALSE;
  708.       break;
  709.  
  710.    default:
  711.       fprintf (stderr, "SOCKSRV: Invalid switch - /%c\n", chSwitch);
  712.       ShowUsage ();
  713.       break;
  714.  
  715.    }
  716. }
  717.  
  718. DWORD
  719. WINAPI
  720. Random (
  721.           DWORD nMaxValue
  722. )
  723. {
  724.    return (((2 * rand () * nMaxValue + RAND_MAX) / RAND_MAX - 1) / 2);
  725. }
  726.  
  727. int _CRTAPI1
  728. DwordComp (const void *e1, const void *e2)
  729. {
  730.    PULONG p1;
  731.    PULONG p2;
  732.  
  733.    p1 = (PULONG) e1;
  734.    p2 = (PULONG) e2;
  735.  
  736.    return (*p1 - *p2);
  737. }
  738.  
  739. VOID
  740. WINAPI
  741. SortTheBuffer (
  742.                  LPDWORD Destination,
  743.                  LPDWORD Source,
  744.                  int DwordCount
  745. )
  746.  
  747. {
  748.    DWORD i;
  749.  
  750.    for (i = 0; i < 2 * dwWorkIndex; i++)
  751.    {
  752.       CopyMemory (Destination, Source, DwordCount << 2);
  753.       qsort ((void *) Destination, (size_t) DwordCount, (size_t) sizeof (DWORD), DwordComp);
  754.    }
  755. }
  756.