multi_thread_win.cpp



This file implements the multi threaded version of the utility (Windows only). By default, under Windows) you are limited to 64 threads.


// -------------------------------------------------------------
// Test program for the class "Http".
//
// Note: Do NOT try to use the following syntax to declare the
// object "http":
//
// Http http();
//
// If you do that, the compiler thinks that you decale a func-
// -tion that takes no argument and returns a object "Http".
// Therfore you get a message related to "non-aggregate" type.
//
// How to compile the program ?
//
// Do not forget the compiler's options:
// - /MT (for multithreading)
// - /GX (for exceptions handling
//
// Also you should use the library "ws2_32.lib" instead of
// "winsowk.lib".
// -------------------------------------------------------------

#include <iostream.h>
#include <stdlib.h>
#include <string.h>
#include "http.hpp"

// -------------------------------------------------------------
// Global variables shared by all threads (read only)
// -------------------------------------------------------------

int port; // port used to connect to the HTTP server
int timeout; // connection timeout
char* hostname; // HTTP server's hostname
char* url; // Path to the web document

// -------------------------------------------------------------
// Print program's usage
// -------------------------------------------------------------

void print_usage()
{
                cerr << endl
                        << "Usage: http host_address path_to_document port timeout nb_threads"
                        << endl
                        << endl;
}

// -------------------------------------------------------------
// HTTP thread
// -------------------------------------------------------------

#define THREAD_OK 0 // success
#define THREAD_MEM_ERROR -1 // Memory allocation error
#define THREAD_INVALID_ARGS -2 // Invalid cpnnection's parameters
#define THREAD_CREATE_SOCK_ERR -3 // Can not create sockets
#define THREAD_CONNECT_FAILED -4 // Cnnect failed
#define THREAD_RESOLV_FAILED -5 // Can ot revolv hostname
#define THREAD_DLL_ERR -6 // DLL error
#define THREAD_DLL_VERSION -7 // Wrong DLL version
#define THREAD_NON_BLOCK_ERR -8 // Can ot set socket to non blocking mode
#define THREAD_BLOCK_ERROR -9 // Can ot set socket to blocking mode
#define THREAD_UNKNOWN_ERR -10 // Unknown error
#define THREAD_WRITE_ERR -11 // Can not write into socket
#define THREAD_READ_ERR -12 // Can not read from socket
#define THREAD_CONNECT_TIMEOUT -13 // Connect timeout
#define THREAD_READ_TIMEOUT -14 // Read timeout
#define THREAD_TIMER_ERR -15 // Can not create timer

// -------------------------------------------------------------
//

DWORD WINAPI Thread_HTTP_Proc(LPVOID lpParameter)
{
        // ---------------------------------------------------------
        // Create HTTP object
        // ---------------------------------------------------------


        Http http;

        // ---------------------------------------------------------
        // Set connection's parameters
        // ---------------------------------------------------------

        try {
                        http.set_hostname(hostname);
                        http.set_url(url);
         }

        catch ( Http_memory_error e )
        {
                ExitThread (THREAD_MEM_ERROR);
                return 1;
        }

        http.set_port(port);
        http.set_timeout(timeout);

        // ---------------------------------------------------------
        // Get the web document
        // ---------------------------------------------------------

        try { http.get(); }

        catch ( Http_memory_error e )
        {
                ExitThread (THREAD_MEM_ERROR);
                return 1;
        }
        
        catch ( Http_invalid_args e )
        {
                ExitThread (THREAD_INVALID_ARGS);
                return 1;
        }
                
        catch ( Http_connect_failed e )
        {
                switch (e.get_error())
                {
                        case SCK_CREATE_ERROR:
                                ExitThread (THREAD_CREATE_SOCK_ERR);
                                return 1;
                        case SCK_CONNECT_ERROR:
                                ExitThread (THREAD_CONNECT_FAILED);
                                return 1;
                        case SCK_GET_IP_ERROR:
                                ExitThread (THREAD_RESOLV_FAILED);
                                return 1;
                        case SCK_SOCK_INIT_DLL_ERR:
                                ExitThread (THREAD_DLL_ERR);
                                return 1;
                        case SCK_SOCK_INIT_DLL_CHECK_ERR:
                                ExitThread (THREAD_DLL_VERSION);
                                return 1;
                        case SCK_SET_NON_BLOCK_ERR:
                                ExitThread (THREAD_NON_BLOCK_ERR);
                                return 1;
                        case SCK_SET_BLOCK_ERR:
                                ExitThread (THREAD_BLOCK_ERROR);
                                return 1;
                        default:
                                ExitThread (THREAD_UNKNOWN_ERR);
                                return 1;
                }
                return 1;
        }

        catch ( Http_write_failed e )
        {
                ExitThread (THREAD_WRITE_ERR);
                return 1;
        }

        catch ( Http_read_failed e )
        {
                ExitThread (THREAD_READ_ERR);
                return 1;
        }

        catch ( Http_timeout_connect e )
        {
                ExitThread (THREAD_CONNECT_TIMEOUT);
                return 1;
        }

        catch ( Http_timeout_read e )
        {
                ExitThread (THREAD_READ_TIMEOUT);
                return 1;
        }

        catch ( Http_timer_error e )
        {
                ExitThread (THREAD_TIMER_ERR);
                return 1;
        }

        ExitThread (THREAD_OK);
        return 0;
}

 




