bionic Condition



然后我们进入源码分析。

bionic将条件变量封装成了一个类,也就成为了Condition

这也是Bionic的设计理念之一,就是让事情变得真正简单化。

 * Condition variables are paired up with mutexes.  Lock the mutex,
 * call wait(), then either re-wait() if things aren't quite what you want,
 * or unlock the mutex and continue.  All threads calling wait() must
 * use the same mutex for a given Condition.

头文件位于//common/frameworks/base/include/utils/Condition.h

class Condition {
public:
    enum {
        PRIVATE = 0,
        SHARED = 1
    };

    Condition();
    Condition(int type);
    ~Condition();
    // Wait on the condition variable.  Lock the mutex before calling.
    status_t wait(Mutex& mutex);
    // same with relative timeout
    status_t waitRelative(Mutex& mutex, nsecs_t reltime);
    // Signal the condition variable, allowing one thread to continue.
    void signal();
    // Signal the condition variable, allowing all threads to continue.
    void broadcast();

private:
#if defined(HAVE_PTHREADS)
    pthread_cond_t mCond;
#else
    void*   mState;
#endif
};

跟Mutex一样,Condition也可以分成两种类型:private和shared

/* Process shared or private flag.  */
enum
{
  PTHREAD_PROCESS_PRIVATE,
#define PTHREAD_PROCESS_PRIVATE PTHREAD_PROCESS_PRIVATE
  PTHREAD_PROCESS_SHARED
#define PTHREAD_PROCESS_SHARED  PTHREAD_PROCESS_SHARED
};

基本的宏

/* We use one bit in condition variable values as the 'shared' flag
 * The rest is a counter.
 */
#define COND_SHARED_MASK        0x0001
#define COND_COUNTER_INCREMENT  0x0002
#define COND_COUNTER_MASK       (~COND_SHARED_MASK)

#define COND_IS_SHARED(c)  (((c)->value & COND_SHARED_MASK) != 0)

相当于pthread_cond_t 长度为4个字节,1bit表示其flag 是private还是shared

其他的都用语表示counter。


1. 基本结构

bionic 针对pthread用到的变量主要是:

typedef struct
{
    int volatile value;
} pthread_cond_t;

typedef struct
{
    uint32_t flags;
    void * stack_base;
    size_t stack_size;
    size_t guard_size;
    int32_t sched_policy;
    int32_t sched_priority;
} pthread_attr_t;

见common/bionic/libc/include/pthread.h


2. 变量初始化

先看一句linux高级编程对条件变量的描述:条件变量是线程可用的另一种同步机制,条件变量给多个线程提供了一个会和的场所,条件变量和互斥量一起使用的,允许线程以无竞争的方式等待特定的条件发生。
条件变量本身是由互斥量保护的,线程在改变条件状态前必须首先锁住互斥量。


int pthread_cond_init(pthread_cond_t *cond,
                      const pthread_condattr_t *attr)
{
    if (cond == NULL)
        return EINVAL;

    cond->value = 0;

    if (attr != NULL && *attr == PTHREAD_PROCESS_SHARED)
        cond->value |= COND_SHARED_MASK;

    return 0;
}

与mutex一样,pthread中,变量和attr相辅相承的,根据attr的信息,修改pthread_cond_t的value。

3. wait

inline status_t Condition::wait(Mutex& mutex) {
    return -pthread_cond_wait(&mCond, &mutex.mMutex);
}

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
    return pthread_cond_timedwait(cond, mutex, NULL);
}

int __pthread_cond_timedwait(pthread_cond_t *cond,
                             pthread_mutex_t * mutex,
                             const struct timespec *abstime,
                             clockid_t clock)
{
    struct timespec ts;
    struct timespec * tsp;

    if (abstime != NULL) {
        if (__timespec_to_absolute(&ts, abstime, clock) < 0)
            return ETIMEDOUT;
        tsp = &ts;
    } else {
        tsp = NULL;
    }

    return __pthread_cond_timedwait_relative(cond, mutex, tsp);
}

int __pthread_cond_timedwait_relative(pthread_cond_t *cond,
                                      pthread_mutex_t * mutex,
                                      const struct timespec *reltime)
{
    int  status;
    int  oldvalue = cond->value;

    pthread_mutex_unlock(mutex);//解锁
    status = __futex_wait_ex(&cond->value, COND_IS_SHARED(cond), oldvalue, reltime);
    pthread_mutex_lock(mutex); //锁住

    if (status == (-ETIMEDOUT)) return ETIMEDOUT;
    return 0;
}

