This is the 14th day of my participation in the August More Text Challenge. For details, see: August More Text Challenge
review
The previous post explored the underlying source code of GCD’s different queues, so this post will explore the underlying source code of GCD’s functions.
1. Sync Analysis of the synchronization function
We all know that the underlying GCD is written in C, encapsulating the block function to perform the added task, so how is the underlying block encapsulated?
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"GCD function analysis");
});
Copy the code
Search for dispatch_sync in the source code
We’re looking at the block which is the second parameter work, so we can just see where the work goes, we can just go to the last line of code, okay
_dispatch_sync_f(dq, work, _dispatch_Block_invoke(work), dc_flags);
Copy the code
Search _dispatch_Block_invoke
- From a
dispatch_function_t
Structure of typeBlock_layout *
theinvoke
So now look at where the block wrapper function _dispatch_sync_f is called, and search to find a middle layer wrapper, as follows:
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
And then search for _dispatch_sync_f_inline
_dispatch_sync_f_inline
insideif
Too much judgment, do not know which to see, how to change? This source code cannot be compiled and run!Don’t panic, pretty boy! We can put different symbolic breakpoints where the function is called and see which 😊 to go
- Lower sign breakpoint
- Let’s run it and see where we go
And you can see clearly by the notation breakpoint, here we go_dispatch_sync_f_slow
, which is the code in the figure below:
And then I’m going to go ahead and search for _dispatch_sync_f_slow in the source code
_dispatch_sync_f_slow
I don’t know where I’m going, so I’m going to break again, and I’m going, okay_dispatch_sync_function_invoke
Execute, then continue the search
_dispatch_sync_function_invoke
We need to find out who used itCTXT, func
Two parameters, found to be this code_dispatch_client_callout(ctxt, func)
So now go and search
_dispatch_client_callout
Pass yourself into return, andblock
The bottom layer is the same way to call, which is also illustrated in the call_dispatch_client_callout
, the asynchronous function executesblock
, verify as follows:throughThe breakpoint
in block
Function execution body, and then passbt
The print callsThe stack
As can be seen clearly in the_dispatch_client_callout
After the function is executed, it will be executedblock
Function body.
This wave of operation, it’s very detailed,666
Who else can GCD
Source exploration so fresh and free from vulgarity,45 degrees
Look up at the sky, my damn charm that can’t be placed!
2. Analysis of asyCN asynchronous functions
Same thing with asynchrony, so let’s search for a wave
dispatch_async
Just the samework
Where is it used, from which to locate qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags)
This code, search again_dispatch_continuation_init
_dispatch_continuation_init
- You can find the following code, right
work
Processing, returnfunc
Again, the incoming_dispatch_continuation_init_f
To continue searching_dispatch_continuation_init_f
In the previous synchronization function, it returned directly, but here it’s wrapped
dc->dc_func = f;
dc->dc_ctxt = ctxt;
Copy the code
Once the packaging is done, there is also a matter of priority
return _dispatch_continuation_priority_set(dc, dqu, pp, flags);
Copy the code
_dispatch_continuation_priority_set
Let’s go back todispatch_async
Inside the function
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
So why would Apple want to do this in asynchronous?
- An asynchronous function represents an asynchronous call
- Asynchrony is an unordered call
- Asynchronous callbacks are also asynchronous and are based on
CPU
The scheduler calls back asynchronously at the appropriate time - It’s also part of functional programming, which is to call back when needed
But the most important line of code here is the last one
_dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
Copy the code
_dispatch_continuation_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
This is where you get lost. Thisdx_push
What is it? I searched and found the following macro definition
#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
Copy the code
In the macro definition dx_vtable is not what we need to look at, let’s go to dq_push, the third parameter z is qos, see where dq_push is used
The underlying layer provides different entry points for different types of queues, such as a global concurrent queue that calls the _dispatch_root_queue_push method. Using this as entry, search globally for the implementation of the _dispatch_root_queue_push method:
The previous code just does some judgment wrapping, and eventually comes down to the last line of code_dispatch_root_queue_push_inline
To continue the source process of the tracker:
_dispatch_root_queue_push_inline
Continue to follow_dispatch_root_queue_poke
Where is it called?
_dispatch_root_queue_poke
_dispatch_root_queue_poke_slow
All this code, I went through it and I didn’t find any key information. In6295
Lines of code,_dispatch_root_queues_init
The method contains key information
_dispatch_root_queues_init(void)
{
dispatch_once_f(&_dispatch_root_queues_pred, NULL,
_dispatch_root_queues_init_once);
}
Copy the code
Here’s a dispatch_once_f singleton with a parameter of _dispatch_root_queues_init_once, so continue searching
_dispatch_root_queues_init_once
In this methodInitialization of the thread pool
Processing,The work queue
Which explains why _dispatch_root_queues_init_once is a singleton. Singletons can avoid repeated initialization.
The execution function is set to _dispatch_worker_thread2 as follows:
cfg.workq_cb = _dispatch_worker_thread2;
r = pthread_workqueue_setup(&cfg, sizeof(cfg));
Copy the code
throughbt
Print the program’s run stack information to verify that the asynchronous function’s final task is passed_dispatch_worker_thread2
Of the callThe console print stack information, and the result of our exploration and reasoning is, a module module a everything 😁, ask pretty boy you accept! Ha ha 😁!
3. Summary
GCD
The source code is difficult to understand, can be inferred from some return values and key information- Through the
Symbol breakpoint
To trace the code call logic - through
bt
Print the results of the stack validation exploration
More to come
🌹 just like it 👍🌹
🌹 feel have harvest, can come a wave, collect + concern, comment + forward, lest you can’t find me next 😁🌹
🌹 welcome everyone to leave a message to exchange, criticize and correct, learn from each other 😁, improve self 🌹