GCD底層源碼分析

底層源碼分析。

首先從創建隊列講起, dispatch_queue_create函數

 dispatch_queue_create 內部調用了 
dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
{
// attr我們一般傳??????????????DISPATCH_QUEUE_SERIAL,??DISPATCH_QUEUE_CONCURRENT??, ??NULL,
// ??DISPATCH_QUEUE_SERIAL????????就是  NULL
    return _dispatch_lane_create_with_target(label, attr,
            DISPATCH_TARGET_QUEUE_DEFAULT, true);
}

_dispatch_lane_create_with_target 函數 當我們在打開libdispatch找到這個函數的時候發現,很難讀。但是我們需要找到讀它的方法 重點就是看他的返回值返回了什么。從返回值上獲取關鍵線索去排查(這里就不粘貼這個方法全部的代碼了有興趣的去自己下載觀看)

    return _dispatch_trace_queue_create(dq)._dq;
  • 從這個返回值上面我們發現了返回的是 將當前函數獲取的dq 傳進_dispatch_trace_queue_create中進行包裝。又將其屬性 _dq返回。所以dq就是 我們要檢查的重點。

進入到 dispatch_trace_queue_create 函數,查看 _dq屬性 和穿進去的dq做了什么

static inline dispatch_queue_class_t
_dispatch_trace_queue_create(dispatch_queue_class_t dqu)
{
    ///對傳進來的dq進行包裝處理
    
/// 僅在啟用跟蹤時進行分派
    _dispatch_only_if_ktrace_enabled({
        uint64_t dq_label[4] = {0}; // So that we get the right null termination
 
        dispatch_queue_t dq = dqu._dq;
        strncpy((char *)dq_label, (char *)dq->dq_label ?: "", sizeof(dq_label));

        _dispatch_ktrace2(DISPATCH_QOS_TRACE_queue_creation_start,
                dq->dq_serialnum,
                _dispatch_priority_to_pp_prefer_fallback(dq->dq_priority));

        _dispatch_ktrace4(DISPATCH_QOS_TRACE_queue_creation_end,
                        dq_label[0], dq_label[1], dq_label[2], dq_label[3]);
    });
    ///將傳進來 dq 傳進 函數 并返回
    return _dispatch_introspection_queue_create(dqu);
}
  • 從上面的函數中 看 又將 最外層 dq 傳進了一個 _dispatch_introspection_queue_create 函數進行了包裝處理

進入到 _dispatch_introspection_queue_create

_dispatch_introspection_queue_create(dispatch_queue_t dq)
{
    dispatch_queue_introspection_context_t dqic;
    size_t sz = sizeof(struct dispatch_queue_introspection_context_s);

    if (!_dispatch_introspection.debug_queue_inversions) {
        sz = offsetof(struct dispatch_queue_introspection_context_s,
                __dqic_no_queue_inversion);
    }
    ///初始化了個 dqic
       dqic = _dispatch_calloc(1, sz);
       ///將 dq賦值給  _dq
    dqic->dqic_queue._dq = dq;
    if (_dispatch_introspection.debug_queue_inversions) {
        LIST_INIT(&dqic->dqic_order_top_head);
        LIST_INIT(&dqic->dqic_order_bottom_head);
    }
    dq->do_finalizer = dqic;

    _dispatch_unfair_lock_lock(&_dispatch_introspection.queues_lock);
    LIST_INSERT_HEAD(&_dispatch_introspection.queues, dqic, dqic_list);
    _dispatch_unfair_lock_unlock(&_dispatch_introspection.queues_lock);

    DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_create, dq);
    if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_create)) {
        _dispatch_introspection_queue_create_hook(dq);
    }
    return upcast(dq)._dqu;
}

  • 這里我們看到了 dq就等于 _dq
  • 再往下看已經沒有意義 而dq就是我們要研究的。

繼續查看 upcast()

static inline dispatch_object_t
upcast(dispatch_object_t dou)
{
    return dou;
}

經過這幾層函數的追蹤判斷 我們并沒有看到我們想要的并指導了 dq就是我們要研究的重點

那么回到 最上面的_dispatch_lane_create_with_target的函數 中尋找 dq做了什么。

DISPATCH_NOINLINE
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
        dispatch_queue_t tq, bool legacy)
{
....省略
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s)); // alloc
    
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0)); // init
    dq->dq_label = label;
    dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,
            dqai.dqai_relpri);
    if (overcommit == _dispatch_queue_attr_overcommit_enabled) {
        dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
    }
    if (!dqai.dqai_inactive) {
        _dispatch_queue_priority_inherit_from_target(dq, tq);
        _dispatch_lane_inherit_wlh_from_target(dq, tq);
    }
    _dispatch_retain(tq);
    dq->do_targetq = tq;
    _dispatch_object_debug(dq, "%s", __func__);
    return _dispatch_trace_queue_create(dq)._dq;
}
  • 回到 _dispatch_lane_create_with_target 函數 中我們看到了 始化 _dispatch_object_alloc, _dispatch_queue_init 。這不就是 初始化 和 init嗎? 不由得想起了我們平常開發的 [NSObjc alloc] init]; 在這里 _dispatch_object_alloc進行了初始化空間的工作。init進行了區分是 當前是否是 串行 還是 并行 ,串行 給了 1 并行給了 DISPATCH_QUEUE_WIDTH_MAX 的操作(并行 給了 最大的寬度)

在 _dispatch_queue_init 里面我們看到了根據 dqai.dqai_concurrent 來區分 并行 和串行。

下面我們在當前函數看一下 dqai

static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
        dispatch_queue_t tq, bool legacy)
{
    // dqai 創建    ///根據傳進來 dqa serio concurrent NULL 獲取 dqai
    dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
....省略
}

發現第一行就根據 傳進來的 要創建什么樣的隊列 進行了 dqai的創建 我看一下_dispatch_queue_attr_to_info 函數


