The previous chapter mainly looked at the source code of the basic data structure and some of the content related to the queue, so this article from the creation of a custom queue as the main line, encountered in the process of new data structure and then expanded as a branch to learn, so start! ⛽ ️ ⛽ ️

The three most commonly used queues in GCD are: Primary queue(dispatch_get_main_queue()), global concurrent queue(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT), 0), dispatch_queue_create (dispatch_queue_create), then we start by creating a custom queue.

Dispatch_queue_create (create custom queue)

Here’s a look at the queue creation process along the source code.

// Create a concurrent queue
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrent", DISPATCH_QUEUE_CONCURRENT);
// Create a serial queue
dispatch_queue_t serialQueue = dispatch_queue_create("com.serial", DISPATCH_QUEUE_SERIAL);
Copy the code

DISPATCH_QUEUE_SERIAL

Property for creating a scheduling queue (serial queue) that serial invokes blocks in FIFO order with a value of NULL.

#define DISPATCH_QUEUE_SERIAL NULL
Copy the code

DISPATCH_QUEUE_CONCURRENT

Properties that can be used to create a dispatch queue (concurrent queue) that simultaneously invokes blocks and supports barrier blocks submitted through the Dispatch barrier API (Dispatch_barrier_Async). (Block task blocks for regular blocks and Barriers)

#define DISPATCH_GLOBAL_OBJECT(type, object) ((OS_OBJECT_BRIDGE type)&(object))

#define DISPATCH_QUEUE_CONCURRENT \
        DISPATCH_GLOBAL_OBJECT(dispatch_queue_attr_t, _dispatch_queue_attr_concurrent)
API_AVAILABLE(macos(10.7), ios(4.3))
DISPATCH_EXPORT
struct dispatch_queue_attr_s _dispatch_queue_attr_concurrent; // There is a global variable of type dispatch_queue_attr_s.
Copy the code

The DISPATCH_QUEUE_CONCURRENT macro definition forces the global variable _dispatch_queue_attr_CONCURRENT to a variable of type dispatch_queue_attr_t.

Implementation of the dispatch_queue_CREATE function. The label parameter is a custom string label to attach to the queue, the attr parameter is a predefined attribute, DISPATCH_QUEUE_SERIAL, DISPATCH_QUEUE_CONCURRENT, or custom dispatch_queue_attr_make_with_* Struct instance.

dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr) {
    return _dispatch_lane_create_with_target(label, attr, DISPATCH_TARGET_QUEUE_DEFAULT, true);
}
Copy the code

The dispatch_queue_CREATE function internally calls an intermediate function, _dispatch_lane_CREATE_WITH_target, with a DISPATCH_TARGET_QUEUE_DEFAULT.

DISPATCH_TARGET_QUEUE_DEFAULT

DISPATCH_TARGET_QUEUE_DEFAULT is delivered to dispatch_queue_CREATE_WITH_target, dispatch_set_target_queue and dispatch_source_create Function to indicate that the default target queue should be used. Its actual value is NULL.

#define DISPATCH_TARGET_QUEUE_DEFAULT NULL
Copy the code

dispatch_lane_t

Dispatch_lane_t is a pointer to the dispatch_lane_s structure.

typedef struct dispatch_lane_s {
    DISPATCH_LANE_CLASS_HEADER(lane);
    /* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN *dispatch_lane_t;
Copy the code

DISPATCH_LANE_CLASS_HEADER

#define DISPATCH_LANE_CLASS_HEADER(x) \
    struct dispatch_queue_s _as_dq[0]; \
    DISPATCH_QUEUE_CLASS_HEADER(x, \
            struct dispatch_object_s *volatile dq_items_tail); \
    dispatch_unfair_lock_s dq_sidelock; \
    struct dispatch_object_s *volatile dq_items_head; \
    uint32_t dq_side_suspend_cnt
Copy the code

To fully expand the dispatch_lane_s macro:

typedef struct dispatch_lane_s {
    // indicates the parent classes dispatch_queue_s, dispatch_object_s, and _OS_OBJect_s of dispatch_lane_s
    struct dispatch_queue_s _as_dq[0].
    struct dispatch_object_s _as_do[0].
    struct _os_object_s _as_os_obj[0].
    
    const struct dispatch_lane_vtable_s *do_vtable; /* must be pointer-sized */
    int volatile do_ref_cnt;
    int volatile do_xref_cnt;
    
    struct dispatch_lane_s *volatile do_next;
    struct dispatch_queue_s *do_targetq;
    void *do_ctxt;
    void *do_finalizer
    
    struct dispatch_object_s *volatile dq_items_tail;
    
    union { 
        uint64_t volatile dq_state;
        struct {
            dispatch_lock dq_state_lock;
            uint32_t dq_state_bits;
        };
    };
    
    /* LP64 global queue cacheline boundary */
    unsigned long dq_serialnum;
    const char *dq_label;
    
    union { 
        uint32_t volatile dq_atomic_flags;
        struct {
            const uint16_t dq_width; // The width of the queue (serial queue 1, concurrent queue greater than 1)
            const uint16_t __dq_opaque2;
        };
    };
    
    dispatch_priority_t dq_priority;
    union {
        struct dispatch_queue_specific_head_s *dq_specific_head;
        struct dispatch_source_refs_s *ds_refs;
        struct dispatch_timer_source_refs_s *ds_timer_refs;
        struct dispatch_mach_recv_refs_s *dm_recv_refs;
        struct dispatch_channel_callbacks_s const *dch_callbacks;
    };
    int volatile dq_sref_cnt;
    
    dispatch_unfair_lock_s dq_sidelock; / / lock
    struct dispatch_object_s *volatile dq_items_head; / / head
    uint32_t dq_side_suspend_cnt // Suspend times
    
} DISPATCH_ATOMIC64_ALIGN *dispatch_lane_t;
Copy the code

