How to set a timeout for a function in C?
I have a requirement that I have to give xx ms to execute a functio开发者_如何转开发n. After xx ms I have to abort that function. Please help me how to implement it in C.
I think the nicest way to do this involves pthreads. Start the calculation that potentially may need to be cancelled in its own thread, and in the main thread use pthread_cond_timedwait:
#include <time.h>
#include <pthread.h>
#include <stdio.h>
/* for ETIMEDOUT */
#include <errno.h>
#include <string.h>
pthread_mutex_t calculating = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t done = PTHREAD_COND_INITIALIZER;
void *expensive_call(void *data)
{
int oldtype;
/* allow the thread to be killed at any time */
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
/* ... calculations and expensive io here, for example:
* infinitely loop
*/
for (;;) {}
/* wake up the caller if we've completed in time */
pthread_cond_signal(&done);
return NULL;
}
/* note: this is not thread safe as it uses a global condition/mutex */
int do_or_timeout(struct timespec *max_wait)
{
struct timespec abs_time;
pthread_t tid;
int err;
pthread_mutex_lock(&calculating);
/* pthread cond_timedwait expects an absolute time to wait until */
clock_gettime(CLOCK_REALTIME, &abs_time);
abs_time.tv_sec += max_wait->tv_sec;
abs_time.tv_nsec += max_wait->tv_nsec;
pthread_create(&tid, NULL, expensive_call, NULL);
/* pthread_cond_timedwait can return spuriously: this should
* be in a loop for production code
*/
err = pthread_cond_timedwait(&done, &calculating, &abs_time);
if (err == ETIMEDOUT)
fprintf(stderr, "%s: calculation timed out\n", __func__);
if (!err)
pthread_mutex_unlock(&calculating);
return err;
}
int main()
{
struct timespec max_wait;
memset(&max_wait, 0, sizeof(max_wait));
/* wait at most 2 seconds */
max_wait.tv_sec = 2;
do_or_timeout(&max_wait);
return 0;
}
you can compile and run this on linux with:
$ gcc test.c -pthread -lrt && ./a.out
do_or_timeout: calculation timed out
->include time.h
->take two variable for start time & current time of type time_t
like time_t start_time,current_time
-> take start time
time(&start_time);
now in while loop continuisly check for
time(¤t_time)
difftime(current_time,start_time)
if difftime's return value is 15ms break while loop & close your program
If you're not using pthreads you can also do a similar timeout function using the Apache Portable Runtime: http://apr.apache.org/docs/apr/1.4/group__apr__thread__proc.html
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "apr.h"
#include "apr_thread_proc.h"
#include "apr_time.h"
void *APR_THREAD_FUNC expensive_call(apr_thread_t *thread, void *data)
{
(void)thread;
bool *done = data;
/* ... calculations and expensive io here, for example:
* infinitely loop
*/
for (;;) {}
// signal caller that we are done
*done = true;
return NULL;
}
bool do_or_timeout(apr_pool_t *pool, apr_thread_start_t func, int max_wait_sec)
{
apr_thread_t *thread;
bool thread_done = false;
apr_thread_create(&thread, NULL, func, &thread_done, pool);
apr_time_t now = apr_time_now();
for (;;) {
if (thread_done) {
apr_thread_join(NULL, thread);
return true;
}
if (apr_time_now() >= now + apr_time_make(max_wait_sec, 0)) {
return false;
}
// avoid hogging the CPU in this thread
apr_sleep(10000);
}
}
int main(void)
{
// initialize APR
apr_initialize();
apr_pool_t *ap;
if (apr_pool_create(&ap, NULL) != APR_SUCCESS) {
exit(127);
}
// try to do the expensive call; wait up to 3 seconds
bool completed = do_or_timeout(ap, expensive_call, 3);
if (completed) {
printf("expensive_call completed\n");
} else {
printf("expensive_call timed out\n");
}
apr_terminate();
return 0;
}
Compile using a command like this
gcc -o example example.c -lapr-1
I do not know that architecture so I can give you only a general hint. I would try something similar to old Symbian TRAP mechanism.
In the main routine:
- start a timer.
- put away a stack pointer
- put away a program counter.
- call your function.
In timer exception (interrupt) handling routine. This is a bit tricky because you need to know where in given architecture stack pointers and program counters are kept (processor's datasheet) when exception handling kicks in. Program counter was most likely pushed to the main routine stack. So your steps are:
- replace the stack pointer value (for main routine) with your copied value.
- replace the program counter value with your copied value + offset (because you want to return to execution after your function call - best to check the assembly code to be sure how big is it).
- return from exception (interrupt) handling routine.
@Bobby Powers answer is work, but need a little change as below
if (!err)
pthread_mutex_unlock(&calculating);
-> change to
pthread_mutex_unlock(&calculating);
as @T.D. Smith says
and need add
pthread_cancel(tid) // if isn't add, the expensive_call may never exit if your function couldn't exit by itself, such as for (;;) {} or something block operation.
精彩评论