dispatch_queue_attr_info_t
_dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa)
{
    dispatch_queue_attr_info_t dqai = { };

    if (!dqa) return dqai;

#if DISPATCH_VARIANT_STATIC
    if (dqa == &_dispatch_queue_attr_concurrent) { // null 默認
        dqai.dqai_concurrent = true;
        return dqai;
    }
#endif

    if (dqa < _dispatch_queue_attrs ||
            dqa >= &_dispatch_queue_attrs[DISPATCH_QUEUE_ATTR_COUNT]) {
        DISPATCH_CLIENT_CRASH(dqa->do_vtable, "Invalid queue attribute");
    }

    size_t idx = (size_t)(dqa - _dispatch_queue_attrs);

    dqai.dqai_inactive = (idx % DISPATCH_QUEUE_ATTR_INACTIVE_COUNT);
    idx /= DISPATCH_QUEUE_ATTR_INACTIVE_COUNT;

    dqai.dqai_concurrent = !(idx % DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT);
    idx /= DISPATCH_QUEUE_ATTR_CONCURRENCY_COUNT;

    dqai.dqai_relpri = -(int)(idx % DISPATCH_QUEUE_ATTR_PRIO_COUNT);
    idx /= DISPATCH_QUEUE_ATTR_PRIO_COUNT;

    dqai.dqai_qos = idx % DISPATCH_QUEUE_ATTR_QOS_COUNT;
    idx /= DISPATCH_QUEUE_ATTR_QOS_COUNT;

    dqai.dqai_autorelease_frequency =
            idx % DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;
    idx /= DISPATCH_QUEUE_ATTR_AUTORELEASE_FREQUENCY_COUNT;

    dqai.dqai_overcommit = idx % DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;
    idx /= DISPATCH_QUEUE_ATTR_OVERCOMMIT_COUNT;

    return dqai;
}
  • 通過此函數的處理我們看到了 無論你傳NULL 還是 DISPATCH_QUEUE_SERIAL 都是 為默認就是串行隊列。

我們繼續返回到_dispatch_lane_create_with_target 繼續 查找 dqai

    //
    // Step 2: Initialize the queue
    //

    if (legacy) {
        // if any of these attributes is specified, use non legacy classes
        if (dqai.dqai_inactive || dqai.dqai_autorelease_frequency) {
            legacy = false;
        }
    }

    const void *vtable;
    dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;

/*----------------- 這區間里面是在做什么? ------------------------*/  
    if (dqai.dqai_concurrent) {
        // OS_dispatch_queue_concurrent
        vtable = DISPATCH_VTABLE(queue_concurrent);
    } else {
        vtable = DISPATCH_VTABLE(queue_serial);
    }
/*---------------------------------------------------------------*/

    switch (dqai.dqai_autorelease_frequency) {
    case DISPATCH_AUTORELEASE_FREQUENCY_NEVER:
        dqf |= DQF_AUTORELEASE_NEVER;
        break;
    case DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM:
        dqf |= DQF_AUTORELEASE_ALWAYS;
        break;
    }
    if (label) {
        const char *tmp = _dispatch_strdup_if_mutable(label);
        if (tmp != label) {
            dqf |= DQF_LABEL_NEEDS_FREE;
            label = tmp;
        }
    }
//    開辟內存空間
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s)); // alloc
/// 區分 串行 還是 并發
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0)); // init
  • 看上面代碼 標記出來的注釋 這是在干什么? 如果是創建并發隊列 就vtable = DISPATCH_VTABLE(queue_concurrent)。串行隊列 vtable = DISPATCH_VTABLE(queue_serial);

繼續搜索 DISPATCH_VTABLE

#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)

繼續搜 DISPATCH_OBJC_CLASS

#define DISPATCH_OBJC_CLASS(name)   (&DISPATCH_CLASS_SYMBOL(name))

繼續搜 DISPATCH_CLASS_SYMBOL

#define DISPATCH_CLASS_SYMBOL(name) OS_dispatch_##name##_class

#define DISPATCH_CLASS(name) OS_dispatch_##name
  • 我的天 這騷操作 進行了 參數拼接 也就是如過是 并行就是 這么個東西 OS_dispatch_queue_concurrent; 如果是 串行就是這樣
    OS_dispatch_queue_serial ; 看到這里不由得發出一個疑問 隊列也是一個對象?
  • ## 在編譯的時候 會去掉。name就是 傳進來的參數進行拼接。
    迫不及待的去打印一下生成的隊列對象。


    截屏2020-11-07 下午1.43.35.png
  • 從這看出隊列 也是一個對象,那它一定有 對isa的操作,在哪里?我們先往下面看

繼續回到這

/*----------------- 這區間里面是在做什么? ------------------------*/  
    if (dqai.dqai_concurrent) {
        // OS_dispatch_queue_concurrent
        vtable = DISPATCH_VTABLE(queue_concurrent);
    } else {
        vtable = DISPATCH_VTABLE(queue_serial);
    }
/*---------------------------------------------------------------*/

  • 這里我們也就知道了 這里是在設置類 的類型

既然創建了一個對象 在 創建完之前 它isa ;類的東西 已經確認完畢,那他在哪里?跟任何一個類 都是一摸摸一樣樣的。一個對象alloc的時候它前面必然會做,它的父類啊,它的isa的都會指向完畢。

回到 _dispatch_object_alloc的地方

    ///設置 類的 信息
    if (dqai.dqai_concurrent) {
        // OS_dispatch_queue_concurrent
        vtable = DISPATCH_VTABLE(queue_concurrent);
    } else {
        vtable = DISPATCH_VTABLE(queue_serial);
    }
    switch (dqai.dqai_autorelease_frequency) {......}
    if (label) {......}
//    開辟內存空間
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s)); // alloc
    
/// 區分 串行 還是 并發
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0)); // init
    /// 參數賦值

  • 我們看到 將類的信息傳入了 _dispatch_object_alloc 里面。

搜索 _dispatch_object_alloc

#pragma mark dispatch_object_t

void *
_dispatch_object_alloc(const void *vtable, size_t size)
{
#if OS_OBJECT_HAVE_OBJC1
    const struct dispatch_object_vtable_s *_vtable = vtable;
    dispatch_object_t dou;
    dou._os_obj = _os_object_alloc_realized(_vtable->_os_obj_objc_isa, size);
    dou._do->do_vtable = vtable;
    return dou._do;
#else
    return _os_object_alloc_realized(vtable, size);
#endif
}

搜索 _os_object_alloc_realized

_os_object_alloc_realized(const void *cls, size_t size)
{
    _os_object_t obj;
    dispatch_assert(size >= sizeof(struct _os_object_s));
    while (unlikely(!(obj = calloc(1u, size)))) {
        _dispatch_temporary_resource_shortage();
    }
    obj->os_obj_isa = cls;
    return obj;
}
  • 由此可以看出 隊列 也是個對象。
