brpc源码学习(一)-butex

由于brpc中引入了bthread,如果在bthread中使用了mutex,那么将会挂起当前pthread,导致该bthread_worker无法执行其他bthread,因此类似pthread和futex的关系,brpc引入butex来实现bthread粒度的挂起和唤醒。

首先看下butex中使用到的FastPthreadMutex,FastPthreadMutex是基于futex实现的pthread粒度的锁,当竞争不激烈时,lock和unlock操作都是通过修改一个用户态的atomic来实现,只有当竞争激烈的时候才会陷入内核进行挂起和wake。不过互斥锁也是这么实现的,不是太理解重新实现一个的目的…

如果开启了BTHREAD_USE_FAST_PTHREAD_MUTEX宏定义,使用的是FastPthreadMutex,否则使用pthread_mutex_t。
首先看下lock方法
brpc源码学习(一)-butex_第1张图片
首先尝试修改locked这个atomic,如果发现锁没被占用,那么直接返回,否则调用lock_contended方法。注意这里使用了memory_order_acquire的memory order,和lock中的release形成syncwith关系,保证了当前线程获得锁之后能看到上个线程在释放锁之前对内存的修改。
brpc源码学习(一)-butex_第2张图片
当发现当前锁被占用时,通过系统调用futex_wait_private将当前线程挂起到whole这个atomic对应的队列中。
brpc源码学习(一)-butex_第3张图片
unlock方法通过futex_wake_private唤醒whole对应队列中的一个pthread,如上所述,这里使用release保证当前线程对内存的修改能被后续竞争到锁的线程看到。

然后看下butex,butex的实现类似futex。其中ButexWaiterList是一个链表,保存了在该butex上挂起的bthread或pthread。
waiter_lock即上述的锁。因为butex_wait时会比较atomic是否为expect_value,如果相等,那么将会挂起当前bthread至等待队列。如果判断是否相等和挂起这两个操作不在同一个临界区,那么有可能在判断之后但是挂起前已经执行过了butex_wake,将出现信号丢失问题,因此这里需要使用互斥锁。
brpc源码学习(一)-butex_第4张图片
然后看下butex_wait的过程
brpc源码学习(一)-butex_第5张图片
如果当前线程不是bthread_worker,那么直接调用butex_wait_from_pthread。
如果当前是bthread,那么直接在该bthread栈上创建ButexBthreadWaiter

brpc源码学习(一)-butex_第6张图片
然后设置remain,即pthread执行下一个bthread之前需要做的事情,设置完成后切到其他bthread上继续执行。664行为当前bthread恢复后开始执行的地方。然后看下remain,即wait_for_butex中做的工作。
brpc源码学习(一)-butex_第7张图片
首先在临界区中再次判断下expect_value是否等于butex中的value,如果相等,才把该waiter加入到butex的等待队列中;如果不相等,则将waiter重新加入到rq中等待调度。
brpc源码学习(一)-butex_第8张图片
然后是butex_wake的过程。
在该butex的等待队列中唤醒第一个ButexWaiter front,如果front是pthread,那么调用wakeup_pthread;
然后如果当前pthread是bthread_worker,那么直接让出当前bthread,直接执行被唤醒的bthread,如果当前pthread不是bthread_worker,那么就把当前bthread加入到某个task_group的remote_rq中。

你可能感兴趣的:(brpc,计算机体系结构)