// -------------------------------------------------------------
// Main entry point
// -------------------------------------------------------------

int main (int argc, char *argv[])
{

        // ---------------------------------------------------------
        // Check connection's parameters
        // ---------------------------------------------------------

        if (argc != 6)
        {
                print_usage();
                return 1;
        }

        // ---------------------------------------------------------
        // Initialize global variables
        // ---------------------------------------------------------

        port = atoi(argv[3]);
        timeout = atoi(argv[4]);

        hostname = new char[strlen(argv[1])+1];
        if (hostname == NULL)
        {
                cerr << endl
                        << "Can not allocate memory for hostname!"
                        << endl
                        << endl;
                return 1;
        }

        url = new char[strlen(argv[2])+1];
        if (url == NULL)
        {
                cerr << endl
                        << "Can not allocate memory for url!"
                        << endl
                        << endl;
                delete [] hostname;
                return 1;
        }

        strcpy (hostname, argv[1]);
        strcpy (url, argv[2]);
        int nthreads = atoi(argv[5]);

        // ---------------------------------------------------------
        // Print parameter summury
        // ---------------------------------------------------------

        cout << endl
                << "Configuration:" << endl << endl
                << " Port: " << port << endl
                << " Timeout: " << timeout << endl
                << " hostname: " << hostname << endl
                << " url: " << url << endl
                << " number fo threads: " << nthreads << " (maximum is: "
                << MAXIMUM_WAIT_OBJECTS << ")" << endl;

        // ---------------------------------------------------------
        // Allocate control structures
        // ---------------------------------------------------------

        // Handles for all threads
        HANDLE *h_threads = new HANDLE[nthreads];
        if (h_threads == NULL)
        {
                cerr << endl
                        << "Can not allocate memory for threads's handles!"
                        << endl
                        << endl;

                delete [] hostname;
                delete [] url;
                return 1;
        }

        // Ids for all threads
        DWORD *id_threads = new DWORD[nthreads];
        if (id_threads == NULL)
        {
                cerr << endl
                        << "Can not allocate memory for threads's IDs!"
                        << endl
                        << endl;

                delete [] h_threads;
                delete [] hostname;
                delete [] url;
                return 1;
        }

        int number_of_threads = 0;

        // Handles for threads that have been successfuly launched only
        HANDLE *open_thread = new HANDLE[nthreads];
        if (open_thread == NULL)
        {
                cerr << endl
                        << "Can not allocate memory for good threads!"
                        << endl
                        << endl;

                delete [] id_threads;
                delete [] h_threads;
                delete [] hostname;
                delete [] url;
                return 1;
        }

        // Return values for threads that have been successfuly launched only
        DWORD *return_values = new DWORD[nthreads];
        if (return_values == NULL)
        {
                cerr << endl
                        << "Can not allocate memory for threads's return values!"
                        << endl
                        << endl;

                delete [] open_thread;
                delete [] id_threads;
                delete [] h_threads;
                delete [] hostname;
                delete [] url;
                return 1;
        }

        // ---------------------------------------------------------
        // Launch threads
        // ---------------------------------------------------------

        cout << endl
                << "Launching threads:"
                << endl
                << flush;

        for (int i=0; i<nthreads; i++)
        {
                cout << endl
                        << " - thread number "
                        << i
                        << flush;

                h_threads[i] = CreateThread (
                                                                NULL,
                                                                0,
                                                                Thread_HTTP_Proc,
                                                                NULL,
                                                                SYNCHRONIZE,
                                                                &(id_threads[i])
                                                        );
                
                if (h_threads[i] == NULL)
                {
                        DWORD sys_error = GetLastError();
                        LPVOID error_msg;
                        DWORD cr = FormatMessage (
                                                                        FORMAT_MESSAGE_FROM_SYSTEM ¦
                                                                        FORMAT_MESSAGE_ALLOCATE_BUFFER ¦
                                                                        FORMAT_MESSAGE_IGNORE_INSERTS,
                                                                        NULL,
                                                                        sys_error,
                                                                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                                                        (LPTSTR) &error_msg,
                                                                        0,
                                                                        NULL
                                                                );
                        cerr << endl
                                << "ERROR: Can not create new thread number "
                                << i
                                << endl
                                << (LPTSTR)error_msg
                                << endl
                                << endl;
                        LocalFree(error_msg);
                }
                else
                {
                        // One more thread sucessfuly launched
                        open_thread[number_of_threads++] = h_threads[i];
                }
        }

        // ---------------------------------------------------------
        // Wait for all threads to terminate
        // ---------------------------------------------------------

        cout << endl
                        << endl
                        << "Wait for all threads to terminate ... (global timeout is " << timeout*2 << " seconds)"
                        << flush;

        DWORD status = WaitForMultipleObjects (
                                                                        number_of_threads, // Number of handles to wait for
                                                                        open_thread, // Handles' list
                                                                        TRUE, // Wait for all handles
                                                                        (timeout*2)*1000 // wait forever.
                                                                );


        if (status == WAIT_FAILED)
        {
                cout << endl << "ERROR !!!!" << endl << flush;
                
                DWORD sys_error = GetLastError();
                LPVOID error_msg;
                DWORD cr = FormatMessage (
                                                                FORMAT_MESSAGE_FROM_SYSTEM ¦
                                                                FORMAT_MESSAGE_ALLOCATE_BUFFER ¦
                                                                FORMAT_MESSAGE_IGNORE_INSERTS,
                                                                NULL,
                                                                sys_error,
                                                                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                                                (LPTSTR) &error_msg,
                                                                0,
                                                                NULL
                                                        );
                cerr << endl
                        << "ERROR: Error while waiting for threads to terminate."
                        << endl
                        << (LPTSTR)error_msg
                        << endl
                        << endl;
                LocalFree(error_msg);

                for (int k=0; k<number_of_threads; k++)
                {
                        TerminateThread (open_thread[k], 0);
                        CloseHandle (open_thread[k]);
                }

                delete [] return_values;
                delete [] open_thread;
                delete [] id_threads;
                delete [] h_threads;
                delete [] hostname;
                delete [] url;

                return 1;
        }


        // ---------------------------------------------------------
        // Get threads return values
        // ---------------------------------------------------------

        int r_error = 0;
        int r_timeout_connect = 0;
        int r_timeout_read = 0;
        int r_ok = 0;

        for (i=0; i<number_of_threads; i++)
        {
                DWORD status;

                // -----------------------------------------------------
                // Get thread's exit code
                // -----------------------------------------------------

                bool cr = GetExitCodeThread (
                                                                open_thread[i], // handle to the thread
                                                                &status // address to receive termination status
                                                        );

                if (cr == 0)
                {
                        DWORD sys_error = GetLastError();
                        LPVOID error_msg;
                        DWORD cr = FormatMessage (
                                                                        FORMAT_MESSAGE_FROM_SYSTEM ¦
                                                                        FORMAT_MESSAGE_ALLOCATE_BUFFER ¦
                                                                        FORMAT_MESSAGE_IGNORE_INSERTS,
                                                                        NULL,
                                                                        sys_error,
                                                                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                                                        (LPTSTR) &error_msg,
                                                                        0,
                                                                        NULL
                                                                );
                        cerr << endl
                                << "ERROR: Error while getting thread's exit code"
                                << endl
                                << (LPTSTR)error_msg
                                << endl
                                << endl;
                        LocalFree(error_msg);

                        for (int k=0; k<number_of_threads; k++)
                        {
                                TerminateThread (open_thread[k], 0);
                                CloseHandle (open_thread[k]);
                        }

                        delete [] return_values;
                        delete [] open_thread;
                        delete [] id_threads;
                        delete [] h_threads;
                        delete [] hostname;
                        delete [] url;

                        return 1;
                }

                // -----------------------------------------------------
                // Analyse the thread's exit code
                // -----------------------------------------------------

                switch (status)
                {
                        case THREAD_OK:
                                r_ok++;
                                break;

                        case THREAD_CONNECT_TIMEOUT:
                                r_timeout_connect++;
                                break;

                        case THREAD_READ_TIMEOUT:
                                r_timeout_read++;
                                break;

                        default:
                                r_error++;

                }

                return_values[i] = status;
        }

        // ---------------------------------------------------------
        // Print statistics
        // ---------------------------------------------------------

        cout << endl << endl;
        cout << "Results:" << endl;
        cout << endl << " - Number of successful connections: " << r_ok;
        cout << endl << " - Number of errors: " << r_error;
        cout << endl << " - Number of connect timeout: " << r_timeout_connect;
        cout << endl << " - Number of read timeout: " << r_timeout_read;
        cout << endl << endl;


        // ---------------------------------------------------------
        // Close all handles
        // ---------------------------------------------------------

        cout << endl
                << "Close all threads' handles"
                << flush;

        for (int k=0; k<number_of_threads; k++)
        {
                TerminateThread (open_thread[k], 0);
                CloseHandle (open_thread[k]);
        }

        // ---------------------------------------------------------
        // Delete all allocated variable
        // ---------------------------------------------------------

        cout << endl
                << "Delete all allocated memory"
                << flush;

        delete [] return_values;
        delete [] open_thread;
        delete [] id_threads;
        delete [] h_threads;
        delete [] hostname;
        delete [] url;
        
        return 0;
}