0

In the project I am working on , I am required to make a GET call and POST call when ever necessary. The "GET" call is used to create a downchannel from the server to the device to monitor for any interesting events that are happen in the cloud server. The "POST" call is used to send the data to the server. The server has a restriction that only a single connection has to be maintained with the server. Since the server supports HTTP2 , I am using "CURLMOPT_MAX_HOST_CONNECTIONS" and by setting it to 1 , I am making CURL to use 1 connection and I am using CURLMOPT_PIPELINING and by setting it to CURLPIPE_MULTIPLEX , I am multiplexing the transfers. The issue is that the code crashes when "curl_multi_perform" is called on the second thread.

PS: This is a part of my original code and might not compile/run straight away.

#include <stdio.h>
#include <string> 
#include <sstream>
#include <curl.h>
#include <pthread.h>


#define EVENTS_URL "https://avs-alexa-na.amazon.com/v20160207/events"
#define DOWNCHANNEL_URL "https://avs-alexa-na.amazon.com/v20160207/directives"

CURLM *multi_handle;
CURL *downchannel_handle;
CURL *eventHttp_handle;

size_t processDownchannelResponse(void *ptr, size_t size, size_t nmemb, void *instance)
{

  printf(" There is downchannel response.");
}

size_t eventResponse(void *ptr, size_t size, size_t nmemb, void *instance)
{
  printf("We got eventResponse");
}
/* The downchannel thread needs to be running always 
, if the transfer is done the connection is closed 
we need to open a new connection and wait for more events*/

