bthread remain
这一节把bthread相关的收收尾。把之前留下的坑填一下。
最主要的就是interrupt了
用来打断在butex上的睡眠
注释有说到如果当前bthread没有被阻塞的话,这次interrupt就会被记住,然后在下一次阻塞的时候进行检查。
这里先调用interrupt and consume waiters
通过原子操作把current waiter拿出来。这样我们就可以获得butex上对应的结点。
然后用erase from butex because of interruption把他从butex中取出来
对于并发的情况,这时候两个线程会通过erase from butex来竞争。率先获得waiter lock的人会获胜,因为他会将container存为null,从而让另一个人失败。
如果不是在butex上等待的话,那么就通过timer去调用unschedule,把对应的任务取消掉。然后再把bthread加入到运行队列中
至于为什么要把current waiter设回来,可以看之前的butex中的代码
在被sched回来后,当前的bthread会不断等待直到current waiter变为not null
假如我们这边被调度回来以后直接结束的话,另一边可能正在调用interrupt,即正在使用bw。而我们这里的bw是栈上的对象。所以需要等待其他人都用完以后才能安全释放。
所以butex wait中会等待并发的任务完成后才会释放。unsleep if necessary表示的是如果timer thread同时正在唤醒我们,我们应该等待他完成。下面那个loop就是处理并发的interrupt
可以看到这里的判断是bthread要么阻塞在了butex上,要么阻塞在了sleep上
刚才我们看完了butex的情况,下面的sleep的情况处理也比较容易。就是unschedule一下。但是问题是,这里的sleep对应的是哪里的阻塞操作呢?
是在bthread中的usleep
可以看到这里的逻辑也比较简单。核心就是这个set remained,然后调用sched调度走
sleep event的作用就是添加一个timer thread的任务。让他在一段时间后调用ready to run,也就是恢复执行
并且设置current sleep,用来让我们后面的interrupt可以拿到对应的task id用来unschedule
这里可以看到,我们会判断一下他有没有被interrupted。以及bthread的version,毕竟我们不能随便设置其他的bthread。因为可能这段时间我们的bthread就已经结束执行并被复用了。
之前有说到,如果没有被阻塞的话interrupt会等待下一次阻塞的时候生效。这里就可以看到如果有interrupt我们就会立刻unschedule
可以看到butex中也有类似的判断。如果有interrupt就会结束阻塞
并且butex wait以及usleep都会consume掉interrupt
值得一提的是,我们很多的地方都有butex version的判断。
绝大多数的判断都是这种样式的。即根据tid来得到version,然后判断是否和task meta中的version相同。
一个问题是为什么我们不直接让slot id作为tid呢?因为如果我们回收了一个bthread,对应的资源也就回收了。但是可能这个bthread还有一些callback需要调用task meta,那么很可能他的task meta就是错误的。并且他没有办法知道。
并且由于slot会复用,如果我们让slot id作为tid说明bthread tid也会复用。这会在并发场景下出现很多bug。因为我们不能唯一的标识线程了。
所以为了线程标识符的唯一性,以及为了方便我们辨认bthread,我们通过版本和slot id共同决定一个唯一的tid。在这个bthread结束后,后续的callback会通过version知道这不是原来的bthread,从而放弃操作。
可以看到这个设计还是比较棒的,需要好好考量。
通过version来保证资源复用情况下不会出现并发的问题。这个思想可能在之后的socket中也有用到。
文章评论