隊列底層探索總結
  • 我們創建一個隊列,傳入的屬性(DISPATCH_QUEUE_SERIAL/NULL/DISPATCH_QUEUE_CONCURRENT)決定了下層的_dispatch_queue_init 的 DISPATCH_QUEUE_WIDTH_MAX 和 1 的區別。
  • 我們創建出來的 queue 也是一個對象 ,底層也需要 alloc init進行創建,alloc過程中也會指定 class 的類型.class的設置 是由底層宏定義拼接而成。
  • 并發隊列最大并發數是多少?DISPATCH_QUEUE_WIDTH_MAX 真么大。
#define DISPATCH_QUEUE_WIDTH_FULL           0x1000ull
#define DISPATCH_QUEUE_WIDTH_MAX  (DISPATCH_QUEUE_WIDTH_FULL - 2)
/// 0x1000ull - 2 =  4094

以上就是對隊列的底層探究 下面來到 函數底層探究篇章

dispatch_async 異步函數底層探究

void
dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
{
    dispatch_continuation_t dc = _dispatch_continuation_alloc();
    uintptr_t dc_flags = DC_FLAG_CONSUME;
    dispatch_qos_t qos;

    // 任務包裝器 - 接受 - 保存 - 函數式
    // 保存 block 
    qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
    _dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}
  • 這里我們看到 初始化了一個任務包裝器 init的時候 將 dc(初始化的任務包裝器);dq(傳進來的隊列);work(傳進來的block任務) 等傳進了 _dispatch_continuation_init 方法

進入 _dispatch_continuation_init 我們看做了什么操作

static inline dispatch_qos_t
_dispatch_continuation_init(dispatch_continuation_t dc,
        dispatch_queue_class_t dqu, dispatch_block_t work,
        dispatch_block_flags_t flags, uintptr_t dc_flags)
{
    ///將work進行copy獲取 得到 *ctxt指針
    void *ctxt = _dispatch_Block_copy(work);

    dc_flags |= DC_FLAG_BLOCK | DC_FLAG_ALLOCATED;
    ///大多數不會走這里
    if (unlikely(_dispatch_block_has_private_data(work))) {
        dc->dc_flags = dc_flags;
        ///將 work副本存入到了 dc包裝器里
        dc->dc_ctxt = ctxt;
        // will initialize all fields but requires dc_flags & dc_ctxt to be set
        return _dispatch_continuation_init_slow(dc, dqu, flags);
    }
   ///  根據任務 獲取 調用 func
    dispatch_function_t func = _dispatch_Block_invoke(work);
    
    if (dc_flags & DC_FLAG_CONSUME) {
        ///指定執行函數 為 _dispatch_call_block_and_release;
        func = _dispatch_call_block_and_release;
    }
    return _dispatch_continuation_init_f(dc, dqu, ctxt, func, flags, dc_flags);
}
  • 這里看到 將傳進的任務 work 進行了copy操作 并放進了 dc任務包裝器里 并又 將 dc ; dq 隊列 傳進了 _dispatch_continuation_init_slow 大多數不會走這條支線 但是 其原理 基本一致。

  • 根據任務 work 獲取 func ,在某些條件下 將指定執行函數 為 _dispatch_call_block_and_release 并將 dc,dqu, ctxt ,func,等傳進了 _dispatch_continuation_init_f

我們搜索 _dispatch_continuation_init_f

static inline dispatch_qos_t
_dispatch_continuation_init_f(dispatch_continuation_t dc,
        dispatch_queue_class_t dqu, void *ctxt, dispatch_function_t f,
        dispatch_block_flags_t flags, uintptr_t dc_flags)
{
    pthread_priority_t pp = 0;
    dc->dc_flags = dc_flags | DC_FLAG_ALLOCATED;
    dc->dc_func = f; /// 將func方法存進 dc包裝器
    dc->dc_ctxt = ctxt;/// 將 ctxt  block 的上下文指針。
    // in this context DISPATCH_BLOCK_HAS_PRIORITY means that the priority
    // should not be propagated, only taken from the handler if it has one
    ///優先級的處理
    if (!(flags & DISPATCH_BLOCK_HAS_PRIORITY)) {
        pp = _dispatch_priority_propagate();
    }
    /// 存入處理過的 dc
    _dispatch_continuation_voucher_set(dc, flags);
    return _dispatch_continuation_priority_set(dc, dqu, pp, flags);
}
  • 我們看到這里就基本上明白了 block 任務塊先被包裝了起來,在合適的時機 將響應任務。看到這里 我們去看一下 異步函數的調用堆棧。在繼續分析 看他到底是由誰調用起來的。


    截屏2020-11-08 下午6.59.27.png
  • 這里好像明白了什么 和我們源碼分析的 一抹抹一樣樣。dispatch_call_block_and_release 函數發起的調用。
  • 流程 start_wqthread - > _pthread_wqthread -> _dispatch_worker_thread2 -> _dispatch_root_queue_drain -> _dispatch_async_redirect_invoke -> _ dispatch_continuation_pop -> _dispatch_client_callout -> _dispatch_call_block_and_release

通過上面的操作 我們了解 任務包裝器大概是如何 將 外部傳進的block進行保存的。 通過堆棧也知道了 正是 任務包裝器保存的 _dispatch_call_block_and_release函數 發起的調用 。雖說我們看到了堆棧調用流程,但是具體到每個函數干什么 我還需要據需研究。

下面繼續回到dispatch_async;函數 繼續研究 下面函數

dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
{
    dispatch_continuation_t dc = _dispatch_continuation_alloc();
    uintptr_t dc_flags = DC_FLAG_CONSUME;
    dispatch_qos_t qos;

    // 任務包裝器 - 接受 - 保存 - 函數式
    // 保存 block 
    qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
    
    /// 研究它
    _dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}
  • dq :隊列對象。dc: 包裝了block任務的包裝器。qos:策略保證正確的資源分配。dc_flags :標簽

搜索 _dispatch_continuation_async

_dispatch_continuation_async(dispatch_queue_class_t dqu,
        dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)
{
#if DISPATCH_INTROSPECTION
    if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {
        _dispatch_trace_item_push(dqu, dc);
    }
#else
    (void)dc_flags;
#endif
    return dx_push(dqu._dq, dc, qos);
}
  • 看到 dx_push 函數 并將 隊列信息,任務包裝器,策略 傳進并返回。

