This is the fourth day of my participation in the August More text Challenge. For details, see:August is more challenging

_dispatch_object_alloc

In the underlying source code of CREat, this line of code is used to request and create memory:

dispatch_lane_t dq = _dispatch_object_alloc(vtable,
			sizeof(struct dispatch_lane_s));
Copy the code

What does _dispatch_object_alloc do? Take a look from the source code

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

_dispatch_object_alloc -> _os_object_alloc_realized

_os_object_t
_os_object_alloc_realized(const void *cls, size_t size)
{
	dispatch_assert(size >= sizeof(struct _os_object_s));
	return _os_objc_alloc(cls, size);
}
Copy the code

_dispatch_object_alloc -> _OS_object_alloc_realized -> _OS_OBJc_alloc

GCD Underlying source inheritance chain

Before we dive into the above issues, let’s move on to the source code for global queues

  • The main queue type is:dispatch_queue_main_t
  • The types of global queues are:dispatch_queue_global_s

We know from the code that both the global queue and the main queue are DISPATCH_DECL(dispatch_queue) that can be received using dispatch_queue_t;

#define DISPATCH_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, dispatch_object)

OS_OBJECT_DECL_SUBCLASS(dispatch_queue, dispatch_object

#define OS_OBJECT_DECL_SUBCLASS(name, super) \
		OS_OBJECT_DECL_IMPL(name, NSObject, <OS_OBJECT_CLASS(super)>)
#define OS_OBJECT_DECL_SUBCLASS(name, super)  DISPATCH_DECL(name)
Copy the code

After a global search, we found the macro definitions in both places. However, we found that the macro definition in the second place is exactly where we came in, so there is no point in studying it this time. But the first one is the same thing as

OS_OBJECT_DECL_IMPL(dispatch_queue, NSObject, <OS_OBJECT_CLASS(dispatch_object)>)

#define OS_OBJECT_DECL_IMPL(name, adhere, ...) \
		OS_OBJECT_DECL_PROTOCOL(name, __VA_ARGS__) \
		typedef adhere<OS_OBJECT_CLASS(name)> \
				* OS_OBJC_INDEPENDENT_CLASS name##_t
Copy the code

Is equivalent to:

OS_OBJECT_DECL_PROTOCOL(dispatch_queue, dispatch_object) typedef NSObject<OS_dispatch_queue)> * OS_OBJC_INDEPENDENT_CLASS dispatch_queue_t

#define OS_OBJECT_DECL_PROTOCOL(name, ...) \
		@protocol OS_OBJECT_CLASS(name) __VA_ARGS__ \
		@end
Copy the code

Is equivalent to:

@protocol OS_OBJECT_CLASS(dispatch_queue) dispatch_object @end

#define OS_OBJECT_CLASS(name) OS_##name
Copy the code

Is equivalent to:

OS_dispatch_queue

Or here’s an even simpler one:

#define DISPATCH_DECL(name) \
		typedef struct name##_s : public dispatch_object_s {} *name##_t
typedef struct dispatch_queue_s: public dispatch_object_s {} *dispatch_queue_t
Copy the code

So we can conclude that the underlying structure of dispatch_queue_t is a structure that dispatch_queue_s inherits from dispatch_object_s, just like class, we know the underlying inheritance of class

class -> object_class -> object_object

dispatch_queue_t -> dispatch_queue_s -> dispatch_object_s -> _os_object_s ->dispatch_object_t

dispatch_queue_s

Just like the research class, it is conceivable that we will focus on dispatch_queue_s

struct dispatch_queue_s {
	DISPATCH_QUEUE_CLASS_HEADER(queue, void *__dq_opaque1);
	/* 32bit hole on LP64 */
} DISPATCH_ATOMIC64_ALIGN;

Copy the code

Continue searching for DISPATCH_QUEUE_CLASS_HEADER

#define _DISPATCH_QUEUE_CLASS_HEADER(x, __pointer_sized_field__) \
	DISPATCH_OBJECT_HEADER(x); \
	DISPATCH_UNION_LE(uint64_t volatile dq_state, \
			dispatch_lock dq_state_lock, \
			uint32_t dq_state_bits \
	); \
	__pointer_sized_field__
Copy the code

DISPATCH_OBJECT_HEADER(queue);

#define DISPATCH_OBJECT_HEADER(x) \
	struct dispatch_object_s _as_do[0]; \
	_DISPATCH_OBJECT_HEADER(x)
Copy the code

It ends up pointing to dispatch_object_s, which just validates the inheritance process above. I’m going to continue searching for _DISPATCH_OBJECT_HEADER

#define_DISPATCH_OBJECT_HEADER(x) \ struct _os_object_s _as_os_obj[0]; \ OS_OBJECT_STRUCT_HEADER(dispatch_##x); \ struct dispatch_##x##_s *volatile do_next; \ struct dispatch_queue_s *do_targetq; \ void *do_ctxt; \ union { \ dispatch_function_t DISPATCH_FUNCTION_POINTER do_finalizer; \ void *do_introspection_ctxt; The \}
Copy the code

