前言
前段时间查一个问题,发现应用层在使用wait函数时,在没有等到信号的情况下,wait函数返回了,并且返回值为0,没有超时及异常提示,不符合常理,跟进后发现,虽然c库代码编写不够严谨,但根源是应用层代码对timer_create的不当使用,引入了隐患。在这做一个分析,作为以后分析同类问题的参考。
一、 wait函数不合理返回问题
如下面代码,在postAndWait函数中,先把task queue进处理队列,然后调用wait等待task处理完成发送信号,接着在run函数中运行task及发送信号,当wait函数收到信号后,正常返回,这为正常的运行流程。但发现有时出现了在run中,task还没运行,也没有发送信号,wait函数就已经返回,并且返回值为0(success)。
frameworks\base\libs\hwui\renderthread\ RenderProxy.cpp
void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) {
void* retval;
task->setReturnPtr(&retval);
SignalingRenderTask syncTask(task, &mSyncMutex, &mSyncCondition);
AutoMutex _lock(mSyncMutex);
mRenderThread.queue(&syncTask); // queue task
mSyncCondition.wait(mSyncMutex); // 等待task运行完成发送信号
return retval;
// 若在task还没运行,wait就返回,task被释放,task运行线程不知道task被释放,一到task运行就出问题
}
frameworks\base\libs\hwui\renderthread\ RenderTask.cpp
void SignalingRenderTask::run() {
mTask->run(); // task的运行
mLock->lock();
mSignal->signal(); // 发送信号给wait
mLock->unlock();
}
二、wait不合理返回分析
跟进内核代码发现,当wait函数在等待时,wait所在的线程被挂起,正常情况下,当task的运行线程给wait所在的线程发送信号后,wait所在的线程被设置为可运行状态,等待系统调度运行并正常返回,wait函数调用路径及返回如下,调用路径如绿色标示的,返回点如紫色标示。(发送信号流程的代码位置与wait流程代码处于相同文件中,可自行跟踪)
system\core\include\utils\ Condition.h
inline status_t Condition::wait(Mutex& mutex) {
return -pthread_cond_wait(&mCond, &mutex.mMutex);
}
bionic\libc\bionic\ Pthread_cond.cpp
int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) {
return __pthread_cond_timedwait(cond, mutex, NULL, COND_GET_CLOCK(cond->value));
}
bionic\libc\bionic\ Pthread_cond.cpp
__LIBC_HIDDEN__
int __pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* abstime, clockid_t clock) {
timespec ts;
timespec* tsp;
if (abstime != NULL) { // 没有设置超时时间,不走这里
if (__timespec_from_absolute(&ts, abstime, clock) < 0) {
return ETIMEDOUT;
}
tsp = &ts;
} else {
tsp = NULL;
}
return __pthread_cond_timedwait_relative(cond, mutex, tsp);
}
bionic\libc\bionic\ Pthread_cond.cpp
__LIBC_HIDDEN__
int __pthread_cond_timedwait_relative(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* reltime) {
int old_value = cond->value;
pthread_mutex_unlock(mutex);
int status = __futex_wait_ex(&cond->value, COND_IS_SHARED(cond->value), old_value, reltime);
pthread_mutex_lock(mutex);
if (status == -ETIMEDOUT) {
return ETIMEDOUT;
}
return 0;
}
bionic\libc\private\ Bionic_futex.h
static inline int __futex_wait_ex(volatile void* ftx, bool shared, int value, const struct timespec* timeout) {
return __futex(ftx, shared ? FUTEX_WAIT : FUTEX_WAIT_PRIVATE, value, timeout);
}
bionic\libc\private\ Bionic_futex.h
static inline __always_inline int __futex(volatile void* ftx, int op, int value, const struct timespec* timeout) {
// Our generated syscall assembler sets errno, but our callers (pthread functions) don't want to.
int saved_errno = errno;
int result = syscall(__NR_futex, ftx, op, value, timeout);
if (__predict_false(result == -1)) {
result = -errno;
errno = saved_errno;
}
return result;
}
kernel\kernel\ Futex.c
SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
struct timespec __user *, utime, u32 __user *, uaddr2,
u32, val3)
{
struct timespec ts;
ktime_t t, *tp = NULL;
u32 val2 = 0;
int cmd = op & FUTEX_CMD_MASK;