那 dx_push函數究竟做了什么?搜索它

#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
#define dx_vtable(x) (&(x)->do_vtable->_os_obj_vtable)
  • 發現 dx_push 是一個宏定義 ,其實就是 將 隊列信息 傳進 dx_vtable(x)得到對象,并調用 此對象的 dq_push函數。
  • 那它這么寫的意義在什么?我們只看這個宏的拆解 可能就 猜到。x為隊列信息。y為 包裝器,z為策略。 通過 x 信息可以獲取不同對象。再去調用不同對象的同名方法 dq_push 。
  • 最終執行為 dq_push函數。

搜索 dq_push

截屏2020-11-08 下午9.58.29.png
  • 這里看到發現 蘋果底層封裝的好屌啊,dx_push,會根據當前隊列信息,來尋找,已經寫好的vtables[ ]集群。每個 vtable對應著 一種隊列。而每個vtable擁有一模一樣的屬性。這里dq_push就是對應著 并發隊列vtable的一個屬性。而這個屬性 是 一個函數名字。dx_push 的最終形態 就是 _dispatch_lane_concurrent_push函數發起的調用。為了證明我們的猜想在當前環境 打符號斷點來驗證。

將_dispatch_lane_concurrent_push函數打個符號斷點 并運行

截屏2020-11-08 下午10.05.39.png

  • 可以的果真來到了這里 ,證明我們的猜測是對的。

搜索_dispatch_lane_concurrent_push 看他的實現

DISPATCH_NOINLINE
void
_dispatch_lane_concurrent_push(dispatch_lane_t dq, dispatch_object_t dou,
        dispatch_qos_t qos)
{
    // <rdar://problem/24738102&24743140> reserving non barrier width
    // doesn't fail if only the ENQUEUED bit is set (unlike its barrier
    // width equivalent), so we have to check that this thread hasn't
    // enqueued anything ahead of this call or we can break ordering
/*如果只設置了排隊位,保留非柵欄寬度不會失敗(不像它的柵欄)
寬度相等),所以我們必須檢查這個線程沒有
在這個調用之前排隊,或者我們可以打破順序*/
    if (dq->dq_items_tail == NULL &&
            !_dispatch_object_is_waiter(dou) &&
            !_dispatch_object_is_barrier(dou) &&
            _dispatch_queue_try_acquire_async(dq)) {
        return _dispatch_continuation_redirect_push(dq, dou, qos);
    }

    _dispatch_lane_push(dq, dou, qos);
}
  • 這里判斷當前隊列尾部為空,&& 不是wait ;&& 不是 barrier && 是 async 那么返回 _dispatch_continuation_redirect_push;
  • 而其他環境 進入_dispatch_lane_push
  • 這里環境猜測會進入這里 。

我們延續上面符號斷點再次將_dispatch_continuation_redirect_push 打入一個符號斷點并運行

截屏2020-11-08 下午10.19.54.png
  • 和猜想一致進入了 _dispatch_continuation_redirect_push

搜索_dispatch_continuation_redirect_push 看它的實現

_dispatch_continuation_redirect_push(dispatch_lane_t dl,
        dispatch_object_t dou, dispatch_qos_t qos)
{
    ///_dispatch對象是不是重定向
      if (likely(!_dispatch_object_is_redirection(dou))) {
        dou._dc = _dispatch_async_redirect_wrap(dl, dou);
    } else if (!dou._dc->dc_ctxt) {
        // find first queue in descending target queue order that has
        // an autorelease frequency set, and use that as the frequency for
        // this continuation./*找到降序目標隊列中設置了自動釋放頻率的第一個隊列,并使用該頻率作為此后續操作的頻率*/
        dou._dc->dc_ctxt = (void *)
        (uintptr_t)_dispatch_queue_autorelease_frequency(dl);
    }

    dispatch_queue_t dq = dl->do_targetq;
    if (!qos) qos = _dispatch_priority_qos(dq->dq_priority);
    dx_push(dq, dou, qos);//咦?怎么又調用了dx_push在進行遞歸
}