Dispatch_lane_s is a subclass of dispatch_queue_s. And the _dispatch_lane_create_with_target function returns dispatch_lane_s instead of dispatch_queue_s.

DISPATCH_QUEUE_WIDTH_MAX

#define DISPATCH_QUEUE_WIDTH_FULL            0x1000ull // (4096) is used when creating the global queue
#define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1) // 0xffFull (4095)
#define DISPATCH_QUEUE_WIDTH_MAX  (DISPATCH_QUEUE_WIDTH_FULL - 2) // maximum queue width (4094)
Copy the code

_dispatch_priority_make

Priority and relative quantity.

#define _dispatch_priority_make(qos, relpri) \
    (qos ? ((((qos) << DISPATCH_PRIORITY_QOS_SHIFT) & DISPATCH_PRIORITY_QOS_MASK) | \
    ((dispatch_priority_t)(relpri - 1) & DISPATCH_PRIORITY_RELPRI_MASK)) : 0)
Copy the code

_dispatch_lane_create_with_target

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_queue_attr_to_info
    // 1. If dQA is DISPATCH_QUEUE_SERIAL (value NULL),
    // an empty dispatch_queue_attr_info_t structure instance is returned, (dispatch_queue_attr_info_t dqai = {};) .
    // 2. If dQA is DISPATCH_QUEUE_CONCURRENT (global variable _dispatch_queue_attr_concurrent) as an input parameter,
    // an instance of the dispatch_queue_attr_info_t structure is returned with dqai_concurrent true (dqai_concurrent true indicates a concurrent queue).
    // dispatch_queue_attr_t = dispatch_queue_attr_t;
    The dispatch_queue_attr_info_t struct instance is returned after each member variable of the dispatch_queue_attr_info_t struct instance is assigned a value.
    
    dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

    //
    // Step 1: Normalize arguments (qos, overcommit, tq)
    //

    dispatch_qos_t qos = dqai.dqai_qos; // (dqai_qos indicates thread priority)
    
    // if HAVE_PTHREAD_WORKQUEUE_QOS is false a dqai_qos switch is performed
#if! HAVE_PTHREAD_WORKQUEUE_QOS
    if (qos == DISPATCH_QOS_USER_INTERACTIVE) {
        // If "user interaction" is the highest priority, then "user start" is the second priority
        dqai.dqai_qos = qos = DISPATCH_QOS_USER_INITIATED;
    }
    if (qos == DISPATCH_QOS_MAINTENANCE) {
        // If "QOS_CLASS_MAINTENANCE" is the lowest priority, then "background threads" is the second-to-last priority
        dqai.dqai_qos = qos = DISPATCH_QOS_BACKGROUND;
    }
