0

I am building an application where in I receive socket data. I need to reply this received data after few seconds(say 8 sec after). So I want to know is there a way to schedule an event which sends the socket data after 8 seconds automatically. I don't like to sleep unnecessarily for 8 seconds in the receiving thread or any other thread. This is what I have written so far for receiving socket data which is a pthread.

long DataSock_fd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

    StSocketAddress.sin_family=AF_INET; //address family
    StSocketAddress.sin_addr.s_addr=inet_addr("10.10.10.10");   //load ip address
    StSocketAddress.sin_port=htons(1234);   //load port number
    //bind the above socket to the above mentioned address, if result is less than 0(error in binding)
    if(bind(DataSock_fd,(struct sockaddr *)&StSocketAddress,sizeof(StSocketAddress))<0)
    {
        close(DataSock_fd); //close the socket
        perror("error while binding\n");
        exit(EXIT_FAILURE); //exit the program
    }

char Buff[1024];
long lSize = recvfrom(DataSock_fd,(char *)Buff,sizeof(Buff),0,NULL,NULL);

But I am stuck at scheduling an event that sends data after 8 seconds.

Harry
  • 2,177
  • 1
  • 19
  • 33
  • 1
    You can always schedule a `SIGALRM`. However your alarm handler must be signal-safe. If you don't know what that means, that's not going to be an option for you. Except for that, the only way for an execution thread to do something after a prescribed period of time, is to `sleep()`, or `poll()`, or `select()`, for the prescribed period of time (after making arrangements that nothing else polled/selected will occur). Those are your choices. – Sam Varshavchik Aug 08 '17 at 11:38
  • @Sam Can you tell me how to do using SIGALRM signal. Thanks – Harry Aug 08 '17 at 11:47
  • You can have your main thread be the one that receives data and throws it into a "task queue" which is handled by some thread pool. Using a condition variable your thread pool can then sleep until the earliest 8 seconds have passed and the first task is ready to processed. Your queue will always be sorted by earliest start time. Nevermind, I saw that you can't use C++11. – jakedipity Aug 08 '17 at 11:49
  • Your use of socket IO combined with timers suggests your code would be a good candidate for [boost:asio](http://www.boost.org/doc/libs/1_64_0/doc/html/boost_asio.html). – G.M. Aug 08 '17 at 18:18
  • Was your Google broken? Searching for SIGALRM gives you everything you need to know. – Sam Varshavchik Aug 09 '17 at 02:13

4 Answers4

1

Take a look at this SO answer.

You could use <async> like this to solve your problem:

auto f = std::async(std::launch::async, [] {
    std::this_thread::sleep_for(std::chrono::seconds(5));
    printf("(5 seconds later) Hello");
});
Määäxx
  • 19
  • 1
  • 4
  • I am using RHEL 5, so compiler doesn't support C++11 standard. Any other option? – Harry Aug 08 '17 at 11:46
  • I'd suggest you look for a `pthread` C++ wrapper, that doesn't require C++11 or, if you don't mind installing parts of the boost library, go for `boost::thread`. – Määäxx Aug 08 '17 at 12:33
0

you can either use boost::sleep, or chrono:: sleep_for or chrono:: sleep_until, but if you don't want to call sleep, my best suggestion for you is to use std::mutex and lock the thread that receive the information from Time.currenttime -startTime == 8.

maayanwis
  • 5
  • 3
0

Approach-1

Since you don't have a C++11 enabled compiler, and am assuming you are not using frameworks such as Qt/boost etc.. Please check if the following code answer your question. It is a simple async timer implementation using pthreads

Sample code:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>

#define TIME_TO_WAIT_FOR_SEND_SECS (8)
#define FAIL_STATUS_CODE (-1)
#define SUCCESS_STATUS_CODE (0)

typedef void (*TimerThreadCbk)(void *);
typedef struct tTimerThreadInitParams
{
   int m_DurationSecs; /* Duration of the timer */
   TimerThreadCbk m_Callback; /* Timer callback */
   void * m_pAppData; /* App data */

}tTimerThreadInitParams;

void PrintCurrTime()
{
    time_t timer;
    char buffer[26];
    struct tm* tm_info;

    time(&timer);
    tm_info = localtime(&timer);

    strftime(buffer, 26, "%Y-%m-%d %H:%M:%S", tm_info);
    puts(buffer);
}

void* TimerThreadEntry(void *a_pTimerThreadInitParams)
{
  tTimerThreadInitParams *pTimerThreadInitParams = (tTimerThreadInitParams *)a_pTimerThreadInitParams;
  if(NULL != pTimerThreadInitParams)
  {
    /*Do validattion of init params */
    sleep(pTimerThreadInitParams->m_DurationSecs);
    pTimerThreadInitParams->m_Callback(pTimerThreadInitParams->m_pAppData);
  }
  else
  {
     printf("pTimerThreadInitParams is (nil)\n");
  }   
}

TimerCallbackForSend(void *a_pAppData)
{
   (void)a_pAppData;
   /* Perform action on timer expiry using a_pAppData */
   printf("TimerCallbackForSend trigggered at: ");
   PrintCurrTime();
}

int main()
{
   /* Timer thread initialization parameters */
   pthread_t TimerThread;
   tTimerThreadInitParams TimerInitParams = {};
   TimerInitParams.m_DurationSecs = TIME_TO_WAIT_FOR_SEND_SECS;
   TimerInitParams.m_Callback = (TimerThreadCbk) TimerCallbackForSend;

   /* Print current time */ 
   printf("Starting timer at:");
   PrintCurrTime();

   /* Create timer thread*/
   if(pthread_create(&TimerThread, NULL, TimerThreadEntry, &TimerInitParams)) 
   {
     fprintf(stderr, "Error creating thread\n");
     return FAIL_STATUS_CODE;
   }
   else
   {
     printf("TimerThread created\n");
   }

   /* wait for the second thread to finish */
   if(pthread_join(TimerThread, NULL))
   {
     fprintf(stderr, "Error joining thread\n");
     return FAIL_STATUS_CODE;
   }
   else
   {
      printf("TimerThread finished\n");
   }
   return SUCCESS_STATUS_CODE;
}

Sample output:

Starting timer at:2017-08-08 20:55:33
TimerThread created
TimerCallbackForSend trigggered at: 2017-08-08 20:55:41
TimerThread finished

Notes:

This is a scratch custom implementation. You can rename main as ScheduleTimer, which will be a generic API which spawns a thread and invokes the registered callback in its own context.

Just now saw that you don't want to sleep in any of the threads.

Approach-2

Refer C: SIGALRM - alarm to display message every second for SIGALRM. May be in the signal handler you can post an event to the queue which your thread will be monitoring

Vivek Maran
  • 2,623
  • 5
  • 38
  • 52
0

Sleeping, whether by a C++ wrapper or by the system's nanosleep function -- it cannot be said often enough -- is... wrong. Unless precision and reliability doesn't matter at all, do not sleep. Never.
For anything related to timing, use a timer.

If portability is not a high priority, and since the question is tagged "Linux", a timerfd would be one of the best solutions.

The timerfd can be waited upon with select/poll/epoll while waiting for something to be received, and other stuff (signals, events) at the same time. That's very elegant, and it is quite performant, too.

Admitted, since you are using UDP, there is the temptation to not wait for readiness in the first place but to just have recvfrom block. There is however nothing inherently wrong with waiting for readiness. For moderate loads, the extra syscall doesn't matter, but for ultra-high loads, you might even consider going a step further into non-portable land and use recvmmsg to receive several datagrams in one go as indicated by the number of datagrams reported by epoll (see code example on the recvmmsg man page, which combines recvmmsg with epoll_wait).

With an eventfd, you have everything in one single event loop, in one single thread, reliable and efficient. No trickery needed, no need to be extra smart, no worries about concurrency issues.

Damon
  • 67,688
  • 20
  • 135
  • 185