首先分析 `if (likely(!_dispatch_object_is_redirection(dou)))
1. 拓展一下 likely

 #define likely(x) __builtin_expect(!!(x), 1) 
 #define unlikely(x) __builtin_expect(!!(x), 0)
  • 它其實是個宏 誰對 _builtin_expect封裝,likely表示更大可能成立,unlikely表示更大可能不成立
  1. 我們看 _dispatch_object_is_redirection 實現代碼
 _dispatch_object_is_redirection(dispatch_object_t dou)
 {
  return _dispatch_object_has_type(dou,
          DISPATCH_CONTINUATION_TYPE(ASYNC_REDIRECT));
 }
  
 _dispatch_object_has_type(dispatch_object_t dou, unsigned long type)
 {
  return _dispatch_object_has_vtable(dou) && dx_type(dou._do) == type;
 }
  • _dispatch_object_has_vtable(dou) 判斷當前隊列是否有vtable 條件滿足
  • dx_type(dou._do) == DISPATCH_CONTINUATION_TYPE(ASYNC_REDIRECT)
    這里 看dx_type 是啥
    #define dx_type(x) dx_vtable(x)->do_type
    
    在上面我們也看了 vtables集群 這次一樣 找到 并發隊列的 do_type看是什么 ?這里找到為DISPATCH_QUEUE_CONCURRENT_TYPE 再次解析
    DISPATCH_QUEUE_CONCURRENT_TYPE     = DISPATCH_OBJECT_SUBTYPE(2, LANE),
    
    在查看DISPATCH_CONTINUATION_TYPE(ASYNC_REDIRECT)
    #define DISPATCH_CONTINUATION_TYPE(name) \
       DISPATCH_OBJECT_SUBTYPE(DC_ASYNC_REDIRECT_TYPE, CONTINUATION)
    
    再次 解析 DC_ASYNC_REDIRECT_TYPE
    enum {
     _DC_USER_TYPE = 0,
      DC_ASYNC_REDIRECT_TYPE, //在這里  我等于1
      DC_MACH_SEND_BARRRIER_DRAIN_TYPE,
      DC_MACH_SEND_BARRIER_TYPE,
      DC_MACH_RECV_BARRIER_TYPE,
     ...
          };
    
    解析了這么多 這里 其實就是為了證明當前會走進 這個判斷里

進入到 if判斷 里面 搜索 _dispatch_async_redirect_wrap 看其實現

  _dispatch_async_redirect_wrap(dispatch_lane_t dq, dispatch_object_t dou)
      {
      dispatch_continuation_t dc = _dispatch_continuation_alloc();//創建新的 任務包裝器

       dou._do->do_next = NULL;
       dc->do_vtable = DC_VTABLE(ASYNC_REDIRECT); // 
       dc->dc_func = NULL;
       dc->dc_ctxt = (void *)(uintptr_t)_dispatch_queue_autorelease_frequency(dq);
       dc->dc_data = dq;
       dc->dc_other = dou._do;
       dc->dc_voucher = DISPATCH_NO_VOUCHER;
       dc->dc_priority = DISPATCH_NO_PRIORITY;
       _dispatch_retain_2(dq); // released in _dispatch_async_redirect_invoke
      return dc;
     }
  • 這里 又初始化了一個任務包裝器,并將 任務包裝器 的do_vtable函數指定為 DC_VTABLE(ASYNC_REDIRECT);
    搜索 DC_VTABLE 看是個啥
    #define DC_VTABLE(name)  (&_dispatch_continuation_vtables[DC_ASYNC_REDIRECT_TYPE])
    
    搜索 _dispatch_continuation_vtables[
      const struct dispatch_continuation_vtable_s _dispatch_continuation_vtables[] = {
      DC_VTABLE_ENTRY(ASYNC_REDIRECT,
          .do_invoke = _dispatch_async_redirect_invoke),
     #if HAVE_MACH
      DC_VTABLE_ENTRY(MACH_SEND_BARRRIER_DRAIN,
          .do_invoke = _dispatch_mach_send_barrier_drain_invoke),
      DC_VTABLE_ENTRY(MACH_SEND_BARRIER,
          .do_invoke = _dispatch_mach_barrier_invoke),
      DC_VTABLE_ENTRY(MACH_RECV_BARRIER,
          .do_invoke = _dispatch_mach_barrier_invoke),
      DC_VTABLE_ENTRY(MACH_ASYNC_REPLY,
          .do_invoke = _dispatch_mach_msg_async_reply_invoke),
     #endif
    #if HAVE_PTHREAD_WORKQUEUE_QOS
      DC_VTABLE_ENTRY(WORKLOOP_STEALING,
          .do_invoke = _dispatch_workloop_stealer_invoke),
      DC_VTABLE_ENTRY(OVERRIDE_STEALING,
          .do_invoke = _dispatch_queue_override_invoke),
      DC_VTABLE_ENTRY(OVERRIDE_OWNING,
          .do_invoke = _dispatch_queue_override_invoke),
     #endif
     #if HAVE_MACH
      DC_VTABLE_ENTRY(MACH_IPC_HANDOFF,
          .do_invoke = _dispatch_mach_ipc_handoff_invoke),
     #endif
     };
    
    • 看到這里就是 就明白了 dc->do_vtable = DC_VTABLE(ASYNC_REDIRECT) 就是指定 重定向的.do_invoke函數為 _dispatch_async_redirect_invoke,后面的任務執行就是通過這個函數。 而這個函數 正在做什么 就是在準備及重定向函數 進行包裝。

回到_dispatch_async_redirect_wrap

_dispatch_continuation_redirect_push(dispatch_lane_t dl,
        dispatch_object_t dou, dispatch_qos_t qos)
{
    if (likely(!_dispatch_object_is_redirection(dou))) {
     ///將  創建新的任務包裝器 并指定給當前  dispatch_object_t 的任務包裝,
    ///  重定向的.do_invoke函數為 _dispatch_async_redirect_invoke  
          dou._dc = _dispatch_async_redirect_wrap(dl, dou);
    } else if (!dou._dc->dc_ctxt) {
        // find first queue in descending target queue order that has
        // an autorelease frequency set, and use that as the frequency for
        // this continuation.
        dou._dc->dc_ctxt = (void *)
        (uintptr_t)_dispatch_queue_autorelease_frequency(dl);
    }
    ///看這里  -----------------------------------------------*/
    dispatch_queue_t dq = dl->do_targetq;
    if (!qos) qos = _dispatch_priority_qos(dq->dq_priority);
    dx_push(dq, dou, qos);
    /* -----------------------------------------------*/
}
  • 我們繼續看代碼中 標注的位置 大致看了一眼 發現 又調用了 dx_push 函數。這個函數我們上面已經分析。它會根據當前隊列,去找 vtables里面 集群,并找到對應 vtable ->do_push 函數。

  • 分析 第一個參數: 此時 dq = dl->do_targetq 這里這是在干嘛? dl是當前的隊列 對象。 do_targetq:這個屬性 這不在隊列初始化底層分析的時候就見到過。我們回去再看一眼。它就是 根隊列(root_queue)。

    回到隊列初始化函數瞄一眼 do_targetq屬性。 搜索 _dispatch_lane_create_with_target
    由于此函數太長 我們只保留了 關于 do_targetq 屬性的方法(有興趣的自己去搜索源碼)

    _dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
         dispatch_queue_t tq, bool legacy)
     ///外邊 tq傳的 NULL  : #define DISPATCH_TARGET_QUEUE_DEFAULT NULL
     ///所以進這里
      if (!tq) {
     ///tq 正是主隊列啊    
         tq = _dispatch_get_root_queue(
                 qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos,
                 overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq;
         if (unlikely(!tq)) {
             DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
         }
     }
    
       /// 1. alloc  出 dq對象
       ///  2. init
      /// 參數賦值
     dq->do_targetq = tq;  
     _dispatch_object_debug(dq, "%s", __func__);
     return _dispatch_trace_queue_create(dq)._dq;
    }
    
  • 這次我們看明白了這次 dx_push 正是在對 root_queue進行操作。

  • dispatch_queue_t dq = dl->do_targetq; 這句代碼 意思正是在 獲取 root_queue對象。

再次進入 到 dx_push

#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
  • 上面分析過 這時候的dx_vtable(x) x,傳入的主隊列 所以 查看 關于主隊列的vtable 看他的 dq_push指定的函數是誰。

搜索 dq_push 找到關于 主隊列的vtable

截屏2020-11-09 上午12.49.31.png

  • 看到這里也就明白這次的dx_push 底層 調用的 _dispatch_root_queue_push函數。
  • 不得不說蘋果的這操作是真 騷啊。

老規矩 打符號斷點驗證

截屏2020-11-09 上午12.53.53.png
  • 果真和我們猜測的一摸摸 一樣樣。

來到 _dispatch_root_queue_push

_dispatch_root_queue_push(dispatch_queue_global_t rq, dispatch_object_t dou,
        dispatch_qos_t qos)
{
///調度使用中的隊列
#if DISPATCH_USE_KEVENT_WORKQUEUE
    dispatch_deferred_items_t ddi = _dispatch_deferred_items_get();
    if (unlikely(ddi && ddi->ddi_can_stash)) {
        dispatch_object_t old_dou = ddi->ddi_stashed_dou;
        dispatch_priority_t rq_overcommit;
        rq_overcommit = rq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
        if (likely(!old_dou._do || rq_overcommit)) {
            dispatch_queue_global_t old_rq = ddi->ddi_stashed_rq;
            dispatch_qos_t old_qos = ddi->ddi_stashed_qos;
            ddi->ddi_stashed_rq = rq;
            ddi->ddi_stashed_dou = dou;
            ddi->ddi_stashed_qos = qos;
            _dispatch_debug("deferring item %p, rq %p, qos %d",
                    dou._do, rq, qos);
            if (rq_overcommit) {
                ddi->ddi_can_stash = false;
            }
            if (likely(!old_dou._do)) {
                return;
            }
            // push the previously stashed item
            qos = old_qos;
            rq = old_rq;
            dou = old_dou;
        }
    }
#endif

#if HAVE_PTHREAD_WORKQUEUE_QOS
// 一般情況下,無論自定義還是非自定義都會走進這個條件(比如:dispatch_get_global_queue)
// 里面主要是對比的是 qos與root隊列的qos是否 一直。基本上都不一致,如果不一致走進這個 if語句
    if (_dispatch_root_queue_push_needs_override(rq, qos)) {
        return _dispatch_root_queue_push_override(rq, dou, qos);
    }
#else
    (void)qos;
#endif
    _dispatch_root_queue_push_inline(rq, dou, dou, 1);
}

進入 _dispatch_root_queue_push_override

DISPATCH_NOINLINE
static void
_dispatch_root_queue_push_override(dispatch_queue_global_t orig_rq,
        dispatch_object_t dou, dispatch_qos_t qos)
{
    bool overcommit = orig_rq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
    dispatch_queue_global_t rq = _dispatch_get_root_queue(qos, overcommit);
    dispatch_continuation_t dc = dou._dc;
// 這個_dispatch_object_is_redirection函數其實就是return _dispatch_object_has_type(dou,DISPATCH_CONTINUATION_TYPE(ASYNC_REDIRECT));
// 所有自定義隊列會走if語句,如果是dispatch_get_global_queue不會走if語句    
if (_dispatch_object_is_redirection(dc)) {
        // no double-wrap is needed, _dispatch_async_redirect_invoke will do
        // the right thing
        dc->dc_func = (void *)orig_rq;
    } else {
        // dispatch_get_global_queue來到這里
               dc = _dispatch_continuation_alloc();
              // 相當于 下面,也就指定了執行函數為 _dispatch_queue_override_innvoke,所以有別與自定義隊列的invoke函數。
//DC_VTABLE_ENTRY(OVERRIDE_OWNING,.do_invoke = _dispatch_queue_override_invoke),

        dc->do_vtable = DC_VTABLE(OVERRIDE_OWNING);
        dc->dc_ctxt = dc;
        dc->dc_other = orig_rq;
        dc->dc_data = dou._do;
        dc->dc_priority = DISPATCH_NO_PRIORITY;
        dc->dc_voucher = DISPATCH_NO_VOUCHER;
    }
    _dispatch_root_queue_push_inline(rq, dc, dc, 1);
}
  • 這個函數區分了 自定義隊列 和 dispatch_get_global_queue 的 do_invoke 屬性 也就是 不同類型隊列 指定 不同函數。
  • 回到前面函數如果不走if 里面 和 當前函數都會去調用 _dispatch_root_queue_push_inline;

來到_dispatch_root_queue_push_inline 進行分析

_dispatch_root_queue_push_inline(dispatch_queue_global_t dq,
        dispatch_object_t _head, dispatch_object_t _tail, int n)
{
    struct dispatch_object_s *hd = _head._do, *tl = _tail._do;
// 把任務裝進隊列,大多數不走近 if語句。 
  if (unlikely(os_mpsc_push_list(os_mpsc(dq, dq_items), hd, tl, do_next))) {
        return _dispatch_root_queue_poke(dq, n, 0);
    }
}
  • 至此,我們可以看到,我們裝入到自定義的任務,都被扔到掛靠的root隊列中去了,所以我們自己創建的隊列只是一個代理身份,真正的管理人是其對應的root隊列,但同時這個隊列也是被管理的。

搜索 _dispatch_root_queue_poke

_dispatch_root_queue_poke(dispatch_queue_global_t dq, int n, int floor)
{
    if (!_dispatch_queue_class_probe(dq)) {
        return;
    }
#if !DISPATCH_USE_INTERNAL_WORKQUEUE
#if DISPATCH_USE_PTHREAD_POOL
    if (likely(dx_type(dq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE))
#endif
    {
        if (unlikely(!os_atomic_cmpxchg2o(dq, dgq_pending, 0, n, relaxed))) {
            _dispatch_root_queue_debug("worker thread request still pending "
                    "for global queue: %p", dq);
            return;
        }
    }
#endif // !DISPATCH_USE_INTERNAL_WORKQUEUE
    return _dispatch_root_queue_poke_slow(dq, n, floor);
}

繼續搜索重點_dispatch_root_queue_poke_slow(有省略)

DISPATCH_NOINLINE
static void
_dispatch_root_queue_poke_slow(dispatch_queue_global_t dq, int n, int floor)
{
    int remaining = n;
    int r = ENOSYS;

    _dispatch_root_queues_init();//重點
    
    ...
    //do-while循環創建線程
    do {
        _dispatch_retain(dq); // released in _dispatch_worker_thread
        while ((r = pthread_create(pthr, attr, _dispatch_worker_thread, dq))) {
            if (r != EAGAIN) {
                (void)dispatch_assume_zero(r);
            }
            _dispatch_temporary_resource_shortage();
        }
    } while (--remaining);
    
    ...
}
  • 通過_dispatch_root_queues_init方法注冊回調

  • 通過do-while循環創建線程,使用pthread_create方法

搜索 _dispatch_root_queues_init

_dispatch_root_queues_init(void)
{
    dispatch_once_f(&_dispatch_root_queues_pred, NULL,
            _dispatch_root_queues_init_once);
}

  • dispatch_once_f 好眼熟的函數 這不就是單例(后面分析單例底層,這里不做說明) ,其中傳入的func是*_dispatch_root_queues_init_once

我們繼續搜索 _dispatch_root_queues_init_once 看其實現

2251862-de6a9414407814d3.jpg
  • 發現了其內部不同事務的調用句柄都是_dispatch_worker_thread2。

小總結
1 、看到這里 我們 先 小總結 一下 ,異步函數 ,首先 將任務進行包裝 ,然后 通過 dxpush 函數 遞歸 的去重定向到根隊列 并執行 根隊列 的 dopush 也就是 _dispatch_root_queue_push,并注冊了 其執行句柄 _dispatch_worker_thread2 并看到了 線程的創建。
分析:
此時此刻 ,我們需要整體的去分析一下 隊列 是什么?它是一種數據結構,并發隊列+異步函數,可以支持同一時間多條線程同時執行任務。那任務在什么時候開始執行?它是就緒狀態,等待著cpu來進行調度。 所以
_dispatch_root_queue_push 個人認為 它就是在為任務分配線程。及丟到 并發隊列中。等待cpu的調度執行。

2 、打印任務 調用棧 我們也看到 任務的執行流程。流程 start_wqthread -> _pthread_wqthread -> _dispatch_worker_thread2 ->_dispatch_root_queue_drain ->_dispatch_async_redirect_invoke ->_dispatch_continuation_pop -> _dispatch_client_callout -> _dispatch_call_block_and_release。

3 、其中start_wqthread ->_pthread_wqthread并不在 libdispatch.dylib 源碼之中。我們這里也不過多分析。我們就將其想象成,cpu來進行調度即可。流程已經熟悉。那每個函數之間的調用又發生了什么 我們下面進入到調用棧到的流程底層源碼實現分析。

從 _dispatch_worker_thread2 開始查看

static void
_dispatch_worker_thread2(pthread_priority_t pp)
{
    bool overcommit = pp & _PTHREAD_PRIORITY_OVERCOMMIT_FLAG;
    dispatch_queue_global_t dq;

    pp &= _PTHREAD_PRIORITY_OVERCOMMIT_FLAG | ~_PTHREAD_PRIORITY_FLAGS_MASK;
    _dispatch_thread_setspecific(dispatch_priority_key, (void *)(uintptr_t)pp);
    dq = _dispatch_get_root_queue(_dispatch_qos_from_pp(pp), overcommit);
//自省線程添加
    _dispatch_introspection_thread_add();
//跟蹤運行時事件
    _dispatch_trace_runtime_event(worker_unpark, dq, 0);

    int pending = os_atomic_dec2o(dq, dgq_pending, relaxed);
    dispatch_assert(pending >= 0);
/*-----------------------------------調用這里 主隊列 線程----------------------------------------------*/
    _dispatch_root_queue_drain(dq, dq->dq_priority,
            DISPATCH_INVOKE_WORKER_DRAIN | DISPATCH_INVOKE_REDIRECTING_DRAIN);
 /*------------------------------------------------------------------------------------*/
    _dispatch_voucher_debug("root queue clear", NULL);
    _dispatch_reset_voucher(NULL, DISPATCH_THREAD_PARK);
    _dispatch_trace_runtime_event(worker_park, NULL, 0);
}

搜索 _dispatch_root_queue_drain

static void
_dispatch_root_queue_drain(dispatch_queue_global_t dq,
        dispatch_priority_t pri, dispatch_invoke_flags_t flags)
{
#if DISPATCH_DEBUG
    dispatch_queue_t cq;
    if (unlikely(cq = _dispatch_queue_get_current())) {
        DISPATCH_INTERNAL_CRASH(cq, "Premature thread recycling");
    }
#endif
    _dispatch_queue_set_current(dq);
    _dispatch_init_basepri(pri);
    _dispatch_adopt_wlh_anon();

    struct dispatch_object_s *item;
    bool reset = false;
    dispatch_invoke_context_s dic = { };
#if DISPATCH_COCOA_COMPAT
    _dispatch_last_resort_autorelease_pool_push(&dic);
#endif // DISPATCH_COCOA_COMPAT
    _dispatch_queue_drain_init_narrowing_check_deadline(&dic, pri);
    _dispatch_perfmon_start();
// 循環取出任務   
while (likely(item = _dispatch_root_queue_drain_one(dq))) {
        if (reset) _dispatch_wqthread_override_reset();
/*---------------------------------------------------------------------------*/ 
///調度出任務的執行函數   
             _dispatch_continuation_pop_inline(item, &dic, flags, dq);
/*-------------------------------------------------------------------------*/
        reset = _dispatch_reset_basepri_override();
        if (unlikely(_dispatch_queue_drain_should_narrow(&dic))) {
            break;
        }
    }

    // overcommit or not. worker thread
    if (pri & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) {
        _dispatch_perfmon_end(perfmon_thread_worker_oc);
    } else {
        _dispatch_perfmon_end(perfmon_thread_worker_non_oc);
    }

#if DISPATCH_COCOA_COMPAT
    _dispatch_last_resort_autorelease_pool_pop(&dic);
#endif // DISPATCH_COCOA_COMPAT
    _dispatch_reset_wlh();
    _dispatch_clear_basepri();
    _dispatch_queue_set_current(NULL);
}

搜索 _dispatch_continuation_pop_inline

static inline void
_dispatch_continuation_pop_inline(dispatch_object_t dou,
        dispatch_invoke_context_t dic, dispatch_invoke_flags_t flags,
        dispatch_queue_class_t dqu)
{
    dispatch_pthread_root_queue_observer_hooks_t observer_hooks =
            _dispatch_get_pthread_root_queue_observer_hooks();
    ///監聽處理方便調試
    if (observer_hooks) observer_hooks->queue_will_execute(dqu._dq);
    
    flags &= _DISPATCH_INVOKE_PROPAGATE_MASK;
    
    /*-------------------研究重點----------------------------*/
    if (_dispatch_object_has_vtable(dou)) {
        dx_invoke(dou._dq, dic, flags);
    } else {
        _dispatch_continuation_invoke_inline(dou, flags, dqu);
    }
    /*-----------------------------------------------*/
    
    if (observer_hooks) observer_hooks->queue_did_execute(dqu._dq);
}
  • 這里我們看到了 我們先看 dx_invoke()這個分支
  • 之前說過 dispatch_async是有do_vtable成員變量的,所以會走進 這個分支,又invoke方法指定為_dispatch_async_redirect_invoke,所以執行該函數。
  • 相同的,如果是dispatch_get_global_queue也會走這個分支,執行_dispatch_queue_override_invoke 方法。

搜索dx_invoke

#define dx_invoke(x, y, z) dx_vtable(x)->do_invoke(x, y, z)
  • 可以看到 dx_invoke是個宏定義 是調用對象 的do_invoke 屬性 和dx_push 是一摸摸一樣樣的 數據結構封裝
  • 并且 在 第二次 dx_push 的時候(也就是由根隊列發起push之前的 方法包裝器就已經指定do_invoke是_dispatch_async_redirect_invoke ) 就已經知道了do_invoke 屬性 是哪個函數。

搜索 _dispatch_async_redirect_invoke

void
_dispatch_async_redirect_invoke(dispatch_continuation_t dc,
        dispatch_invoke_context_t dic, dispatch_invoke_flags_t flags)
{
    dispatch_thread_frame_s dtf;
    struct dispatch_continuation_s *other_dc = dc->dc_other;
    dispatch_invoke_flags_t ctxt_flags = (dispatch_invoke_flags_t)dc->dc_ctxt;
    // if we went through _dispatch_root_queue_push_override,
    // the "right" root queue was stuffed into dc_func
    dispatch_queue_global_t assumed_rq = (dispatch_queue_global_t)dc->dc_func;
    dispatch_lane_t dq = dc->dc_data;
    dispatch_queue_t rq, old_dq;
    dispatch_priority_t old_dbp;

    if (ctxt_flags) {
        flags &= ~_DISPATCH_INVOKE_AUTORELEASE_MASK;
        flags |= ctxt_flags;
    }
    old_dq = _dispatch_queue_get_current();
    if (assumed_rq) {
        old_dbp = _dispatch_root_queue_identity_assume(assumed_rq);
        _dispatch_set_basepri(dq->dq_priority);
    } else {
        old_dbp = _dispatch_set_basepri(dq->dq_priority);
    }

    uintptr_t dc_flags = DC_FLAG_CONSUME | DC_FLAG_NO_INTROSPECTION;
    _dispatch_thread_frame_push(&dtf, dq);
    _dispatch_continuation_pop_forwarded(dc, dc_flags, NULL, {
        _dispatch_continuation_pop(other_dc, dic, flags, dq);
    });
    _dispatch_thread_frame_pop(&dtf);
    if (assumed_rq) _dispatch_queue_set_current(old_dq);
    _dispatch_reset_basepri(old_dbp);

    rq = dq->do_targetq;
    while (unlikely(rq->do_targetq && rq != old_dq)) {
        _dispatch_lane_non_barrier_complete(upcast(rq)._dl, 0);
        rq = rq->do_targetq;
    }

    // pairs with _dispatch_async_redirect_wrap
    _dispatch_lane_non_barrier_complete(dq, DISPATCH_WAKEUP_CONSUME_2);
}


搜索 _dispatch_continuation_pop

_dispatch_continuation_pop(dispatch_object_t dou, dispatch_invoke_context_t dic,
        dispatch_invoke_flags_t flags, dispatch_queue_class_t dqu)
{
    _dispatch_continuation_pop_inline(dou, dic, flags, dqu._dq);
}

搜索_dispatch_continuation_pop_inline

static inline void
_dispatch_continuation_pop_inline(dispatch_object_t dou,
        dispatch_invoke_context_t dic, dispatch_invoke_flags_t flags,
        dispatch_queue_class_t dqu)
{
    dispatch_pthread_root_queue_observer_hooks_t observer_hooks =
            _dispatch_get_pthread_root_queue_observer_hooks();
    ///監聽處理方便調試
    if (observer_hooks) observer_hooks->queue_will_execute(dqu._dq);
    
    flags &= _DISPATCH_INVOKE_PROPAGATE_MASK;
    
    /*-------------------研究重點----------------------------*/
    if (_dispatch_object_has_vtable(dou)) {
        dx_invoke(dou._dq, dic, flags);
    } else {
        _dispatch_continuation_invoke_inline(dou, flags, dqu);
    }
    /*-----------------------------------------------*/
    
    if (observer_hooks) observer_hooks->queue_did_execute(dqu._dq);
}
  • 這個函數好眼熟 又回到了這里 只不過這次走的else

搜索_dispatch_continuation_invoke_inline

DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_continuation_invoke_inline(dispatch_object_t dou,
        dispatch_invoke_flags_t flags, dispatch_queue_class_t dqu)
{
    dispatch_continuation_t dc = dou._dc, dc1;
    dispatch_invoke_with_autoreleasepool(flags, {
        uintptr_t dc_flags = dc->dc_flags;
        // Add the item back to the cache before calling the function. This
        // allows the 'hot' continuation to be used for a quick callback.
        //
        // The ccache version is per-thread.
        // Therefore, the object has not been reused yet.
        // This generates better assembly.
        _dispatch_continuation_voucher_adopt(dc, dc_flags);
        if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {
            _dispatch_trace_item_pop(dqu, dou);
        }
        if (dc_flags & DC_FLAG_CONSUME) {
            dc1 = _dispatch_continuation_free_cacheonly(dc);
        } else {
            dc1 = NULL;
        }
        if (unlikely(dc_flags & DC_FLAG_GROUP_ASYNC)) {
            _dispatch_continuation_with_group_invoke(dc);
        } else {
            _dispatch_client_callout(dc->dc_ctxt, dc->dc_func);
            _dispatch_trace_item_complete(dc);
        }
        if (unlikely(dc1)) {
            _dispatch_continuation_free_to_cache_limit(dc1);
        }
    });
    _dispatch_perfmon_workitem_inc();
}

搜索 _dispatch_client_callout

_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
    return f(ctxt);
}
  • 看到這里 此時外部的block任務 就會執行了。而此時的f 是是什么? ctxt 是什么?
    f: _dispatch_call_block_and_release
    ctxt: block任務的上下文的指針。
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容