这部分代码已经是最终插入的那部分代码,在这之前还有很多流程是在其它文章分析。
log按时间进行排序很重要,时间乱序的的log可能会干扰查问题。所以安卓系统对log的时间戳进行了一些特别的设计
// assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
//这里假设已经加锁了
void LogBuffer::log(LogBufferElement* elem) {
// cap on how far back we will sort in-place, otherwise append
//log插入需要按时间排序,但是如果时间差超过一定限度too_far_back之后就不排序,直接插到队列尾部
static uint32_t too_far_back = 5; // five seconds
// Insert elements in time sorted order if possible
//要尽可能的按照时间顺序插入log
// NB: if end is region locked, place element at end of list
LogBufferElementCollection::iterator it = mLogElements.end();
LogBufferElementCollection::iterator last = it;
if (__predict_true(it != mLogElements.begin())) --it;
if (__predict_false(it == mLogElements.begin()) ||
__predict_true((*it)->getRealTime() <= elem->getRealTime()) ||
__predict_false((((*it)->getRealTime().tv_sec - too_far_back) >
elem->getRealTime().tv_sec) &&
(elem->getLogId() != LOG_ID_KERNEL) &&
((*it)->getLogId() != LOG_ID_KERNEL))) {
mLogElements.push_back(elem);
} else {
log_time end(log_time::EPOCH);
//log_time::EPOCH = {0, 0};
bool end_set = false;
bool end_always = false;
LogTimeEntry::rdlock();
LastLogTimes::iterator times = mTimes.begin();
//mTimes保存的是reader客户端创建的时间
while (times != mTimes.end()) {
LogTimeEntry* entry = times->get();
if (!entry->mNonBlock) {
end_always = true;//只要有一个客户端以堵塞模式读的话就直接插入到尾部,详见后文
break;
}
// it passing mEnd is blocked by the following checks.
if (!end_set || (end <= entry->mEnd)) {
end = entry->mEnd;
end_set = true;
}
times++;
}
if (end_always || (end_set && (end > (*it)->getRealTime()))) {
mLogElements.push_back(elem);
} else {
// should be short as timestamps are localized near end()
//找到要插入的位置
do {
last = it;
if (__predict_false(it == mLogElements.begin())) {
break;
}
--it;
} while (((*it)->getRealTime() > elem->getRealTime()) &&
(!end_set || (end <= (*it)->getRealTime())));
mLogElements.insert(last, elem);
}
LogTimeEntry::unlock();
}
stats.add(elem);
maybePrune(elem->getLogId());
}
客户端堵塞读的处理
源码中我们看到如果 !entry->mNonBlock 为真,即有客户端在阻塞地读取 log时,就把把新的 log 直接插入列表的末尾,没有进行排序。这主要是考虑这样一种比较极端的情况,它已经读取了所有的 log 并等待新的 log,我们又没有把新的 log 放入列表的末尾,就会导致客户端无法读取新写入的这条 log,毕竟,此时它应该读列表最后面的 log。另一种情况是,有客户端在读 log,并且它读到的最后一条 log 已经超过了我们正要写入的 log。此时最简单的做法就是把新 log 放到末尾,这样客户才能读取到新写入的 log。
其它知识点:
__predict_true 和 __predict_false
__predict_true 和 __predict_false 用来提示编译器对应的判断很可能是 true/false,类似于 Linux 内核的 likely/unlikely。如果判断正确,可以得到很大的性能提升。