11. yloop


11.1. Yloop概要

Yloop 是AliOS Things的异步事件框架。Yloop借鉴了,libuv及嵌入式业界常见的event loop,综合考虑使用复杂性,性能,及footprint,实现了一个适合于MCU的事件调度机制。我们移植了相关的插件。其主要优势是所有的处理都是在主任务中执行的,不需要额外的创建任务,从而节省内存使用。同时,由于所有处理都是在主任务进行,不需要复杂的互斥操作。

11.2. Yloop上下文

每个Yloop实例(aos_loop_t)与特定的任务上下文绑定,AliOS Things的程序入口application_start 所在的上下文与系统的主Yloop实例绑定,该上下文也称为主任务。主任务以外的任务也可以创建自己的Yloop实例。

11.3. Yloop调度

Yloop实现了对IO,timer,callback,event的统一调度管理:

  • IO:最常见的是Socket,也可以是AliOS Things的vfs管理的设备

  • timer:即常见的定时器

  • callback:特定的执行函数

  • event:包括系统事件,用户自定义事件 当调用aos_loop_run后,当前任务将会等待上述的各类事件发生。

11.4. Yloop实现原理

Yloop利用协议栈的select接口实现了对IO及timer的调度。AliOS Things自带的协议栈又暴露一个特殊的eventfd接口,Yloop利用此接口把VFS的设备文件,和eventfd关联起来,实现了对整个系统的事件的统一调度。

11.5. 主要api介绍

  • 注册事件监听函数

/**
 * Register system event filter callback.
 *
 @param[in]  type  event type interested.
 * @param[in]  cb    system event callback.
 * @param[in]  priv  private data past to cb.
 *
 * @return  the operation status, 0 is OK, others is error.
 */
int aos_register_event_filter(uint16_t type, aos_event_cb cb, void *priv);

/**
 * Unregister native event callback.
 *
 * @param[in]  type  event type interested.
 * @param[in]  cb    system event callback.
 * @param[in]  priv  private data past to cb.
 *
 * @return  the operation status, 0 is OK, others is error.
 */
int aos_unregister_event_filter(uint16_t type, aos_event_cb cb, void *priv);
  • 发布一个 event

/**
 * Post local event.
 *
 * @param[in]  type   event type.
 * @param[in]  code   event code.
 * @param[in]  value  event value.
 *
 * @return  the operation status, 0 is OK,others is error.
 */
int aos_post_event(uint16_t type, uint16_t code, unsigned long  value);
  • 注册和取消一个 poll event

/**
 * Register a poll event in main loop.
 *
 * @param[in]  fd      poll fd.
 * @param[in]  action  action to be executed.
 * @param[in]  param   private data past to action.
 *
 * @return  the operation status, 0 is OK,others is error.
 */
int aos_poll_read_fd(int fd, aos_poll_call_t action, void *param);

/**
 * Cancel a poll event to be executed in main loop.
 *
 * @param[in]  fd      poll fd.
 * @param[in]  action  action to be executed.
 * @param[in]  param   private data past to action.
 */
void aos_cancel_poll_read_fd(int fd, aos_poll_call_t action, void *param);
  • 发布和取消一个延迟执行的 action

/**static void adc_cb_read(int fd, void *param)
{
    aos_post_event(EV_ADCKEY, CODE_ADCKEY_INT_TRIGGER, fd);
}
 * Post a delayed action to be executed in main loop.
 *
 * @param[in]  ms      milliseconds to wait.
 * @param[in]  action  action to be executed.
 * @param[in]  arg     private data past to action.
 *
 * @return  the operation status, 0 is OK,others is error.
 */
int aos_post_delayed_action(int ms, aos_call_t action, void *arg);

/**
 * Cancel a delayed action to be executed in main loop.
 *
 * @param[in]  ms      milliseconds to wait, -1 means don't care.
 * @param[in]  action  action to be executed.
 * @param[in]  arg     private data past to action.
 */