void createDownchannel()
{
  int retryCount =0;
  std::string downchURL = DOWNCHANNEL_URL;
  long response_code;
  int runninghandles =0;

downchannel_handle = curl_easy_init();

if(downchannel_handle == NULL){
  printf("createDownchannel : Not able to create curl handle");  
  return  ; 
}



struct curl_slist *header = NULL;
header = curl_slist_append(header, "Host: avs-alexa-na.amazon.com"); 
header = curl_slist_append(header, "TOKEN GOES HERE");

curl_easy_setopt(downchannel_handle, CURLOPT_URL, downchURL.c_str()); 
curl_easy_setopt(downchannel_handle, CURLOPT_SSL_VERIFYPEER, 0L); 
curl_easy_setopt(downchannel_handle, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(downchannel_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); 
curl_easy_setopt(downchannel_handle, CURLOPT_WRITEFUNCTION, processDownchannelResponse); 
curl_easy_setopt(downchannel_handle, CURLOPT_WRITEDATA, NULL);
curl_easy_setopt(downchannel_handle, CURLOPT_PIPEWAIT, 1L); 

if (header) 
  curl_easy_setopt(downchannel_handle, CURLOPT_HTTPHEADER, header);


if(multi_handle)
/* add the individual easy handle */
  curl_multi_add_handle(multi_handle, downchannel_handle);

mbexitdownchannelthread = 1;

do{
  CURLMcode mc;
  int numfds;
  mc = curl_multi_perform(multi_handle, &runninghandles);
  if(mc != CURLM_OK) {
    fprintf(stderr, "curl_multi failed, code %d.n", mc);
    printf(“a<---curl_multi failed, code %d.n", mc);
    break;
  }
  printf("a<--downchannel while");
// downchannel timeout can be higher  10 seconds
  mc = curl_multi_wait(multi_handle, NULL, 0, 10000, &numfds);
  if(mc != CURLM_OK) {
    fprintf(stderr, "curl_multi failed, code %d.n", mc);
    printf("a<---curl_multi failed, code %d.n", mc);
    break;
  }

}while(!mbexitdownchannelthread);


curl_multi_remove_handle(multi_handle, downchannel_handle);

curl_easy_cleanup(downchannel_handle);

if (header)
  curl_slist_free_all(header);


}

bool getTransferStatus(CURLM *multiHandleInstance,CURL *currentHandle,std::string& contentType,int* http_status_code)
{
//check the status of transfer    CURLcode return_code=0;
  int msgs_left=0;
  CURLMsg *msg=NULL;
  CURL *eh=NULL;
  CURLcode return_code; 

  bool msgTrasferDone = false; 

  while ((msg = curl_multi_info_read(multiHandleInstance, &msgs_left))) 
  {  
    if (msg->msg == CURLMSG_DONE) {
      eh = msg->easy_handle;
      if(currentHandle == eh)
      {
        msgTrasferDone = true;
        return_code = msg->data.result;
        if(return_code!=CURLE_OK) 
        {
//fprintf(stderr, "CURL error code: %d\n", msg->data.result);
          printf("a<--return_code!=CURLE_OK CURL error code: %d\n", msg->data.result);
          continue;
        }  
// Get HTTP status code
        *http_status_code=0;
        char* ch= NULL;
        curl_easy_getinfo(eh, CURLINFO_RESPONSE_CODE, http_status_code);
        printf("a<--CURLINFO_RESPONSE_CODE=%d",*http_status_code);
        curl_easy_getinfo(eh, CURLINFO_CONTENT_TYPE, &ch);
        contentType.clear();
        if(ch!=NULL)
          contentType.append(ch);
      }
    }
  }
  return msgTrasferDone;
}

int eventThread()
{ 
  int still_running = 0;

  std::string contentType;
  int responseCode = 0;
  int numfds = 0; 
  std::string postUrl;
  postUrl.assign(EVENTS_URL);

// init the curl session 
  CURL *eventHttp_handle;
  CURLcode res;
  eventHttp_handle = curl_easy_init();

//assign speech buffer pointer for read callbacks

  curl_easy_setopt(eventHttp_handle, CURLOPT_URL, postUrl.c_str()); 
  curl_easy_setopt(eventHttp_handle, CURLOPT_SSL_VERIFYPEER, 0L); 
  curl_easy_setopt(eventHttp_handle, CURLOPT_SSL_VERIFYHOST, 0L);
  curl_easy_setopt(eventHttp_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); 

/* wait for pipe connection to confirm */ 
  curl_easy_setopt(eventHttp_handle, CURLOPT_PIPEWAIT, 1L); 

  curl_easy_setopt(eventHttp_handle, CURLOPT_WRITEFUNCTION, eventResponse); 
  curl_easy_setopt(eventHttp_handle, CURLOPT_WRITEDATA, NULL);

  struct curl_slist *header = NULL;
  header = curl_slist_append(header, "Host: avs-alexa-na.amazon.com"); 
  header = curl_slist_append(header, "TOKEN GOES HERE");

  if (header) 
    curl_easy_setopt(eventHttp_handle, CURLOPT_HTTPHEADER, header);

  if (multi_handle) 
    curl_multi_add_handle(multi_handle, eventHttp_handle);


  do 
  {     
    {
      CURLMcode mc;
      int numfds;
      mc = curl_multi_perform(multi_handle, &still_running);
      if(mc != CURLM_OK) {
        fprintf(stderr, "curl_multi failed, code %d.n", mc);
        printf("a<---curl_multi failed, code %d.n", mc);
        break;
      }
      mc = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds);
      if(mc != CURLM_OK) {
        fprintf(stderr, "curl_multi failed, code %d.n", mc);
        printf("a<---curl_multi failed, code %d.n", mc);
        break;
      }

    }

  }while(!getTransferStatus(multi_handle,eventHttp_handle,contentType,&responseCode));


  if (header)
    curl_slist_free_all(header);

  if (formpost)
  { 
    curl_formfree(formpost); 
    formpost = NULL; 
  } 

  printf("a<-- CURL  HTTP RESP CODE =%d",responseCode);

  curl_multi_remove_handle(multi_handle, eventHttp_handle); 
  curl_easy_cleanup(eventHttp_handle); 
  return 0;
}






int main ()
{
  multi_handle = curl_multi_init();
  curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
  curl_multi_setopt(multi_handle, CURLMOPT_MAX_HOST_CONNECTIONS, 1L);

// create a downchannel thread
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  int err = pthread_create(&mPostThread, &attr, downchannelThread,NULL);
  pthread_attr_destroy(&attr);

// i am doing some work here //
  sleep(3);

// create a event thread
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  int err = pthread_create(&mPostThread, &attr, eventThread,NULL);
  pthread_attr_destroy(&attr);
}
hungryspider
  • 300
  • 2
  • 13
  • "the code crashes" - the **first** thing you do at that point is review the code while setting up your debugger. Then, run your code in the debugger, reproducing the crash and examining the call-stack and variables to see where your presumption of the code's execution behavior and the *actual* behavior differ. I'd personally start by verifying every invoke is returning a successful result ([Spencer's 6th Commandment](https://www.lysator.liu.se/c/ten-commandments.html)). – WhozCraig Jan 31 '17 at 20:33
  • 1
    Hi Craig, I know where the code crashes, the code crashes exactly when "curl_multi_perform" is called on the second thread. – hungryspider Jan 31 '17 at 20:42
  • 1
    It sounds like you're not adhering to this: https://curl.haxx.se/libcurl/c/threadsafe.html – Daniel Stenberg Jan 31 '17 at 23:09
  • Hi Daniel, nice to interact with you again. From the link that you sent "You must never share the same handle in multiple threads" does that mean I cannot addhandles to same multi_handle in different threads and call CURLmulti_perform ? – hungryspider Jan 31 '17 at 23:38
  • 1
    @DanielStenberg can you please help, I am kind of stuck on how to progress on this. – hungryspider Feb 02 '17 at 03:38
  • Are you asking me if the docs is correct? Yes it is. As long as you make sure you don't use the handles *simultaneously*, you can do that. Ie you can fix it with a mutex or similar. But you could also opt to design your code differently. – Daniel Stenberg Feb 02 '17 at 08:11
  • Sorry Curl is something new for me, excuse me if my questions are too basic. So from what I understand , I need to use a mutex and wait till the first "perform" is completed on the first thread and only then call the "CURLmulti_perform" on the second thread. The problem with this is that the first thread could take anywhere between 30 mins- 1 hour for any event to occur, so until then I would not be able to send events to cloud using my second thread."But you could also opt to design your code differently" could you please share an example code for me to refer . – hungryspider Feb 02 '17 at 14:05
  • Also one more way to solve it (which i was using earlier ) was to create a separate multi handle for two threads(which is as good as creating two easy handles). This approach doesn't crash my system , but this approach creates two connections with the server even though the "hostname" is same. Is there a way for me to force system to reuse single connection. – hungryspider Feb 02 '17 at 14:11

0 Answers0