阿里的iOS协程库 coobjc 源码解析(一)——元组和协程

我最近在阅读阿里的iOS协程库,了解如何在iOS体系下实现协程上下文切换的过程。这是该系列的首篇文章,主要是了解了coobjc中的元组的实现,和coobjc中协程的上下文切换是如何进行的。
摘要由CSDN通过智能技术生成

Coobjc中的元组

底层主要依赖NSPointerArray进行实现,因为NSPointerArray支持插入nil指针,能配合元组中有对象为nil的特性。

比较引人入胜的设计

主要是co_tuple(...)这个宏定义。

co_tuple(...) [[COTuple alloc] initWithObjects:__VA_ARGS__, co_tupleSentinel()]

__VA_ARGS__作为传参,但是支持nil进行传值。以自定义的全局单例co_tupleSentinel()作为结束标识。这样就可以支持所有类型的外部输入了。

关于协程如何中断和继续

这里涉及到子程序的上下文切换,iOS 系统下,并没有提供直接的触及方式。所以coobjc是通过自己写的汇编,实现了上下文切换。

里面实现了arm64, armv7, x86_64, i386的上下文切换汇编代码,分别对应了64位和32位的真机和模拟器。

上下文切换主要是两个方法:

extern int coroutine_getcontext (coroutine_ucontext_t *__ucp);

extern int coroutine_setcontext (coroutine_ucontext_t *__ucp);

getcontext获取当前的上下文,也就是堆栈信息,setcontext设置当前的上下文。通过这样的方式,在iOS中实现了子程序调用的上下文切换。

image.png

coroutine.m 源码解读

这个文件里是关于协程创建、销毁,协程队列,协程调度管理的代码,里面代码量不多,总共只有300多行,皆由c实现。但由于可中断的设计,里面的源码阅读难度陡然上升。但是只要清楚概念,实际也并不会太难理解。

协程队列

协程队列是一条FIFO的队列,数据结构使用的是链表。每次新增一个协程到调度队列中,都通过尾插入的方式;每次从队列中pop出一个要被执行的协程,都是从链表的头取出。对应具体方法是:

// 添加协程到调度器的队列中
void scheduler_queue_push(coroutine_scheduler_t *scheduler, coroutine_t *co);
// 从调度器的协程队列中取出一个协程
coroutine_t *scheduler_queue_pop(coroutine_scheduler_t *scheduler);

协程和调度器的定义

协程

在coobjc里,协程用结构体进行定义,就是一个子程序的调度单位。在用户态里生成和销毁,十分轻量,上下文切换所涉及的资源量也很小,对CPU来说效率更高。

代码上,协程的定义也很精致,涉及到的数据并不多,定义如下:

    struct coroutine {
   
        coroutine_func entry;                   // Process entry.
        void *userdata;                         // Userdata.
        coroutine_func userdata_dispose;        // Userdata's dispose action.
        void *context;                          // Coroutine's Call stack data.
        void *pre_context;                      // Coroutine's source process's Call stack data.
        int status;                             // Coroutine's running status.
        uint32_t stack_size;                    // Coroutine's stack size
        void *stack_memory;                     // Coroutine's stack memory address.
        void *stack_top;                    // Coroutine's stack top address.
        struct coroutine_scheduler *scheduler;  // The pointer to the scheduler.
        
        struct coroutine *prev;
        struct coroutine *next;
        
        void *autoreleasepage;                  // If enable autorelease, the custom autoreleasepage.
        void *chan_alt;                         // If blocking by a channel, record the alt
        bool is_cancelled;                      // The coroutine is cancelled
        int8_t   is_scheduler;                  // The coroutine is a scheduler.
    };

结构体上的数据,可以归纳成几点:

  1. 带有协程的调用方法入口,带有始终标记着栈顶的指针;
  2. 带有协程当前对应的上下文 context,以及存储着由于开始协程的运行,而中断的前上下文 pre_context,每次在中断或完成自己的上下文时,调度器会回到写成的pre_context;
  3. 可以绑定一份用户数据;
  4. 协程的基本需求信息,开辟的栈内存,栈空间大小;
  5. 关联当前协程所处队列中的前一个协程与下一个协程;
  6. CSP阻塞时,channel的数据chan_alt
  7. 一些状态位。

由此可以一窥,协程在运行时,具体所需要的数据。

调度器

根据coobjc官方架构介绍文档的表述,协程调度器本身也是一个协程,这个其实从代码上,也能看得出来。
调度器的具体代码,可以放在下一节里说,先来看看调度器具体定义:

     /**
   
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值