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