问题:当向前向后设置系统时间时,sem_timedwait和pthread_cond_timedwait会出现长时间堵塞或一直堵塞的情况;
分析:sem_timedwait()
是 通过传入未来的某个时钟实现超时等待信号量的获取,具体请参考 sem_timedwait(3),当调用sem_timedwait()后,因为系统实时时钟被修改,导致 sem_timedwait() 用于计算是否达到超时时钟的基准时钟向前大幅度偏移而阻塞,sem_timedwait()这个接口就是使用系统实时时钟计算超时的。同样的问题,这种情况下pthread_cond_timedwait就不能用绝对时钟计时方式。
解决方法:除了实时时钟之外,还有一个单调递增时钟,此时钟从某一时刻开始单调递增而不会被修改,详见 clock_gettime(3)。具体实现时,使用 clock_gettime(CLOCK_MONOTONIC, &ts) 获取单调递增时钟,再基于此时钟计算超时时间。所以在会修改系统实时时钟的应用中,需要谨慎使用 sem_timedwait(),而pthread_cond_timedwait时需要相当时间方式。
测试代码和修改代码参考如下:
#include <stdio.h>
#include<sys/time.h>
#include<unistd.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <semaphore.h>
#include <pthread.h>
#include <signal.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <errno.h>
int set_system_time(char *dt)
{
struct tm rtc_time;
struct tm _tm;
struct timeval tv;
time_t timep;
sscanf(dt, "%d-%d-%d %d:%d:%d", &rtc_time.tm_year,
&rtc_time.tm_mon, &rtc_time.tm_mday,&rtc_time.tm_hour,
&rtc_time.tm_min, &rtc_time.tm_sec);
_tm.tm_sec = rtc_time.tm_sec;
_tm.tm_min = rtc_time.tm_min;
_tm.tm_hour = rtc_time.tm_hour;
_tm.tm_mday = rtc_time.tm_mday;
_tm.tm_mon = rtc_time.tm_mon - 1;
_tm.tm_year = rtc_time.tm_year - 1900;
timep = mktime(&_tm);
tv.tv_sec = timep;
tv.tv_usec = 0;
if(settimeofday (&tv, (struct timezone *) 0) < 0)
{
printf("Set system datatime error!/n");
return -1;
}
return 0;
}
void thread_sem_wait()
{
int ret = -1;
int timeoutms = 1000;
struct timeval ltv;
struct timespec ts
sem_t sem;
sem_init(&sem,0,0);
/* Fall back to RTC if realtime clock unavailable */
gettimeofday(<v, 0);
ts.tv_sec = ltv.tv_sec + timeoutms / 1000;
ts.tv_nsec = (ltv.tv_usec + ((timeoutms % 1000) * 1000)) * 1000;
while ((ret = sem_timedwait(&sem, &ts)) == -1 && errno == EINTR){
continue;
}
printf("sem timewait:timeout\n")
}
void thread_cond_timedwait()
{
pthread_cond_t cond;
pthread_mutex_t mutex;
struct timeval now;
struct timespec waittime;
int waitRet = 0;
int msec = 100;
mtos_task_sleep(1000);
pthread_mutex_init(&mutex, NULL);
#if 1 /***relative time mode****/
pthread_condattr_init(&cattr);
pthread_condattr_setclock(&cattr, CLOCK_MONOTONIC);
pthread_cond_init(&cond, &cattr);
#else /**abstime mode***/
pthread_cond_init(&cond, NULL);
#endif
#if 1 /***relative time mode****/
clock_gettime(CLOCK_MONOTONIC, &waittime);
waittime.tv_sec += 0;
waittime.tv_nsec += (msec * 1000) * 1000;
#else /**abstime mode***/
gettimeofday(&now, NULL);
waittime.tv_sec = now.tv_sec;
waittime.tv_nsec = (now.tv_usec + msec * 1000) * 1000;
#endif
pthread_mutex_lock(&mutex);
waitRet = -1;
while(waitRet != ETIMEDOUT)
{
waitRet = pthread_cond_timedwait(&cond, &mutex, &waittime);
}
pthread_mutex_unlock(&mutex);
printf("sem timewait:timeout\n")
}
void msleep(unsigned int mSec) // milliseconds
{
int ret = -1;
struct timespec ts = {0};
ts.tv_sec = mSec / 1000;
ts.tv_nsec = (mSec % 1000) * 1000000;
while (ret = nanosleep(&ts, &ts) == -1 && errno == EINTR){
continue;
}
}
void thread_function(sem_t *p_sem,u32 mSec)
{
set_system_time("2018-8-8 09:00:0");
msleep(50);
set_system_time("2017-8-8 09:00:0");
}
int my_sem_timedwait(sem_t *p_sem,u32 mSec)
{
int ret = -1;
u32 timeCnt = 0;
timeCnt = 0;
do{
ret = sem_trywait(p_sem);
if(ret != 0)
{
struct timespec timeParam = {0};
timeParam.tv_sec = 0;
timeParam.tv_nsec = 1000000;
while (nanosleep(&timeParam, &timeParam) < 0 && errno == EINTR)
{
continue;
}
timeCnt ++;
if(timeCnt >= mSec)
{
return -1;
}
}
}while(ret != 0);
return 0;
}