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 adispatch_function_tStructure 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_inlineinsideifToo 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_invokeExecute, then continue the search

  • _dispatch_sync_function_invoke

We need to find out who used itCTXT, funcTwo parameters, found to be this code_dispatch_client_callout(ctxt, func)So now go and search

  • _dispatch_client_callout

Pass yourself into return, andblockThe 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 breakpointin blockFunction execution body, and then passbtThe print callsThe stackAs can be seen clearly in the_dispatch_client_calloutAfter the function is executed, it will be executedblockFunction body.

This wave of operation, it’s very detailed,666Who else can GCDSource exploration so fresh and free from vulgarity,45 degreesLook 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 sameworkWhere 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 workProcessing, return funcAgain, the incoming_dispatch_continuation_init_fTo 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_asyncInside 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 onCPUThe 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_pushWhat 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_inlineTo continue the source process of the tracker:

  • _dispatch_root_queue_push_inline

Continue to follow_dispatch_root_queue_pokeWhere 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. In6295Lines of code,_dispatch_root_queues_initThe 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 poolProcessing,The work queueWhich 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

throughbtPrint the program’s run stack information to verify that the asynchronous function’s final task is passed_dispatch_worker_thread2Of 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

  • GCDThe source code is difficult to understand, can be inferred from some return values and key information
  • Through theSymbol breakpointTo trace the code call logic
  • throughbtPrint 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 🌹