#endif / /! HAVE_PTHREAD_WORKQUEUE_QOS

    // Check whether "overuse (more than the physical core number)" is allowed.
    _dispatch_queue_attr_overcommit_t overcommit = dqai.dqai_overcommit;
    if(overcommit ! = _dispatch_queue_attr_overcommit_unspecified && tq) {// If overcommit does not equal "overcommit not specified" and tq is not null
        // (dispatch_queue_create is NULL)
        if (tq->do_targetq) {
            // crash
            DISPATCH_CLIENT_CRASH(tq, "Cannot specify both overcommit and "
                    "a non-global target queue"); }}if (tq && dx_type(tq) == DISPATCH_QUEUE_GLOBAL_ROOT_TYPE) {
        // Handle discrepancies between attr and target queue, attributes win
        // Handle the difference between the attR and the target queue, with the attR as the main
        
        // If the destination queue exists and the destination queue is the global root queue
        if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
            // If overcommit is not specified
            if (tq->dq_priority & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) {
                // If the priority of the destination queue is DISPATCH_PRIORITY_FLAG_OVERCOMMIT, set overCOMMIT to permit
                overcommit = _dispatch_queue_attr_overcommit_enabled;
            } else {
                // Otherwise, it is not allowedovercommit = _dispatch_queue_attr_overcommit_disabled; }}// If the priority is not specified, the priority of the newly created queue inherits the priority of the target queue
        if (qos == DISPATCH_QOS_UNSPECIFIED) {
            qos = _dispatch_priority_qos(tq->dq_priority);
        }
        
        / / NULL tq
        tq = NULL;
    } else if(tq && ! tq->do_targetq) {// target is a pthread or runloop root queue, setting QoS or overcommit is disallowed
        // Target queue is a pthread or runloop root queue. Setting QoS or overcommit is not allowed
        
        if(overcommit ! = _dispatch_queue_attr_overcommit_unspecified) {// Crash if tq exists and overcommit is not unspecified
            DISPATCH_CLIENT_CRASH(tq, "Cannot specify an overcommit attribute "
                    "and use this kind of target queue"); }}else {
        // tq is NULL
        
        if (overcommit == _dispatch_queue_attr_overcommit_unspecified) {
            // Serial queues default to overcommit! (Serial queue defaults to Overcommit)
            // Dqai_concurrent is false for the serial queue and true for the concurrent queue.
            
            // When dqai.dqai_concurrent is true, overcommit is not allowed, otherwise overcommit is allowedovercommit = dqai.dqai_concurrent ? _dispatch_queue_attr_overcommit_disabled : _dispatch_queue_attr_overcommit_enabled; }}// When tq is NULL, i.e. DISPATCH_TARGET_QUEUE_DEFAULT (value NULL),
    // Get a root queue from the _dispatch_root_queues global root queue array according to qos and overCOMMIT as the target queue for the new queue
    if(! 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)) {// If no target queue is obtained, crash
            DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute"); }}//
    // Initialize the queue
    //
    
    Legacy passes true by default when the dispatch_queue_create function is called
    if (legacy) {
        // if any of these attributes is specified, use non legacy classes
        // If any of these attributes are specified, use a non-old class
        
        // Dqai_inactive and dqai_autorelease_frequency
        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) {
        // Concurrent queue
        vtable = DISPATCH_VTABLE(queue_concurrent); // _dispatch_queue_conCURRENT_vtable Function calls that wrap queues can make
    } else {
        // Serial queue
        vtable = DISPATCH_VTABLE(queue_serial); // _dispatch_queue_serial_vtable Function call that can be made by the package queue
    }
    
    // Automatically release the frequency
    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;
    }
    
    // Queue label
    if (label) {
        // _dispatch_strdup_if_mutable allocates space and copies the original string if the label parameter is a mutable string, and returns the original if the label parameter is an immutable string
        const char *tmp = _dispatch_strdup_if_mutable(label);
        
        if(tmp ! = label) {// A new space is requested
            dqf |= DQF_LABEL_NEEDS_FREE;
            // Assign "new value" to labellabel = tmp; }}// void *_dispatch_object_alloc(const void *vtable, size_t size);
    // dispatch_lane_s is a subclass of dispatch_queue_s.
    
    // dq is a pointer to the dispatch_lane_s structure
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s));
            
    // If dqai.dqai_concurrent is true the input parameter is DISPATCH_QUEUE_WIDTH_MAX (4094) otherwise 1
    // if dqai.dqai_inactive is true, it is inactive; otherwise, it is active
    // #define DISPATCH_QUEUE_ROLE_INNER 0x0000000000000000ull
    // #define DISPATCH_QUEUE_INACTIVE 0x0180000000000000ull
    
    // Initialize dQ
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));

    // Queue signature
    dq->dq_label = label;
    / / priority
    dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,
            dqai.dqai_relpri);
    // overcommit
    if (overcommit == _dispatch_queue_attr_overcommit_enabled) {
        dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
    }
    
    // If it is inactive
    if(! dqai.dqai_inactive) {// The priority of the new queue is inherited from the priority of the destination queue
        _dispatch_queue_priority_inherit_from_target(dq, tq);
        _dispatch_lane_inherit_wlh_from_target(dq, tq);
    }
    
    // The internal reference count of the target queue increases by 1 (atomic operation)
    _dispatch_retain(tq);
    
    // Set the target queue for the new queue
    dq->do_targetq = tq;
    
    // DEBUG print function
    _dispatch_object_debug(dq, "%s", __func__);
    return _dispatch_trace_queue_create(dq)._dq;
}
Copy the code

The execution flow of the _dispatch_lane_create_with_target function is shown in the note, and the key points are extracted and analyzed.

_dispatch_get_root_queue

When tQ does not exist, the _dispatch_get_root_queue call returns a dispatch_queue_global_t assignment to TQ. In the dispatch_queue_CREATE function call tQ passes a default value: DISPATCH_TARGET_QUEUE_DEFAULT (which is NULL), so the _dispatch_get_root_queue function is called when we create serial or concurrent queues to get a target queue.

