AndroidO bluedroid alarm 机制分析
bluedroid的alarm 机制实现在osi/osi/src/alarm.cc 中:
这里面实现了很多的接口:
alarm_t* alarm_new(const char* name);
alarm_t* alarm_new_periodic(const char* name) ;
static alarm_t* alarm_new_internal(const char* name, bool is_periodic) ;
void alarm_free(alarm_t* alarm);
void alarm_set(alarm_t* alarm, period_ms_t interval_ms, alarm_callback_t cb,
void* data) ;
void alarm_cancel(alarm_t* alarm);
void alarm_cleanup(void);
static bool lazy_initialize(void) ;
我们先看一个使用alarm 的事例:
在hci_layer.cc 文件中关于hci_module_start_up 的实现:
startup_timer = alarm_new("hci.startup_timer");
...
alarm_set(startup_timer, startup_timeout_ms, startup_timer_expired, NULL);
当startup_timeout_ms 时间到达的时候,如果startup_timer还没有被取消的话,那么startup_timer_expired函数将会被执行。
我们具体看看 其实现的过程:
我们先看看新建定时器的代码:
alarm_t* alarm_new(const char* name) { return alarm_new_internal(name, false); }
static alarm_t* alarm_new_internal(const char* name, bool is_periodic) {
// Make sure we have a list we can insert alarms into.
if (!alarms && !lazy_initialize()) { //最初alarm 还没有初始化,需要执行初始化流程
CHECK(false); // if initialization failed, we should not continue
return NULL;
} alarm_t* ret = static_cast<alarm_t*>(osi_calloc(sizeof(alarm_t))); ret->callback_mutex = new std::recursive_mutex;
ret->is_periodic = is_periodic;
ret->stats.name = osi_strdup(name);
// NOTE: The stats were reset by osi_calloc() above return ret;
}
我们继续看看 lazy_initialize 是如何 初始化的:
static bool lazy_initialize(void) { bool timer_initialized = false;
bool wakeup_timer_initialized = false; std::lock_guard<std::mutex> lock(alarms_mutex); alarms = list_new(NULL); if (!timer_create_internal(CLOCK_ID, &timer)) goto error;//timer_create
timer_initialized = true; if (!timer_create_internal(CLOCK_ID_ALARM, &wakeup_timer)) goto error;
wakeup_timer_initialized = true; alarm_expired = semaphore_new();//新建信号量 default_callback_thread =
thread_new_sized("alarm_default_callbacks", SIZE_MAX);//新建线程 thread_set_rt_priority(default_callback_thread, THREAD_RT_PRIORITY);//提高线程优先级
default_callback_queue = fixed_queue_new(SIZE_MAX);//新建队列 alarm_register_processing_queue(default_callback_queue,
default_callback_thread);//将线程和队列绑定 dispatcher_thread_active = true;
dispatcher_thread = thread_new("alarm_dispatcher");//新建定时器分发线程,该线程不停运行while(true) thread_set_rt_priority(dispatcher_thread, THREAD_RT_PRIORITY);
thread_post(dispatcher_thread, callback_dispatch, NULL);//运行callback_dispatch
return true; error:
...
return false;
}
这里有两个线程,一个是dispatcher_thread,它一直轮询,如果有定时器到期那么他就将这个定时器放到一个(当初创建啊定时器的时候关联的队列)特定的队列里面,默认的就是default_callback_queue ,然后由另外一个线程default_callback_thread 来处理该队列里面的已经expire的定时器.
我们看看timer_create_internal的实现:
static bool timer_create_internal(const clockid_t clock_id, timer_t* timer) {
CHECK(timer != NULL); struct sigevent sigevent;
// create timer with RT priority thread
pthread_attr_t thread_attr;
pthread_attr_init(&thread_attr);
pthread_attr_setschedpolicy(&thread_attr, SCHED_FIFO);
struct sched_param param;
param.sched_priority = THREAD_RT_PRIORITY;
pthread_attr_setschedparam(&thread_attr, ¶m); memset(&sigevent, , sizeof(sigevent));
sigevent.sigev_notify = SIGEV_THREAD;
sigevent.sigev_notify_function = (void (*)(union sigval))timer_callback;//发送信号量
sigevent.sigev_notify_attributes = &thread_attr;
if (timer_create(clock_id, &sigevent, timer) == -) { //创建定时器
/*错误处理*/
}
return false;
} return true;
}
这里我们注意到,当定时器到期的时候,会执行timer_callback,其就是发送了一个alarm_expired的信号量:
static void timer_callback(UNUSED_ATTR void* ptr) {
semaphore_post(alarm_expired);
}
这里有发送信号量,那么一定有一个地方会等待这个信号量,就是在定时器的不断等待的线程里面:
static void callback_dispatch(UNUSED_ATTR void* context) {
while (true) {
semaphore_wait(alarm_expired);//一直循环等待信号量
...
}
}
lazy_initialize 初始化完成之后,定时器并没有启动,只是创建了定时器.那是在哪里启动的呢?根据我们上面展示出的使用示例,在alarm_set 肯定是有启动定时器的操作的:
void alarm_set(alarm_t* alarm, period_ms_t interval_ms, alarm_callback_t cb,
void* data) {
alarm_set_on_queue(alarm, interval_ms, cb, data, default_callback_queue);
}
这里发现 调用alarm_set 传入的 队列都是default_callback_queue,
void alarm_set_on_queue(alarm_t* alarm, period_ms_t interval_ms,
alarm_callback_t cb, void* data, fixed_queue_t* queue) {
CHECK(queue != NULL);
alarm_set_internal(alarm, interval_ms, cb, data, queue);
}
// Runs in exclusion with alarm_cancel and timer_callback.
static void alarm_set_internal(alarm_t* alarm, period_ms_t period,
alarm_callback_t cb, void* data,
fixed_queue_t* queue) { std::lock_guard<std::mutex> lock(alarms_mutex); alarm->creation_time = now();
alarm->period = period;
alarm->queue = queue;
alarm->callback = cb;
alarm->data = data; schedule_next_instance(alarm);
alarm->stats.scheduled_count++;
}
上面是对定时器进行封装以及赋值,然后调用schedule_next_instance 来启动 定时器:
// Must be called with |alarms_mutex| held
static void schedule_next_instance(alarm_t* alarm) {
// If the alarm is currently set and it's at the start of the list,
// we'll need to re-schedule since we've adjusted the earliest deadline.
bool needs_reschedule =
(!list_is_empty(alarms) && list_front(alarms) == alarm);//如果alarms 队列第一个元素就是这个定时器,那么需要重启schedule
if (alarm->callback) remove_pending_alarm(alarm);//取出所有的pending,重复的alarm // Calculate the next deadline for this alarm
period_ms_t just_now = now();
period_ms_t ms_into_period = ;
if ((alarm->is_periodic) && (alarm->period != ))
ms_into_period = ((just_now - alarm->creation_time) % alarm->period);
alarm->deadline = just_now + (alarm->period - ms_into_period); // Add it into the timer list sorted by deadline (earliest deadline first).//以下是给alarm排序,插入到某个合适的问题,最近的alarm 排在第一个
if (list_is_empty(alarms) ||
((alarm_t*)list_front(alarms))->deadline > alarm->deadline) {
list_prepend(alarms, alarm);
} else {
for (list_node_t* node = list_begin(alarms); node != list_end(alarms);
node = list_next(node)) {
list_node_t* next = list_next(node);
if (next == list_end(alarms) ||
((alarm_t*)list_node(next))->deadline > alarm->deadline) {
list_insert_after(alarms, node, alarm);
break;
}
}
} // If the new alarm has the earliest deadline, we need to re-evaluate our
// schedule.
if (needs_reschedule ||
(!list_is_empty(alarms) && list_front(alarms) == alarm)) {
reschedule_root_alarm();
}
}
上面主要就是将alarm插入到 alarms 列表中,等待schedule,如果当前这个alarm 就是最紧迫的alarm,那么就会立即进行 schedule.
我们看看其实现reschedule_root_alarm;
// NOTE: must be called with |alarms_mutex| held
static void reschedule_root_alarm(void) {
CHECK(alarms != NULL); const bool timer_was_set = timer_set;
alarm_t* next;
int64_t next_expiration; // If used in a zeroed state, disarms the timer.
struct itimerspec timer_time;
memset(&timer_time, , sizeof(timer_time)); if (list_is_empty(alarms)) goto done; next = static_cast<alarm_t*>(list_front(alarms));
next_expiration = next->deadline - now();
if (next_expiration < TIMER_INTERVAL_FOR_WAKELOCK_IN_MS) {//如果deadline<3s> timer_time.it_value.tv_sec = (next->deadline / );
timer_time.it_value.tv_nsec = (next->deadline % ) * 1000000LL; /*下面设置最长的wake_up 是为了减少删除该timer的开销,可以略过*/
struct itimerspec end_of_time;
memset(&end_of_time, , sizeof(end_of_time));
end_of_time.it_value.tv_sec = (time_t)(1LL << (sizeof(time_t) * - ));
timer_settime(wakeup_timer, TIMER_ABSTIME, &end_of_time, NULL);
} else {
// WARNING: do not attempt to use relative timers with *_ALARM clock IDs
// in kernels before 3.17 unless you have the following patch:
// https://lkml.org/lkml/2014/7/7/576
struct itimerspec wakeup_time;
memset(&wakeup_time, , sizeof(wakeup_time)); wakeup_time.it_value.tv_sec = (next->deadline / );
wakeup_time.it_value.tv_nsec = (next->deadline % ) * 1000000LL;
if (timer_settime(wakeup_timer, TIMER_ABSTIME, &wakeup_time, NULL) == -)
LOG_ERROR(LOG_TAG, "%s unable to set wakeup timer: %s", __func__,
strerror(errno));
} done:
timer_set =
timer_time.it_value.tv_sec != || timer_time.it_value.tv_nsec != ;
if (timer_was_set && !timer_set) {
wakelock_release();
} if (timer_settime(timer, TIMER_ABSTIME, &timer_time, NULL) == -)
LOG_ERROR(LOG_TAG, "%s unable to set timer: %s", __func__, strerror(errno));//设置定时器 // If next expiration was in the past (e.g. short timer that got context
// switched) then the timer might have diarmed itself. Detect this case and
// work around it by manually signalling the |alarm_expired| semaphore.
//
// It is possible that the timer was actually super short (a few
// milliseconds) and the timer expired normally before we called
// |timer_gettime|. Worst case, |alarm_expired| is signaled twice for that
// alarm. Nothing bad should happen in that case though since the callback
// dispatch function checks to make sure the timer at the head of the list
// actually expired.
if (timer_set) {
struct itimerspec time_to_expire;
timer_gettime(timer, &time_to_expire);
if (time_to_expire.it_value.tv_sec == &&
time_to_expire.it_value.tv_nsec == ) { semaphore_post(alarm_expired);//如果定时器的时机已经到了,那么直接发送信号量
}
}
}
代码是实现是 在离expire 不到3s的时候启动定时器.
当定时器时间到的时候,发动alarm_expired的信号.
我们接下来看看 定时器的 已经到期的处理流程:上面我们已经知道,线程dispatcher_thread一直轮询,我们看看其实现:
// Function running on |dispatcher_thread| that performs the following:
// (1) Receives a signal using |alarm_exired| that the alarm has expired
// (2) Dispatches the alarm callback for processing by the corresponding
// thread for that alarm.
static void callback_dispatch(UNUSED_ATTR void* context) {
while (true) {
semaphore_wait(alarm_expired);//等待expire的信号量
if (!dispatcher_thread_active) break; std::lock_guard<std::mutex> lock(alarms_mutex);
alarm_t* alarm; // Take into account that the alarm may get cancelled before we get to it.
// We're done here if there are no alarms or the alarm at the front is in
// the future. Exit right away since there's nothing left to do.
if (list_is_empty(alarms) ||
(alarm = static_cast<alarm_t*>(list_front(alarms)))->deadline > now()) {
reschedule_root_alarm();
continue;
} list_remove(alarms, alarm);//remove 该alarm 从队列中 if (alarm->is_periodic) {
alarm->prev_deadline = alarm->deadline;
schedule_next_instance(alarm);
alarm->stats.rescheduled_count++;
}
reschedule_root_alarm();//去启动下一个定时器 // Enqueue the alarm for processing
fixed_queue_enqueue(alarm->queue, alarm);//将该expire的定时器放到相应队列等待处理
} LOG_DEBUG(LOG_TAG, "%s Callback thread exited", __func__);
}
这个函数做的事情,就像其名字一样,收到expire的信号之后,做一个dispatch的动作,我们接下来看看放置到队列之后如何处理的.
这里我们还要看一下当时队列和线程绑定的情况:
void alarm_register_processing_queue(fixed_queue_t* queue, thread_t* thread) {
CHECK(queue != NULL);
CHECK(thread != NULL); fixed_queue_register_dequeue(queue, thread_get_reactor(thread),
alarm_queue_ready, NULL);
}
我们看看alarm_queue_ready:
static void alarm_queue_ready(fixed_queue_t* queue, UNUSED_ATTR void* context) {
CHECK(queue != NULL); std::unique_lock<std::mutex> lock(alarms_mutex);
alarm_t* alarm = (alarm_t*)fixed_queue_try_dequeue(queue);
if (alarm == NULL) {
return; // The alarm was probably canceled
} //
// If the alarm is not periodic, we've fully serviced it now, and can reset
// some of its internal state. This is useful to distinguish between expired
// alarms and active ones.
//
alarm_callback_t callback = alarm->callback;
void* data = alarm->data;
period_ms_t deadline = alarm->deadline;
if (alarm->is_periodic) {
// The periodic alarm has been rescheduled and alarm->deadline has been
// updated, hence we need to use the previous deadline.
deadline = alarm->prev_deadline;
} else {
alarm->deadline = ;
alarm->callback = NULL;
alarm->data = NULL;
alarm->queue = NULL;
} std::lock_guard<std::recursive_mutex> cb_lock(*alarm->callback_mutex);
lock.unlock(); period_ms_t t0 = now();
callback(data);//执行callback 函数,并reset alarm
period_ms_t t1 = now(); // Update the statistics
CHECK(t1 >= t0);
period_ms_t delta = t1 - t0;
update_scheduling_stats(&alarm->stats, t0, deadline, delta);
}
上面流程的核心 就是 取出队列中的alarm,并执行其中的callback,也就是我们开定时器的时候的回调函数.
关于定时器的介绍,就到这里.
AndroidO bluedroid alarm 机制分析的更多相关文章
- Linux信号(signal) 机制分析
Linux信号(signal) 机制分析 [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核 ...
- Linux mips64r2 PCI中断路由机制分析
Linux mips64r2 PCI中断路由机制分析 本文主要分析mips64r2 PCI设备中断路由原理和irq号分配实现方法,并尝试回答如下问题: PCI设备驱动中断注册(request_irq) ...
- IOS Table中Cell的重用reuse机制分析
IOS Table中Cell的重用reuse机制分析 技术交流新QQ群:414971585 创建UITableViewController子类的实例后,IDE生成的代码中有如下段落: - (UITab ...
- 您还有心跳吗?超时机制分析(java)
注:本人是原作者,首发于并发编程网(您还有心跳吗?超时机制分析),此文结合那里的留言作了一些修改. 问题描述 在C/S模式中,有时我们会长时间保持一个连接,以避免频繁地建立连接,但同时,一般会有一个超 ...
- Java 类反射机制分析
Java 类反射机制分析 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.在计算机科学领域,反射是一类应用,它们能够自描述和自控制.这类应用通过某 ...
- Java 动态代理机制分析及扩展
Java 动态代理机制分析及扩展,第 1 部分 王 忠平, 软件工程师, IBM 何 平, 软件工程师, IBM 简介: 本文通过分析 Java 动态代理的机制和特点,解读动态代理类的源代码,并且模拟 ...
- Android内存机制分析1——了解Android堆和栈
//----------------------------------------------------------------------------------- Android内存机制分析1 ...
- memcache redundancy机制分析及思考
设计和开发可以掌控客户端的分布式服务端程序是件幸事,可以把很多事情交给客户端来做,而且可以做的很优雅.角色决定命运,在互联网架构中,web server必须冲锋在前,注定要在多浏览器版本以及协议兼容性 ...
- Linux内核态抢占机制分析(转)
Linux内核态抢占机制分析 http://blog.sina.com.cn/s/blog_502c8cc401012pxj.html 摘 要]本文首先介绍非抢占式内核(Non-Preemptive ...
随机推荐
- 初步了解redux
redux作为react的状态状态管理工具,是十分重要的一部分,但是它在学习起来比较困难.它的写法一共分为三部分,而且他不像其他的东西一样可以写一步,在页面上查看一下.它必须三个部分全部完成之后,才能 ...
- 作为IT,你的价值在哪里?
也许最近是真的被无穷无尽的数据整理.导入.再整理.再导入给恶心到了. 业务部提交的数据只是一个非常初始的数据,IT还得在这个基础上七整八整,对导出的结果还要再做二次导入三次导入,不仅要帮业务部批导生成 ...
- Linux中对逻辑卷的建立
大体上与主分区的建立相同,只有一些不同. 建议大家先看下我的“Linux中安装硬盘后对硬盘的分区以及挂载” https://www.cnblogs.com/feiquan/p/9219447.htm ...
- The log scan number (620023:3702:1) passed to log scan in database 'xxxx' is not valid
昨天一台SQL Server 2008R2的数据库在凌晨5点多抛出下面告警信息: The log scan number (620023:3702:1) passed to log scan in d ...
- mssql sqlserver避免sql脚本中出现除零错误的方法分享
摘自:http://www.maomao365.com/?p=6612 摘要:下文介绍sql server中,sql脚本避免出现除零错误的方法分享 在各种业务系统开发中,通常会遇到除零的错误,下文分享 ...
- web前端(10)—— 浮动,清除默认样式
文档流 web页面和ps等设计软件有本质的区别,web 网页的制作,是个“流”,从上而下 ,像 “织毛衣”,就跟编程语言一样,都是由上而下 而设计软件 ,想往哪里画东西,就去哪里画 文档流带来的最明显 ...
- CVE-2017-8464 分析
目录 CVE-2017-8464(stuxnet 3.0) 分析 0xFF 前言 0x00 分析工具 0x01 漏洞复现 1).生成一个DLL用于测试 2).构造一个恶意的lnk二进制文件 3).RU ...
- KFCM算法的matlab程序
KFCM算法的matlab程序 在“聚类——KFCM”这篇文章中已经介绍了KFCM算法,现在用matlab程序对iris数据库进行简单的实现,并求其准确度. 作者:凯鲁嘎吉 - 博客园 http:// ...
- 记录参加QCon2017北京站的心得
如有侵权,请告知作者删除.scottzg@126.com 很荣幸参加QCon全球软件开发大会,这里特别感谢我们部门的总经理,也是<互联网广告算法和系统实践>此书的作者王勇睿.因为他我才有这 ...
- Java 过一下基础
点我跳过黑哥的卑鄙广告行为,进入正文. Java多线程系列更新中~ 正式篇: Java多线程(一) 什么是线程 Java多线程(二)关于多线程的CPU密集型和IO密集型这件事 Java多线程(三)如何 ...