void aos_cancel_delayed_action(int ms, aos_call_t action, void *arg);
  • 安排一次回调

/**
 * Schedule a callback in next event loop.
 * Unlike aos_post_delayed_action,
 * this function can be called from non-aos-main-loop context.

 * @param[in]  action  action to be executed.
 * @param[in]  arg     private data past to action.
 *
 * @return  the operation status, <0 is error,others is OK.
 */
int aos_schedule_call(aos_call_t action, void *arg);

11.6. 示例代码

这里会介绍事件注册、通知、回调、取消流程poll事件的注册取消延迟执行一个actio)以及安排一次回调的使用方法

11.6.1. 事件注册、通知、回调、取消流程

aos_register_event_filter(EV_WIFI, event_cb_wifi_event, NULL);

用户首先调用aos_register_event_filter注册事件监听函数,例如首先显注册一个EV_WIFI事件的监听函数event_cb_wifi_event

aos_post_event(EV_WIFI, CODE_WIFI_ON_INIT_DONE, 0);

当有任务调用aos_post_event接口,发布CODE_WIFI_ON_INIT_DONE事件之后

static void event_cb_wifi_event(input_event_t *event, void *private_data)
{
    switch (
        case CODE_WIFI_ON_INIT_DONE:
        {
            printf("[APP] [EVT] CODE_WIFI_ON_INIT_DONE %lld\r\n", aos_now_ms());
        }
        break;
        case CODE_WIFI_ON_PRE_GOT_IP:
        {
            printf("[APP] [EVT] connected %lld\r\n", aos_now_ms());
        }
        break;
        case CODE_WIFI_ON_GOT_IP:
        {
            printf("[APP] [EVT] GOT IP %lld\r\n", aos_now_ms());
        }
        break;
        default:
        {
            /*nothing*/
        }
    }
}

event_cb_wifi_event会被调用,并进入caseCODE_WIFI_ON_INIT_DONE分支

aos_unregister_event_filter(EV_WIFI, event_cb_wifi_event, NULL);

如果用户不需要事件的监听,用户可以主动调用aos_unregister_event_filter取消监听

11.6.2. poll事件的注册取消

/*uart*/
fd_console = aos_open("/dev/ttyS0", 0);
if (fd_console >= 0) {
    printf("Init CLI with event Driven\r\n");
    aos_cli_init(0);
    aos_poll_read_fd(fd_console, aos_cli_event_cb_read_get(), (void*)0x12345678);
    _cli_init();
}

这里以 uart0 为例,用户首先注册一个aos_poll_read_fdpoll事件

aos_cancel_poll_read_fd(fd_console, action, (void*)0x12345678);

如果用户不需要事件的poll,用户可以调用aos_cancel_poll_read_fd 取消poll

11.6.3. 延迟执行一个action

aos_post_delayed_action(1000, app_delayed_action_print, NULL);

用户可以调用aos_post_delayed_action做一个延迟1s执行的事件

static void app_delayed_action_print(void *arg)
{
    printf("test.\r\n");
}

那过1s之后会主动调用app_delayed_action_print函数

aos_cancel_delayed_action(1000, app_delayed_action_print, NULL);

当用户想直接取消一个延迟动作可以调用aos_cancel_delayed_action,其第一个ms参数, 当ms == -1时,表示无需关心时间是否一致

11.6.4. 安排一次回调

aos_schedule_call(app_action_print, NULL);

用户主动调用aos_schedule_call函数

static app_action_print(void *arg)
{
    printf("test\r\n");
}

那么会在下一次循环中主动调用app_action_print函数

11.7. 注意事项

Yloop的API(include/aos/yloop.h)除了下述API,都必须在Yloop实例所绑定的任务的上下文执行:

  • aos_schedule_call

  • aos_loop_schedule_call

  • aos_loop_schedule_work

  • aos_cancel_work

  • aos_post_event