int  __futex_wait_ex(volatile void *ftx, int pshared, int val, const struct timespec *timeout)
{
    return __futex_syscall4(ftx, pshared ? FUTEX_WAIT : FUTEX_WAIT_PRIVATE, val, timeout);
}
这就是调用流程,最后调用了__futex_wait_ex()

我们很奇怪的是为什么通常进入了wait之前还要用pthread_mutex_lock来锁住mutex,难道不会死锁?

但从linux高级编程上的描述来说,pthread_cond_wait是干了这么一件事:函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作都是原子操作。

而当pthread_cond_wait返回时,互斥量再次被锁住。

为什么要绕这么一圈:

pthead_cond_wait之前调用pthread_mutex_lock,其真正目的是为了把mutex的控制权还给调用pthread_cond_wait的线程;



再往底层抠点:以x86为例common/bionic/libc/arch-x86/bionic/futex_x86.S

/*int __futex_syscall4(volatile void *ftx, int op, int val, const struct timespec *timeout) */

.text
.globl __futex_syscall4
.type __futex_syscall4, @function
.align 4
__futex_syscall4:
    pushl   %ebx
    pushl   %esi
    movl    12(%esp), %ebx      /* ftx */
    movl    16(%esp), %ecx      /* op */
    movl    20(%esp), %edx      /* val */
    movl    24(%esp), %esi      /* timeout */
    movl    $__NR_futex, %eax
    int     $0x80
    popl    %esi
    popl    %ebx
    ret

common/bionic/libc/include/sys/linux-syscalls.h中定义了系统调用
#define __NR_futex                        (__NR_SYSCALL_BASE + 240)

其实这个就是调用了futex(ftx, op, val, timeout)

futex 的等待操作可以指定条件。将常量 FUTEX_WAIT 作为参数传递给 futex 作为第二个参数 op 的值,这个时候第三个参数 op 的值将作为一个比较对象。

当 *(int *)futex == val 的时候,调用体将被阻塞直到 timeout 时间到达,或者被其它执行体唤醒。

如果 *(int *)futex != val,则锁定过程会被直接跳过。

至于futex需要继续研究。

4. signal 唤醒等待mCond的线程

inline void Condition::signal() {
    pthread_cond_signal(&mCond);
}

int pthread_cond_signal(pthread_cond_t *cond)
{
    return __pthread_cond_pulse(cond, 1);
}

/* This function is used by pthread_cond_broadcast and
 * pthread_cond_signal to atomically decrement the counter
 * then wake-up 'counter' threads.
 */
static int
__pthread_cond_pulse(pthread_cond_t *cond, int  counter)
{
    long flags;

    if (__unlikely(cond == NULL))
        return EINVAL;

    flags = (cond->value & ~COND_COUNTER_MASK);
    for (;;) {
        long oldval = cond->value;
        long newval = ((oldval - COND_COUNTER_INCREMENT) & COND_COUNTER_MASK)
                      | flags;
        if (__bionic_cmpxchg(oldval, newval, &cond->value) == 0)
            break;
    }

    /*
     * Ensure that all memory accesses previously made by this thread are
     * visible to the woken thread(s).  On the other side, the "wait"
     * code will issue any necessary barriers when locking the mutex.
     *
     * This may not strictly be necessary -- if the caller follows
     * recommended practice and holds the mutex before signaling the cond
     * var, the mutex ops will provide correct semantics.  If they don't
     * hold the mutex, they're subject to race conditions anyway.
     */
    ANDROID_MEMBAR_FULL();

    __futex_wake_ex(&cond->value, COND_IS_SHARED(cond), counter);//这里传递过来的counter = 1;
    return 0;
}

调用__futex_wake_ex来唤醒。
5. broadcast 唤醒所有等待mCond的线程

inline void Condition::broadcast() {
    pthread_cond_broadcast(&mCond);
}
int pthread_cond_broadcast(pthread_cond_t *cond)
{
    return __pthread_cond_pulse(cond, INT_MAX);//

INT_MAX是个超大值






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值