home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 32 / IOPROG_32.ISO / SOFT / SqlEval7 / devtools / samples / BACKUP / mthread.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-30  |  16.3 KB  |  594 lines

  1. //
  2. // This file is part of Microsoft SQL Server online documentation.
  3. // Copyright (C) 1992-1997 Microsoft Corporation. All rights reserved.
  4. //
  5. // This source code is an intended supplement to the Microsoft SQL
  6. // Server online references and related electronic documentation.
  7. //
  8. // This sample is for instructional purposes only.
  9. // Code contained herein is not intended to be used "as is" in real applications.
  10. // 
  11. // mthread.cpp  : 
  12. //  
  13. // Test & demonstrate the use of multiple streams from a single process.
  14. //
  15. // This is a sample program used to demonstrate the Virtual Device Interface
  16. // feature of Microsoft SQL Server.
  17. //
  18. // The program will backup or restore the 'pubs' sample database.
  19. //
  20. // The program requires two command line parameters.  
  21. // 1)
  22. // One of:
  23. //  b   perform a backup
  24. //  r   perform a restore
  25. //
  26. // 2)
  27. // The number of streams to use, 1-32.
  28. //  
  29.  
  30. #define _WIN32_DCOM
  31.  
  32. #include <objbase.h>    // for 'CoInitialize()'
  33. #include <stdio.h>      // for file operations
  34. #include <ctype.h>      // for toupper ()
  35. #include <process.h>    // for C library-safe _beginthreadex,_endthreadex
  36. #include <windows.h>
  37.  
  38. #include "vdi.h"        // interface declaration
  39. #include "vdierror.h"   // error constants
  40. #include "vdiguid.h"    // define the GUIDs 
  41.  
  42. void LogError (
  43.     LPSTR           location,    // must always be provided
  44.     LPSTR           description, // NULL is acceptable
  45.     DWORD           errCode);    // windows status code
  46.  
  47. int performTransfer (
  48.     IClientVirtualDevice*   vd,
  49.     int                     backup,
  50.     int                     streamId);
  51.  
  52. HANDLE execSQL (int doBackup, int nStreams);
  53.  
  54. int
  55. startSecondaries(
  56.     IClientVirtualDeviceSet *vds,
  57.     HANDLE  hSQLProcess,  // handle to process dealing with the SQL
  58.     int     nStreams);    // number of i/o streams
  59.  
  60. unsigned __stdcall
  61. runSecondary (void *parms);
  62.  
  63.  
  64. //
  65. // main function
  66. //
  67. int main(int argc, char *argv[])
  68. {
  69.     HRESULT                     hr;
  70.     IClientVirtualDeviceSet*    vds = NULL ; 
  71.     VDConfig                    config;
  72.     int                         badParm=TRUE;
  73.     int                         doBackup;
  74.     HANDLE                      hProcess;
  75.     int                         termCode = -1;
  76.     int                         nStreams=1;
  77.  
  78.     // Check the input parm
  79.     //
  80.     if (argc == 3)
  81.     {
  82.         sscanf (argv[2], "%d", &nStreams);
  83.         switch (toupper(argv[1][0]))
  84.         {
  85.             case 'B':
  86.                 doBackup = TRUE;
  87.                 badParm = FALSE;
  88.                 break;
  89.         
  90.             case 'R':
  91.                 doBackup = FALSE;
  92.                 badParm = FALSE;
  93.                 break;
  94.         }
  95.     }
  96.  
  97.     if (badParm)
  98.     {
  99.         printf ("useage: mprocess {B|R} <nStreams>\n"
  100.             "Demonstrate a multistream Backup or Restore using the Virtual Device Interface\n");
  101.         exit (1);
  102.     }
  103.  
  104.     // 1..32 streams.
  105.     //
  106.     if (nStreams < 1)
  107.             nStreams = 1;
  108.     else if (nStreams > 32)
  109.             nStreams = 32;
  110.  
  111.     printf ("Performing a %s using %d virtual device(s).\n", 
  112.             (doBackup) ? "BACKUP" : "RESTORE", nStreams);
  113.  
  114.     // Initialize COM Library
  115.     // Note: _WIN32_DCOM must be defined during the compile.
  116.     //
  117.     hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
  118.  
  119.     if (!SUCCEEDED (hr))
  120.     {
  121.         printf ("Coinit fails: x%X\n", hr);
  122.         exit (1);
  123.     }
  124.  
  125.  
  126.     // Get an interface to the device set.
  127.     // Notice how we use a single IID for both the class and interface
  128.     // identifiers.
  129.     //
  130.     hr = CoCreateInstance ( 
  131.         IID_IClientVirtualDeviceSet,
  132.         NULL, 
  133.         CLSCTX_INPROC_SERVER,
  134.         IID_IClientVirtualDeviceSet,
  135.         (void**)&vds);
  136.  
  137.     if (!SUCCEEDED (hr))
  138.     {
  139.         // This failure might happen if the DLL was not registered.
  140.         //
  141.         printf ("Could not create component: x%X\n", hr);
  142.         printf ("Check registration of SQLVDI.DLL and value of IID\n");
  143.         goto exit;
  144.     }
  145.  
  146.     // Setup the VDI configuration we want to use.
  147.     // This program doesn't use any fancy features, so the
  148.     // only field to setup is the deviceCount.
  149.     //
  150.     // The server will treat the virtual device just like a pipe:
  151.     // I/O will be strictly sequential with only the basic commands.
  152.     //
  153.     memset (&config, 0, sizeof(config));
  154.  
  155.     config.deviceCount = nStreams;
  156.  
  157.     // Create the virtual device set
  158.     //
  159.     hr = vds->Create (L"SUPERBAK", &config);
  160.     if (!SUCCEEDED (hr))
  161.     {
  162.         printf ("VDS::Create fails: x%X", hr);
  163.         goto exit;
  164.     }
  165.  
  166.     // Send the SQL command, via isql in a subprocess.
  167.     //
  168.     printf("\nSending the SQL...\n");
  169.  
  170.     hProcess = execSQL (doBackup, nStreams);
  171.     if (hProcess == NULL)
  172.     {
  173.         printf ("execSQL failed.\n");
  174.         goto shutdown;
  175.     }
  176.  
  177.  
  178.     // Wait for the server to connect, completing the configuration.
  179.     // Notice that we wait a maximum of 15 seconds.
  180.     //
  181.     printf("\nWaiting for SQL to complete configuration...\n");
  182.  
  183.     hr = vds->GetConfiguration (15000, &config);
  184.     if (!SUCCEEDED (hr))
  185.     {
  186.         printf ("VDS::Getconfig fails: x%X\n", hr);
  187.         goto shutdown;
  188.     }
  189.  
  190.     // Handle the virtual devices in secondary processes.
  191.     //
  192.     printf ("\nSpawning secondary threads..\n");
  193.     termCode = startSecondaries (vds, hProcess, nStreams);
  194.     
  195. shutdown:
  196.  
  197.     // Close the set
  198.     //
  199.     vds->Close ();
  200.  
  201.     // COM reference counting: Release the interface.
  202.     //
  203.     vds->Release () ;
  204.  
  205. exit:
  206.  
  207.     // Uninitialize COM Library
  208.     //
  209.     CoUninitialize () ;
  210.  
  211.     return termCode;
  212. }
  213.  
  214. //
  215. // Execute a basic backup/restore, by starting 'isql' in a subprocess.
  216. //
  217. // Returns:
  218. //  NULL    : failed to start isql
  219. //  else    : process handle
  220. //
  221. HANDLE execSQL (int doBackup, int nStreams)
  222. {
  223.     char                    cmd[5000];
  224.     char                    extend[100];
  225.     PROCESS_INFORMATION     pi;
  226.     STARTUPINFO             si;
  227.     int                     ix;
  228.  
  229.     // Build the SQL, submitting it via 'isql'
  230.     //
  231.     sprintf (cmd, 
  232.         "isql -Usa -P -b -Q\"%s DATABASE PUBS %s VIRTUAL_DEVICE='SUPERBAK'",
  233.         (doBackup) ? "BACKUP" : "RESTORE",
  234.         (doBackup) ? "TO"     : "FROM");
  235.     
  236.     for (ix=1; ix<nStreams; ix++)
  237.     {
  238.         sprintf (extend, ", VIRTUAL_DEVICE='SUPERBAK%d'", ix);
  239.         strcat (cmd, extend);
  240.     }
  241.  
  242.     strcat (cmd, "\"");
  243.  
  244.     printf ("Submitting SQL:\n%s\n\n", cmd);
  245.  
  246.     // use my process for startup info
  247.     //
  248.     GetStartupInfo (&si);
  249.  
  250.     if (!CreateProcess (NULL, cmd, NULL, NULL,
  251.             TRUE,   // inherit handles (stdin/stdout)
  252.             0,      // creation flags,
  253.             NULL, NULL,
  254.             &si,    // startup info
  255.             &pi))   // out: process info
  256.     {
  257.         LogError ("startSecondary", "CreateProcess", GetLastError ());
  258.         return NULL;
  259.     }
  260.     
  261.     // Return the process handle
  262.     //
  263.     return (pi.hProcess);
  264. }
  265.  
  266. //-----------------------------------------------------------
  267. //
  268. // A parmameter block to use when spawning secondaries.
  269. //
  270. struct THREAD_PARMS {
  271.     IClientVirtualDeviceSet *vds;
  272.     int                      streamId;
  273. };
  274.  
  275.  
  276. //-----------------------------------------------------------
  277. // Invoke the secondary threads, and wait for all children
  278. // to complete.
  279. //
  280. // Returns: 0 if no errors were detected.
  281. //
  282. //
  283. int
  284. startSecondaries(
  285.     IClientVirtualDeviceSet *vds,
  286.     HANDLE  hSQLProcess,  // handle to process dealing with the SQL
  287.     int     nStreams)     // number of i/o streams
  288. {
  289.     THREAD_PARMS    parms[32];      // each thread needs its own parm block
  290.     int             ix,nActive;
  291.     HANDLE          children[33];  // 32 is maximum number of streams.
  292.                                    // plus one for the isql process.
  293.     DWORD           waitStatus, exitCode;
  294.     unsigned        threadId;
  295.  
  296.     for (ix=0; ix<nStreams; ix++)
  297.     {
  298.         // All threads share the same virtual device set,
  299.         // but must operate on different virtual devices.
  300.         //
  301.         parms[ix].vds = vds;
  302.         parms[ix].streamId = ix;
  303.  
  304.         children[ix] = (HANDLE)_beginthreadex (
  305.             NULL, 0, runSecondary, (void*)&parms[ix], 0, &threadId);
  306.  
  307.         if (children[ix] == NULL)
  308.         {
  309.             printf ("Failed to create thread. errno is %d\n", errno);
  310.             goto errorExit;
  311.         }
  312.  
  313.         printf ("\nStarted thread %d\n", threadId);
  314.     }
  315.  
  316.     // Add the isql process into the array
  317.     //
  318.     children[nStreams] = hSQLProcess;
  319.     nActive = nStreams+1;
  320.  
  321.     // Wait for all to finish.
  322.     // Max wait is one minute for this tiny test.
  323.     //
  324.     printf ("All children are now running.\n"
  325.         "Waiting for their completion...\n");
  326.     
  327.     waitStatus = WaitForMultipleObjects (nActive, children, 
  328.             TRUE, 60000);
  329.  
  330.     if (waitStatus < WAIT_OBJECT_0 ||
  331.             waitStatus >= WAIT_OBJECT_0+nActive)
  332.     {
  333.         LogError ("startSecondary", "WaitForMultiple", GetLastError ());
  334.         printf("Unexpected wait code: %d\n", waitStatus);
  335.         goto errorExit;
  336.     }
  337.  
  338.     // All of the children have completed.
  339.     // Get the completion code from 'isql' to check for sucess.
  340.     //
  341.     if (!GetExitCodeProcess (hSQLProcess, &exitCode))
  342.     {
  343.         LogError ("startSecondary", "GetExitCode", GetLastError ());
  344.         goto errorExit;
  345.     }
  346.  
  347.     if (exitCode != 0)
  348.     {
  349.         printf ("The SQL operation failed with code %d\n", exitCode);
  350.         goto errorExit;
  351.     }
  352.  
  353.     printf ("The SQL operation was sucessful.\n");
  354.  
  355.     // Be sure to close handles when finished with them.
  356.     //
  357.     // Notice that in our trivial error handling here we
  358.     // don't bother closing them, since handles are
  359.     // automatically closed as part of process termination.
  360.     //
  361.     for (ix = 0; ix < nActive; ix++)
  362.     {
  363.         CloseHandle (children[ix]);
  364.     }
  365.  
  366.     return 0;
  367.  
  368. errorExit:
  369.     // Handle all problems in a trivial fashion:
  370.     //  SignalAbort() will cause all processes using the virtual device set
  371.     //  to terminate processing.
  372.     //
  373.     vds->SignalAbort ();
  374.  
  375.     //  However, since the threads are using the virtual device set allocated
  376.     //  by the main thread, we can't let the main thread close the set before
  377.     //  the threads are finished with it. There are two options:
  378.     //  1) exit the process immediately.
  379.     //  2) wait for the threads to terminate.
  380.     //
  381.     //  Option 2 is "cleaner" but requires more code, so we just exit here:
  382.     //
  383.     ExitProcess ((unsigned)-1);
  384.  
  385.     return -1;  // ExitProcess doesn't return; this avoids a compiler error.
  386. }
  387.  
  388. //------------------------------------------------------------------
  389. // Perform secondary client processing from within a thread.
  390. // Exits with 0 if no errors detected, else nonzero.
  391. // The caller must setup a THREAD_PARMS parameter block when
  392. // spawning the thread.
  393. //
  394. unsigned __stdcall
  395. runSecondary (void *parms)
  396. {
  397.     HRESULT                         hr;
  398.     WCHAR                           devName[20];
  399.     IClientVirtualDevice*           vd;
  400.     VDConfig                        config;
  401.     int                             termCode;
  402.  
  403.     // Fetch the input parms
  404.     //
  405.     int                         streamId = ((THREAD_PARMS*)parms)->streamId;
  406.     IClientVirtualDeviceSet *   vds      = ((THREAD_PARMS*)parms)->vds;
  407.  
  408.     // Build the name of the device assigned to this thread.
  409.     //
  410.     if (streamId == 0)
  411.     {
  412.         // The first device has the same name as the set.
  413.         //
  414.         wcscpy (devName, L"SUPERBAK");
  415.     }
  416.     else
  417.     {
  418.         // For this example, we've simply appended a number
  419.         // for additional devices.  You are free to name them
  420.         // as you wish.
  421.         //
  422.         swprintf (devName, L"SUPERBAK%d", streamId);
  423.     }
  424.  
  425.     // Open the device assigned to this thread.
  426.     //
  427.     hr = vds->OpenDevice (devName, &vd);
  428.     if (!SUCCEEDED (hr))
  429.     {
  430.         printf ("OpenDevice fails on %ls: x%X", devName, hr);
  431.         termCode = -1;
  432.         goto errExit;
  433.     }
  434.  
  435.     // Grab the config to figure out data direction
  436.     //
  437.     hr = vds->GetConfiguration (INFINITE, &config);
  438.     if (!SUCCEEDED (hr))
  439.     {
  440.         printf ("VDS::Getconfig fails: x%X\n", hr);
  441.         termCode = -1;
  442.         goto errExit;
  443.     }
  444.  
  445.     printf ("\nPerforming data transfer...\n");
  446.         
  447.     termCode = performTransfer (vd, 
  448.         (config.features&VDF_WriteMedia), streamId);
  449.  
  450. errExit:
  451.  
  452.     // If errors were detected, force an abort.
  453.     //
  454.     if (termCode != 0)
  455.     {
  456.         vds->SignalAbort ();
  457.     }
  458.  
  459.     return ((unsigned)termCode);
  460.  
  461. }
  462.  
  463.  
  464.  
  465. // This routine reads commands from the server until a 'Close' status is received.
  466. // It simply synchronously reads or writes a file on the root of the current drive.
  467. //
  468. // Returns 0, if no errors are detected, else non-zero.
  469. //
  470. int performTransfer (
  471.     IClientVirtualDevice*   vd,
  472.     int                     backup,
  473.     int                     streamId)
  474. {
  475.     FILE *          fh;
  476.     char            fname[80];
  477.     VDC_Command *   cmd;
  478.     DWORD           completionCode;
  479.     DWORD           bytesTransferred;
  480.     HRESULT         hr;
  481.     int             termCode = -1;
  482.  
  483.     sprintf (fname, "multi.%d.dmp", streamId);
  484.  
  485.     fh = fopen (fname, (backup)? "wb" : "rb");
  486.     if (fh == NULL )
  487.     {
  488.         printf ("Failed to open: %s\n", fname);
  489.         return -1;
  490.     }
  491.  
  492.     while (SUCCEEDED (hr=vd->GetCommand (INFINITE, &cmd)))
  493.     {
  494.         bytesTransferred = 0;
  495.         switch (cmd->commandCode)
  496.         {
  497.             case VDC_Read:
  498.                 bytesTransferred = fread (cmd->buffer, 1, cmd->size, fh);
  499.                 if (bytesTransferred == cmd->size)
  500.                     completionCode = ERROR_SUCCESS;
  501.                 else
  502.                     // assume failure is eof
  503.                     completionCode = ERROR_HANDLE_EOF;
  504.  
  505.                 break;
  506.  
  507.             case VDC_Write:
  508.                 bytesTransferred = fwrite (cmd->buffer, 1, cmd->size, fh);
  509.                 if (bytesTransferred == cmd->size )
  510.                 {
  511.                     completionCode = ERROR_SUCCESS;
  512.                 }
  513.                 else
  514.                     // assume failure is disk full
  515.                     completionCode = ERROR_DISK_FULL;
  516.                 break;
  517.  
  518.             case VDC_Flush:
  519.                 fflush (fh);
  520.                 completionCode = ERROR_SUCCESS;
  521.                 break;
  522.     
  523.             case VDC_ClearError:
  524.                 completionCode = ERROR_SUCCESS;
  525.                 break;
  526.  
  527.             default:
  528.                 // If command is unknown...
  529.                 completionCode = ERROR_NOT_SUPPORTED;
  530.         }
  531.  
  532.  
  533.         hr = vd->CompleteCommand (cmd, completionCode, bytesTransferred, 0);
  534.         if (!SUCCEEDED (hr))
  535.         {
  536.             printf ("Completion Failed: x%X\n", hr);
  537.             break;
  538.         }
  539.     }
  540.  
  541.     if (hr != VD_E_CLOSE)
  542.     {
  543.         printf ("Unexpected termination: x%X\n", hr);
  544.     }
  545.     else
  546.     {
  547.         // As far as the data transfer is concerned, no
  548.         // errors occurred.  The code which issues the SQL
  549.         // must determine if the backup/restore was
  550.         // really successful.
  551.         //
  552.         printf ("Successfully completed data transfer.\n");
  553.         termCode = 0;
  554.     }
  555.  
  556.     fclose (fh);
  557.  
  558.     return termCode;
  559. }
  560.  
  561.  
  562. //--------------------------------------------------------------------
  563. // 
  564. // A simple error logger.
  565. //
  566. void LogError (
  567.     LPSTR           location,    // must always be provided
  568.     LPSTR           description, // NULL is acceptable
  569.     DWORD           errCode)     // windows status code
  570. {
  571.     LPVOID lpMsgBuf;
  572.  
  573.     printf (
  574.         "Error at %s: %s StatusCode: %X\n",
  575.         location, 
  576.         (description==NULL)?"":description,
  577.         errCode);
  578.  
  579.     // Attempt to explain the code
  580.     //
  581.     if (errCode != 0 && FormatMessage(
  582.         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
  583.             FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,    
  584.         NULL,
  585.         errCode,
  586.         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  587.         (LPTSTR) &lpMsgBuf,    0,    NULL ) )// Process any inserts in lpMsgBuf.
  588.     {
  589.         printf ("Explanation: %s\n", lpMsgBuf);
  590.         LocalFree( lpMsgBuf );
  591.     }
  592. }
  593.  
  594.