It turns out that there’s a layer behind dispatch_object_s, and it inherits from _OS_object_s, so we’re going to search for the macro definition of OS_OBJECT_STRUCT_HEADER, and we’re going to unpack all of that and finally get the internal structure of dispatch_queue_s, There are three member variables

#define OS_OBJECT_STRUCT_HEADER(x) \
	_OS_OBJECT_HEADER(\
	const void *_objc_isa, \
	do_ref_cnt, \
	do_xref_cnt); \
	const struct x##_vtable_s *do_vtable
#else
Copy the code

GCD task execution stack-sync

   dispatch_sync(dispatch_get_global_queue(0.0), ^ {NSLog(@"Synchronization function analysis");
    });
Copy the code

So let’s look at the timing of this NSLog, and again, look at the source code

void
dispatch_sync(dispatch_queue_t dq, dispatch_block_t work)
{
	uintptr_t dc_flags = DC_FLAG_BLOCK;
	if (unlikely(_dispatch_block_has_private_data(work))) {
		return _dispatch_sync_block_with_privdata(dq, work, dc_flags);
	}
	_dispatch_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags);
}
Copy the code

We only need to pay attention to the flow of work: _dispatch_sync_f pays attention to the second and third parameters: CTXT, func

static void
_dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func,
		uintptr_t dc_flags)
{
	_dispatch_sync_f_inline(dq, ctxt, func, dc_flags);
}
Copy the code

Continue searching for _dispatch_sync_f_inline

_dispatch_sync_f_inline(dispatch_queue_t dq, void *ctxt,
		dispatch_function_t func, uintptr_t dc_flags)
{
	if (likely(dq->dq_width == 1)) {
	 // There are calls to CTXT and func
		return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags);
	}

	if (unlikely(dx_metatype(dq) ! = _DISPATCH_LANE_TYPE)) {DISPATCH_CLIENT_CRASH(0."Queue type doesn't support dispatch_sync");
	}

	dispatch_lane_t dl = upcast(dq)._dl;
	// Global concurrent queues and queues bound to non-dispatch threads
	// always fall into the slow case, see DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE
	if (unlikely(! _dispatch_queue_try_reserve_sync_width(dl))) {// // here's the call to CTXT and func
		return _dispatch_sync_f_slow(dl, ctxt, func, 0, dl, dc_flags);
	}

	if (unlikely(dq->do_targetq->do_targetq)) {
        // // here's the call to CTXT and func
		return _dispatch_sync_recurse(dl, ctxt, func, dc_flags);
	}
	_dispatch_introspection_sync_begin(dl);
    // // here's the call to CTXT and func
	_dispatch_sync_invoke_and_complete(dl, ctxt, func DISPATCH_TRACE_ARG(
			_dispatch_trace_item_sync_push_pop(dq, ctxt, func, dc_flags)));
}
Copy the code

_dispatch_barrier_sync_f -> _dispatch_sync_f_slow -> _dispatch_sync_function_invoke -> _dispatch_sync_function_invoke_inline -> _dispatch_client_callout

static inline void
_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
	return f(ctxt);
}
Copy the code

To verify this, you can simply look at the project stack or just bittorrent on the console.

GCD Task execution stack – asynchronous

   dispatch_async(dispatch_get_global_queue(0.0), ^ {NSLog(@"Analysis of asynchronous functions");
    });
Copy the code

Let’s go to the source code to analyze the synchronization function:

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;

	qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
	_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}
Copy the code

Continuation_async () async () async () async () async () async () async () async () async () async () async () async () async () async () async (

static inline void
_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);
}
Copy the code

Dx_push is actually a macro here

#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
Copy the code

The above qos is in the third parameter, so we will focus on dq_push(x, y, z).

Go directly to the global concurrent queue here

.dq_push        = _dispatch_root_queue_push,
Copy the code

_dispatch_continuation_async -> dq_push -> _dispatch_root_queue_push -> _dispatch_root_queue_push_inline -> _dispatch_root_queue_poke -> _dispatch_root_queue_poke_slow -> _dispatch_root_queues_init ->_dispatch_root_queues_init_once

static inline void
_dispatch_root_queues_init(void)
{
	dispatch_once_f(&_dispatch_root_queues_pred, NULL,
			_dispatch_root_queues_init_once);
}
Copy the code

Dispatch_once_f _dispatch_root_queues_init_once -> _dispatch_worker_thread2 -> _dispatch_root_queue_drain -> _dispatch_continuation_pop_inline -> _dispatch_continuation_invoke_inline -> _dispatch_client_callout

_dispatch_client_callout(void *ctxt, dispatch_function_t f)
{
	return f(ctxt); // is a call execution
}
Copy the code

This asynchronous function process is relatively complex and lengthy