// When tq is NULL, i.e. DISPATCH_TARGET_QUEUE_DEFAULT (value NULL),
// Get a root queue from the _dispatch_root_queues global root queue array according to qos and overCOMMIT as the target queue for the new queue
if(! 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)) {// If no target queue is obtained, crash
        DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute"); }}Copy the code

The _dispatch_get_root_queue function has two parameters: qos and overcommit. What are the two parameters used by the _dispatch_get_root_queue function when DQA is DISPATCH_QUEUE_SERIAL or DISPATCH_QUEUE_CONCURRENT?

  • qosAnalysis:_dispatch_queue_attr_to_infoWhen we know whendqaDISPATCH_QUEUE_SERIALorDISPATCH_QUEUE_CONCURRENTWill not return todispatch_queue_attr_info_tOf the struct instancedqai_qosMember variable assignment sodqai_qosThe value of is 0, i.eDISPATCH_QOS_UNSPECIFIED(#define DISPATCH_QOS_UNSPECIFIED ((dispatch_qos_t)0)), thenqos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qosThe value of isDISPATCH_QOS_DEFAULT(#define DISPATCH_QOS_DEFAULT ((dispatch_qos_t)4)), that is, whether a serial queue or a concurrent queue is created when called_dispatch_get_root_queueWhen the functionqosI’m using all 4’s.
  • overcommitAnalysis:_dispatch_queue_attr_to_infoWhen we know the serial queuedqai.dqai_concurrentA value offalse, for concurrent queuestrue, that is, whendqaDISPATCH_QUEUE_SERIALWhen,overcommitThe value is_dispatch_queue_attr_overcommit_enabledwhendqaDISPATCH_QUEUE_CONCURRENTWhen,overcommitThe value is_dispatch_queue_attr_overcommit_disabled.

Dqa is DISPATCH_QUEUE_SERIAL:

tq = _dispatch_get_root_queue(4.true)->_as_dq;
Copy the code

Dqa is DISPATCH_QUEUE_CONCURRENT:

tq = _dispatch_get_root_queue(4.false)->_as_dq;
Copy the code

The _dispatch_get_root_queue function is simple to implement, simply fetching the specified queue from the _dispatch_root_queues root queue array by subscript.

DISPATCH_ALWAYS_INLINE DISPATCH_CONST
static inline dispatch_queue_global_t
_dispatch_get_root_queue(dispatch_qos_t qos, bool overcommit)
{
    // DISPATCH_QOS_MAX = 6
    // DISPATCH_QOS_MIN = 1
    
    if (unlikely(qos < DISPATCH_QOS_MIN || qos > DISPATCH_QOS_MAX)) {
        DISPATCH_CLIENT_CRASH(qos, "Corrupted priority");
    }
    
    return &_dispatch_root_queues[2 * (qos - 1) + overcommit];
}
Copy the code
  • DISPATCH_QUEUE_SERIALThe target queue is:&_dispatch_root_queues[7](com. Apple. Root. The default – qos. Overcommit).
  • DISPATCH_QUEUE_CONCURRENTThe target queue is:&_dispatch_root_queues[6](com. Apple. Root. The default – qos)

_dispatch_root_queues

Two macros were defined when building the _dispatch_root_queues array: _DISPATCH_ROOT_QUEUE_IDX and _DISPATCH_ROOT_QUEUE_ENTRY to initialize each element in the array.

// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol
struct dispatch_queue_global_s _dispatch_root_queues[] = {
    _DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, 0,
        .dq_label = "com.apple.root.maintenance-qos",
        .dq_serialnum = 4,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.maintenance-qos.overcommit",
        .dq_serialnum = 5,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, 0,
        .dq_label = "com.apple.root.background-qos",
        .dq_serialnum = 6,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.background-qos.overcommit",
        .dq_serialnum = 7,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, 0,
        .dq_label = "com.apple.root.utility-qos",
        .dq_serialnum = 8,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.utility-qos.overcommit",
        .dq_serialnum = 9,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
        .dq_label = "com.apple.root.default-qos",
        .dq_serialnum = 10,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,
            DISPATCH_PRIORITY_FLAG_FALLBACK | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.default-qos.overcommit",
        .dq_serialnum = 11,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, 0,
        .dq_label = "com.apple.root.user-initiated-qos",
        .dq_serialnum = 12,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.user-initiated-qos.overcommit",
        .dq_serialnum = 13,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, 0,
        .dq_label = "com.apple.root.user-interactive-qos",
        .dq_serialnum = 14,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.user-interactive-qos.overcommit",
        .dq_serialnum = 15,),};Copy the code

_DISPATCH_ROOT_QUEUE_IDX

Check whether n is overcommit based on flags.

#define _DISPATCH_ROOT_QUEUE_IDX(n, flags) \
        ((flags & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) ? \
        DISPATCH_ROOT_QUEUE_IDX_##n##_QOS_OVERCOMMIT : \
        DISPATCH_ROOT_QUEUE_IDX_##n##_QOS)
Copy the code

DISPATCH_GLOBAL_OBJECT_HEADER

#if OS_OBJECT_HAVE_OBJC1
#define DISPATCH_GLOBAL_OBJECT_HEADER(name) \
    .do_vtable = DISPATCH_VTABLE(name), \
    ._objc_isa = DISPATCH_OBJC_CLASS(name), \
    .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, \
    .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT
#else
#defineDISPATCH_GLOBAL_OBJECT_HEADER(name) \ ⬅️ We use this part of the macro definition
    .do_vtable = DISPATCH_VTABLE(name), \
    .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, \
    .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT
#endif
Copy the code

DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE

/* Magic dq_state values for global queues: they have QUEUE_FULL and IN_BARRIER set to force the slow path in dispatch_barrier_sync() and dispatch_sync() */
#define DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE \
        (DISPATCH_QUEUE_WIDTH_FULL_BIT | DISPATCH_QUEUE_IN_BARRIER)
Copy the code

_dispatch_root_queue_ctxt

#if DISPATCH_USE_INTERNAL_WORKQUEUE
static struct dispatch_pthread_root_queue_context_s
        _dispatch_pthread_root_queue_contexts[DISPATCH_ROOT_QUEUE_COUNT];
#define _dispatch_root_queue_ctxt(n) &_dispatch_pthread_root_queue_contexts[n]
#else
#define _dispatch_root_queue_ctxt(n) NULL
#endif // DISPATCH_USE_INTERNAL_WORKQUEUE
Copy the code

DQF_WIDTH

#define DQF_WIDTH(n) ((dispatch_queue_flags_t)(uint16_t)(n))
Copy the code

_dispatch_priority_make_fallback

#define _dispatch_priority_make_fallback(qos) \
    (qos ? ((((qos) << DISPATCH_PRIORITY_FALLBACK_QOS_SHIFT) & \
     DISPATCH_PRIORITY_FALLBACK_QOS_MASK) | DISPATCH_PRIORITY_FLAG_FALLBACK) : 0)
Copy the code

_dispatch_priority_make

#define _dispatch_priority_make(qos, relpri) \
    (qos ? ((((qos) << DISPATCH_PRIORITY_QOS_SHIFT) & DISPATCH_PRIORITY_QOS_MASK) | \
     ((dispatch_priority_t)(relpri - 1) & DISPATCH_PRIORITY_RELPRI_MASK)) : 0)

// Call an example:
#define DISPATCH_QOS_USER_INTERACTIVE   ((dispatch_qos_t)6)
#define DISPATCH_PRIORITY_QOS_SHIFT          8
#define DISPATCH_PRIORITY_QOS_MASK           ((dispatch_priority_t)0x00000f00)
#define DISPATCH_PRIORITY_RELPRI_MASK        ((dispatch_priority_t)0x000000ff)

_dispatch_priority_make(DISPATCH_QOS_USER_INTERACTIVE, 0) can be expanded as follows: (6 ? ((((6) < <8) & 0x00000f00) | ((dispatch_priority_t) (0 - 1) & 0x000000ff)) : 0)
Copy the code

_DISPATCH_ROOT_QUEUE_ENTRY

Let’s expand _DISPATCH_ROOT_QUEUE_ENTRY step by step:

#define _DISPATCH_ROOT_QUEUE_ENTRY(n, flags, ...) \
    [_DISPATCH_ROOT_QUEUE_IDX(n, flags)] = { \
        DISPATCH_GLOBAL_OBJECT_HEADER(queue_global), \
        .dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, \
        .do_ctxt = _dispatch_root_queue_ctxt(_DISPATCH_ROOT_QUEUE_IDX(n, flags)), \
        .dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL), \
        .dq_priority = flags | ((flags & DISPATCH_PRIORITY_FLAG_FALLBACK) ? \
                _dispatch_priority_make_fallback(DISPATCH_QOS_##n) : \
                _dispatch_priority_make(DISPATCH_QOS_##n, 0)), \
        __VA_ARGS__ \
    }
Copy the code

Expand DISPATCH_GLOBAL_OBJECT_HEADER with DISPATCH_GLOBAL_OBJECT_HEADER

#define _DISPATCH_ROOT_QUEUE_ENTRY(n, flags, ...) \
    [_DISPATCH_ROOT_QUEUE_IDX(n, flags)] = { \
        .do_vtable = (&_dispatch_queue_global_vtable), \
        .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, \ // INT_MAX
        .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, \ // INT_MAX
        .dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, \ / / (DISPATCH_QUEUE_WIDTH_FULL_BIT | DISPATCH_QUEUE_IN_BARRIER) (0 x0020000000000000ull | 0 x0040000000000000ull)
        .do_ctxt = _dispatch_root_queue_ctxt(_DISPATCH_ROOT_QUEUE_IDX(n, flags)), \
        .dq_atomic_flags = ((dispatch_queue_flags_t) (uint16_t) (0xfffull)), \
        .dq_priority = flags | ((flags & DISPATCH_PRIORITY_FLAG_FALLBACK) ? \
                _dispatch_priority_make_fallback(DISPATCH_QOS_##n) : \ _dispatch_priority_make(DISPATCH_QOS_##n, 0)), \ __VA_ARGS__ \ }
Copy the code

The following example is used to expand _DISPATCH_ROOT_QUEUE_ENTRY:

_DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, 0,
    .dq_label = "com.apple.root.user-interactive-qos",
    .dq_serialnum = 14.)Copy the code
[DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS] = {
    .do_vtable = (&_dispatch_queue_global_vtable), 
    .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, // INT_MAX
    .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, // INT_MAX
    .dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, // (DISPATCH_QUEUE_WIDTH_FULL_BIT | DISPATCH_QUEUE_IN_BARRIER)
                                                      // (0x0020000000000000ULL | 0x0040000000000000ULL)
                                                      // The value is 0x0060000000000000ULL
    
    .do_ctxt = _dispatch_root_queue_ctxt(DISPATCH_ROOT_QUEUE_IDX_USER_INTERACTIVE_QOS), / / 10
    .dq_atomic_flags = ((dispatch_queue_flags_t) (uint16_t) (0xfffull)), // The number of concurrent requests in the root queue, (the number of concurrent requests in the custom queue is Oxffeull, 1 less than the root queue, 1 less than the serial queue)
    
    // (6 ? ((((6) << 8) & 0x00000f00) | ((dispatch_priority_t)(0 - 1) & 0x000000ff)) : 0)
    .dq_priority = flags | (_dispatch_priority_make(DISPATCH_QOS_USER_INTERACTIVE, 0)),
    
    .dq_label = "com.apple.root.user-interactive-qos",
    .dq_serialnum = 14,}Copy the code

See that a structure instance is built from the input parameter.

_dispatch_queue_init

After the dispatch_lane_s structure instance is created, the _dispatch_queue_init function is called for initialization.

.// dq is a pointer to the dispatch_lane_s structure
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
        sizeof(struct dispatch_lane_s));
        
// If dqai.dqai_concurrent is true, the input parameter is DISPATCH_QUEUE_WIDTH_MAX (4094); otherwise, it is 1 for serial queues and 4094 for concurrent queues

// if dqai.dqai_inactive is true, it is inactive; otherwise, it is active
// #define DISPATCH_QUEUE_ROLE_INNER 0x0000000000000000ull
// #define DISPATCH_QUEUE_INACTIVE 0x0180000000000000ull

// Initialize dQ
_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
        DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
        (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0)); .Copy the code

dispatch_queue_class_t

Dispatch_queue_class_t is a transparent union type, and each member variable is a pointer to a subclass of the dispatch_queue_s structure.

// Dispatch queue cluster class: type for any dispatch_queue_t
// Scheduling queue cluster class: contains any dispatch_queue_t
typedef union {
    struct dispatch_queue_s* _dq;
    struct dispatch_workloop_s* _dwl;
    struct dispatch_lane_s* _dl;
    struct dispatch_queue_static_s* _dsq;
    struct dispatch_queue_global_s* _dgq;
    struct dispatch_queue_pthread_root_s* _dpq;
    struct dispatch_source_s* _ds;
    struct dispatch_channel_s* _dch;
    struct dispatch_mach_s* _dm;
    dispatch_lane_class_t _dlu;
#ifdef __OBJC__
    id<OS_dispatch_queue> _objc_dq;
#endif
} dispatch_queue_class_t DISPATCH_TRANSPARENT_UNION;
Copy the code

_dispatch_queue_init

// Note to later developers: ensure that any initialization changes are made for statically allocated queues (i.e. _dispatch_main_q).
static inline dispatch_queue_class_t
_dispatch_queue_init(dispatch_queue_class_t dqu, dispatch_queue_flags_t dqf,
        uint16_t width, uint64_t initial_state_bits)
{
    uint64_t dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(width); // Dq_state value: 2 << 41 dq_state value: Oxfffull << 41
    dispatch_queue_t dq = dqu._dq;

    dispatch_assert((initial_state_bits & ~(DISPATCH_QUEUE_ROLE_MASK |
            DISPATCH_QUEUE_INACTIVE)) == 0);

    // Initial_STATE_bits input parameter value is 0x0180000000000000ULL (DISPATCH_QUEUE_INACTIVE) otherwise 0
    if (initial_state_bits & DISPATCH_QUEUE_INACTIVE) {
        // If it is inactive
        // Reference count +2
        dq->do_ref_cnt += 2; // rdar://8181908 see _dispatch_lane_resume
        
        // _DISPATCH_SOURCE_TYPE = 0x00000013, // meta-type for sources
        // #define dx_metatype(x) (dx_vtable(x)->do_type & _DISPATCH_META_TYPE_MASK)
        // #define dx_vtable(x) (&(x)->do_vtable->_os_obj_vtable)
        // _DISPATCH_META_TYPE_MASK = 0x000000ff, // mask for object meta-types
        
        if (dx_metatype(dq) == _DISPATCH_SOURCE_TYPE) {
            // if dq is _DISPATCH_SOURCE_TYPE, the reference count is incremented
            dq->do_ref_cnt++; // released when DSF_DELETED is set
        }
    }

    dq_state |= initial_state_bits;
    dq->do_next = DISPATCH_OBJECT_LISTLESS; // ((void *) 0xffffFFff89abcdef) a literal hard-coded
    
    // #define DQF_WIDTH(n) ((dispatch_queue_flags_t)(uint16_t)(n))
    
    // The concurrent queue is DISPATCH_QUEUE_WIDTH_MAX (Oxffeull)
    // Serial queue is 1
    dqf |= DQF_WIDTH(width);
    
    // #define os_atomic_store2o(p, f, v, m) \ // os_atomic_store(&(p)->f, (v), m)
    Dq_atomic_flags specifies the number of concurrent messages in the update queue. The number of concurrent messages in the custom queue is Oxffeull, which is 1 less than the root queue and 1 less than the serial queue.
    // The number of concurrent messages in the root queue is 0xffFull, 1 more than in the custom queue.
    // The concurrent queue is DISPATCH_QUEUE_WIDTH_MAX (Oxffeull)
    // Serial queue is 1
    // dq_atomic_flags indicates the number of concurrent queues
    
    os_atomic_store2o(dq, dq_atomic_flags, dqf, relaxed);
    
    dq->dq_state = dq_state;
    
    // #define os_atomic_inc_orig(p, m) \ // os_atomic_add_orig((p), 1, m)
    // Atom plus 1
    dq->dq_serialnum =
            os_atomic_inc_orig(&_dispatch_queue_serial_numbers, relaxed);
            
    return dqu;
}
Copy the code

DISPATCH_QUEUE_STATE_INIT_VALUE

Some simple bit manipulation.

// #define DISPATCH_QUEUE_WIDTH_FULL 0x1000ull
// #define DISPATCH_QUEUE_WIDTH_SHIFT 41
// 
Width: #define DISPATCH_QUEUE_WIDTH_MAX (DISPATCH_QUEUE_WIDTH_FULL 2)
/ / 2 < < 41

// serial queue width: 1
// Oxfffull << 41
#define DISPATCH_QUEUE_STATE_INIT_VALUE(width) \
        ((DISPATCH_QUEUE_WIDTH_FULL - (width)) << DISPATCH_QUEUE_WIDTH_SHIFT)
Copy the code

DISPATCH_OBJECT_LISTLESS

Hard coded.

#if DISPATCH_SIZEOF_PTR == 8

// the bottom nibble must not be zero, the rest of the bits should be random
// we sign extend the 64-bit version so that a better instruction encoding is generated on Intel

#define DISPATCH_OBJECT_LISTLESS ((void *)0xffffffff89abcdef)
#else
#define DISPATCH_OBJECT_LISTLESS ((void *)0x89abcdef)
#endif
Copy the code

That’s it for creating custom serial and concurrent queues. So when was our root queue created above? At the end of the libdispatch_init function is a _dispatch_introspection_init() call, Then inside _dispatch_introspection_init we see that inside a for loop the _dispatch_trace_queue_create(&_dispatch_root_queues[I]) call creates the root queue. There are also operations such as _dispatch_trace_queue_CREATE (& _dispatch_MAIN_q) to create the main queue.

_dispatch_introspection_init

void _dispatch_introspection_init(void) {
    _dispatch_introspection.debug_queue_inversions =
            _dispatch_getenv_bool("LIBDISPATCH_DEBUG_QUEUE_INVERSIONS".false);

    // Hack to determine queue TSD offset from start of pthread structure
    uintptr_t thread = _dispatch_thread_self();
    thread_identifier_info_data_t tiid;
    mach_msg_type_number_t cnt = THREAD_IDENTIFIER_INFO_COUNT;
    kern_return_t kr = thread_info(pthread_mach_thread_np((void*)thread),
            THREAD_IDENTIFIER_INFO, (thread_info_t)&tiid, &cnt);
    if (!dispatch_assume_zero(kr)) {
        _dispatch_introspection.thread_queue_offset =
                (void(*)uintptr_t)tiid.dispatch_qaddr - (void*)thread;
    }
    _dispatch_thread_key_create(&dispatch_introspection_key,
            _dispatch_introspection_thread_remove);
    _dispatch_introspection_thread_add(); // add main thread

    // The for loop executes the _dispatch_trace_queue_create function to create the queues in the _dispatch_root_queues array
    for (size_t i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) {
        _dispatch_trace_queue_create(&_dispatch_root_queues[i]);
    }
    
#if DISPATCH_USE_MGR_THREAD && DISPATCH_USE_PTHREAD_ROOT_QUEUES
    _dispatch_trace_queue_create(_dispatch_mgr_q.do_targetq);
#endif
    
    _dispatch_main_q is a global static variable.
    _dispatch_trace_queue_create(&_dispatch_main_q);
    
    _dispatch_trace_queue_create(&_dispatch_mgr_q);
}
Copy the code

DISPATCH_ROOT_QUEUE_COUNT

#define DISPATCH_ROOT_QUEUE_COUNT (DISPATCH_QOS_NBUCKETS * 2)
#define DISPATCH_QOS_NBUCKETS           (DISPATCH_QOS_MAX - DISPATCH_QOS_MIN + 1)

#define DISPATCH_QOS_MAX                DISPATCH_QOS_USER_INTERACTIVE
#define DISPATCH_QOS_USER_INTERACTIVE   ((dispatch_qos_t)6)

#define DISPATCH_QOS_MIN                DISPATCH_QOS_MAINTENANCE
#define DISPATCH_QOS_MAINTENANCE        ((dispatch_qos_t)1)

(6 - 1 + 1) * 2 = 12 
Copy the code

That is, the number of root queues corresponds to twice the number of different priorities (QOS). (Multiplied by 2 indicates whether the overcommit has the same priority.)

_dispatch_main_q master queue

The main queue is a global variable.

API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT
#ifdefined(__DISPATCH_BUILDING_DISPATCH__) && ! defined(__OBJC__)
struct dispatch_queue_static_s _dispatch_main_q;
#else
struct dispatch_queue_s _dispatch_main_q;
#endif
Copy the code

Initialization of the main queue.

// 6618342 Contact the team that owns the Instrument DTrace probe before
// renaming this symbol
struct dispatch_queue_static_s _dispatch_main_q = {
    DISPATCH_GLOBAL_OBJECT_HEADER(queue_main), // Inherits from the parent class
    
#if! DISPATCH_USE_RESOLVERS

    DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS +!! (overcommit) The subscript queue is the target queue.)
    // #define _dispatch_get_default_queue(overcommit) \ // _dispatch_root_queues[DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS + \ //  !!(overcommit)]._as_dq
    
    .do_targetq = _dispatch_get_default_queue(true),
#endif
    // #define DISPATCH_QUEUE_STATE_INIT_VALUE(width) \ // ((DISPATCH_QUEUE_WIDTH_FULL - (width)) << DISPATCH_QUEUE_WIDTH_SHIFT)
    // #define DISPATCH_QUEUE_WIDTH_FULL 0x1000ull
    // // #define DISPATCH_QUEUE_WIDTH_SHIFT 41
    // #define DISPATCH_QUEUE_ROLE_BASE_ANON 0x0000001000000000ull
    
    .dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |
            DISPATCH_QUEUE_ROLE_BASE_ANON, // (0xfffull << 41) | 0x0000001000000000ull
    .dq_label = "com.apple.main-thread".// Queue label (queue name)
    .dq_atomic_flags = DQF_THREAD_BOUND | DQF_WIDTH(1), // The number of concurrent requests is 1, i.e. the serial queue
    .dq_serialnum = 1.// The queue number is 1
};
Copy the code

dispatch_get_main_queue

The main thread dispatch_get_main_queue is the _dispatch_main_q variable.

// Cast
#define DISPATCH_GLOBAL_OBJECT(type, object) ((OS_OBJECT_BRIDGE type)&(object))

DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_CONST DISPATCH_NOTHROW
dispatch_queue_main_t
dispatch_get_main_queue(void)
{
    return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q);
}
Copy the code

_dispatch_trace_queue_CREATE (create root queue/primary queue)

DISPATCH_ALWAYS_INLINE
static inline dispatch_queue_class_t
_dispatch_trace_queue_create(dispatch_queue_class_t dqu) {
    _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]);
    });

    return _dispatch_introspection_queue_create(dqu);
}
Copy the code

_dispatch_introspection_queue_create

Mainly to do some assignment operations on DQ.

dispatch_queue_class_t
_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);
    }
    // Request space
    dqic = _dispatch_calloc(1, sz);
    
    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);
    }
    
    // the do_finalizer function is assigned
    dq->do_finalizer = dqic;

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

    // hook
    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;
}
Copy the code

So far, we have roughly looked at the process of creating custom queues, root queues and main queues. There are too many macro conversions and nested functions in GCD, so we need a lot of time and energy to clear up the code. ⛽ ️ ⛽ ️

Refer to the link

Reference link :🔗

  • Libdispatch Apple source code
  • GCD source code analysis 1 — the opening chapter
  • Dig up the libDispatch source code
  • GCD source code analysis
  • A few things about GCD development
  • GCD in-depth understanding: Part ONE
  • Dispatch_once,
  • Transparent federation type
  • Perversion of libDispatch -dispatch_object_s
  • The basics of GCD
  • Swift multithreading from the source analysis – DispatchGroup
  • GCD source code analysis (a)
  • CD- source code analysis
  • GCD underlying source code analysis
  • (1) — GCD Queue
  • C/C ++: computes variable parameter macrosVA_ARGSNumber of arguments of