freeswitch的任务引擎实现分析


概述 freeswitch核心框架中有一个定时任务系统,在开发过程中用来做一些延时操作和异步操作很方便。
我们在VOIP的呼叫流程中,经常会有一些对实时性要求没那么高的操作,或者会有阻塞流程的操作,我们都可以开启一个定时任务子流程,来达到延时和异步的目标。
下面,我们来对这个任务引擎的代码实现做一个简单的梳理和分析。

环境 centos:CentOSrelease 7.0 (Final)或以上版本
freeswitch:v1.8.7
GCC:4.8.5

数据结构 源码文件
src\include\switch_scheduler.h
src\switch_scheduler.c

任务数据结构
struct switch_scheduler_task {
int64_t created;
int64_t runtime;
【freeswitch的任务引擎实现分析】uint32_t cmd_id;
uint32_t repeat;
char *group;
void *cmd_arg;
uint32_t task_id;
unsigned long hash;
};

struct switch_scheduler_task_container {
switch_scheduler_task_t task;
int64_t executed;
int in_thread;
int destroyed;
int running;
switch_scheduler_func_t func;
switch_memory_pool_t *pool;
uint32_t flags;
char *desc;
struct switch_scheduler_task_container *next;
};
typedef struct switch_scheduler_task_container switch_scheduler_task_container_t;

static struct {
switch_scheduler_task_container_t *task_list;
switch_mutex_t *task_mutex;
uint32_t task_id;
int task_thread_running;
switch_queue_t *event_queue;
switch_memory_pool_t *memory_pool;
} globals;

总图
freeswitch的任务引擎实现分析
文章图片




常用接口 查看src\include\switch_scheduler.h头文件,常用接口如下。
switch_scheduler_add_task//Schedule a task in the future

switch_scheduler_del_task_id//Delete a scheduled task

switch_scheduler_del_task_group//Delete a scheduled task based on the group name

switch_scheduler_task_thread_start//Start the scheduler system

switch_scheduler_task_thread_stop//Stop the scheduler system

外部接口很简单。初始化接口使用start和stop,新增任务使用add_task,删除任务使用del_task_id,另外有一个del_task_group的接口针对任务群。

引擎初始化switch_scheduler_task_thread_start 函数原型
SWITCH_DECLARE(void) switch_scheduler_task_thread_start(void);

函数逻辑:
1, 初始化内存池globals.memory_pool。
2, 初始化互斥锁globals.task_mutex。
3, 初始化消息队列globals.event_queue。
4, 创建任务执行线程,线程函数switch_scheduler_task_thread,以下是任务线程的逻辑流程。
5, 设置全局变量globals.task_thread_running = 1。
6, 任务线程循环开始,task_thread_loop(0)。
7, 加锁globals.task_mutex。
8, 遍历任务链表globals.task_list,检查任务执行时间,符合执行时间的任务检查线程标识,对于有单独线程标识SSHF_OWN_THREAD的任务启动线程task_own_thread并执行,对于没有单独线程标识的任务,在当前线程中执行。
9, 解锁globals.task_mutex。
10,加锁globals.task_mutex。
11,遍历任务链表globals.task_list,检查任务删除标识tp->destroyed,销毁任务,释放任务相关内存。
12,解锁globals.task_mutex。
13,从全局消息队列globals.event_queue中获取事件,并发布该事件。
14,任务线程循环结束。
15,task_thread_loop(1),遍历任务链表globals.task_list,设置所有任务删除标识tp->destroyed = 1,销毁任务,释放任务相关内存。
16,从全局消息队列globals.event_queue中获取事件并销毁。
17,设置全局变量globals.task_thread_running = 0。

引擎初始化后的内存模型如图
freeswitch的任务引擎实现分析
文章图片




引擎停止switch_scheduler_task_thread_stop 函数原型
SWITCH_DECLARE(void) switch_scheduler_task_thread_stop(void);

函数逻辑:
1, 设置全局变量globals.task_thread_running=-1。
2, 等待任务线程退出。
3, 销毁内存池globals.memory_pool。

新增任务switch_scheduler_add_task 函数原型
SWITCH_DECLARE(uint32_t) switch_scheduler_add_task(time_t task_runtime,
switch_scheduler_func_t func,
const char *desc, const char *group, uint32_t cmd_id, void *cmd_arg, switch_scheduler_flag_t flags);

函数逻辑:
1, 加锁globals.task_mutex。
2, 分配一块内存给任务容器container,类型为switch_scheduler_task_container_t。
3, container数据初始化。包括回调函数func和任务预定运行时间等信息。
4, 将container插入任务链表globals.task_list的队尾。
5, 解锁globals.task_mutex。
6, 新建SWITCH_EVENT_ADD_SCHEDULE事件,并将事件插入消息队列globals.event_queue。
7, 结束返回任务id。

增加多个任务之后的内存模型如图
freeswitch的任务引擎实现分析
文章图片




删除任务switch_scheduler_del_task_id 函数原型
SWITCH_DECLARE(uint32_t) switch_scheduler_del_task_id(uint32_t task_id);

函数逻辑:
1, 加锁globals.task_mutex。
2, 遍历任务链表globals.task_list,找到对应task_id的任务。
3, 任务标识SSHF_NO_DEL则不删除。
4, 任务正在运行则不删除。
5, 设置任务删除标识tp->destroyed++。
6, 解锁globals.task_mutex。

总结 任务引擎中的循环,在任务执行正常的情况下,每隔500ms检查1次任务链表,在实际应用中,可能会有一定的延迟,无法做到实时执行。
对于任务的执行过程,考虑到有阻塞操作的任务,一定要使用单独线程执行,否则会阻塞其他任务。
定时任务引擎,使用时间轮模式是否更好用?


空空如常
求真得真

    推荐阅读