Saturday, March 15, 2014

Boost Thread Local Storage Example

Description: The below demonstrates the usage of thread local storages. It uses boost's implementation of thread local storage. The program creates mulitple thread which sends mulitple HTTP requests using libcurl.  In order for each thread to use a single connection to send HTTP requests, each thread should have a single CURL handle. Each thread creates his own handle in his local storage.

Progaram:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <curl/curl.h>
#include <boost/thread/tss.hpp>
#include <iostream>

using namespace std;

class HTTPHandle
{
  private:
    CURL* curl;

  public:

    HTTPHandle()
    {
      cout << "Creating curl easy handle" << endl;
      curl = curl_easy_init();
    }

    CURL* getHandle()
    {
       return curl;
    }

    ~HTTPHandle()
    {
      cout << "Cleaning up easy handle" << endl;
      curl_easy_cleanup(curl);
    }
};

void destroyHttpHandle(HTTPHandle* ptr)
{
  delete ptr;
}
void makeRequest()
{
 //We cannote directly create instance of  CURL since it is defined as 'typedef CURL void;'
  static boost::thread_specific_ptr<HTTPHandle> instance(destroyHttpHandle);
  // Create new HTTPHandle object if not already created one for this thread
  if(! instance.get())
  {
    instance.reset(new HTTPHandle);
  }

  CURL* curl = instance->getHandle();

  CURLcode res;

  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "http://10.10.0.1");
    // example.com is redirected, so we tell libcurl to follow redirection
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);

    // Perform the request, res will get the return code
    res = curl_easy_perform(curl);
    // Check for errors
    if(res != CURLE_OK)
      cerr << "curl_easy_perform() failed:" << curl_easy_strerror(res) << endl;
  }
}

// Starting point of thread. Makes two HTTP request
void* thread_func(void* arg)
{
  for(int i = 1; i <= 2; i++)
  {
    makeRequest();
  }

  pthread_exit(NULL);
}

int main(void)
{
  pthread_t thread1, thread2;

  cout << "Global curl init." << endl;

  //initialize libcurl
  curl_global_init(CURL_GLOBAL_ALL);


  //Start two threads.

  pthread_create(&thread1, NULL, thread_func, NULL);

  pthread_create(&thread2, NULL, thread_func, NULL);

  // Wait for two threads to finish
  pthread_join(thread1, NULL);
  pthread_join(thread2, NULL);

  // Two threads completed. Each would have made two HTTP requests. No. of connection to 10.10.0.1 would be only two.
  // Since each thread creates only one curl handle. Check this using 'netstat -nap | grep 10.10.0.1'


  cout << "Check output of 'netstat -nap | grep 10.10.0.1'" << endl;

  sleep(5);

  cout << "Global curl cleanup." << endl;
  // global libcurl cleanup
  curl_global_cleanup();

}
Output:
Global curl init.
Creating curl easy handle
Creating curl easy handle
Cleaning up easy handle
Cleaning up easy handle
Check output of 'netstat -nap | grep 10.10.0.1'
Global curl cleanup.
 

[root@localhost curl]# netstat -nap | grep 10.223.3.131
tcp        0      0 10.223.3.171:45511          10.10.0.1:80             TIME_WAIT   -
tcp        0      0 10.223.3.171:45512          10.10.0.1:80             TIME_WAIT   -