1/*
2 * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20
21// Contains introspection routines that only exist in the version of the
22// library with introspection support
23
24#if DISPATCH_INTROSPECTION
25
26#include "internal.h"
27#include "dispatch/introspection.h"
28#include "introspection_private.h"
29
30typedef struct dispatch_introspection_thread_s {
31	void *dit_isa;
32	TAILQ_ENTRY(dispatch_introspection_thread_s) dit_list;
33	pthread_t thread;
34	dispatch_queue_t *queue;
35} dispatch_introspection_thread_s;
36typedef struct dispatch_introspection_thread_s *dispatch_introspection_thread_t;
37
38static TAILQ_HEAD(, dispatch_introspection_thread_s)
39		_dispatch_introspection_threads =
40		TAILQ_HEAD_INITIALIZER(_dispatch_introspection_threads);
41static volatile OSSpinLock _dispatch_introspection_threads_lock;
42
43static void _dispatch_introspection_thread_remove(void *ctxt);
44
45static TAILQ_HEAD(, dispatch_queue_s) _dispatch_introspection_queues =
46		TAILQ_HEAD_INITIALIZER(_dispatch_introspection_queues);
47static volatile OSSpinLock _dispatch_introspection_queues_lock;
48
49static ptrdiff_t _dispatch_introspection_thread_queue_offset;
50
51#pragma mark -
52#pragma mark dispatch_introspection_init
53
54void
55_dispatch_introspection_init(void)
56{
57	TAILQ_INSERT_TAIL(&_dispatch_introspection_queues,
58			&_dispatch_main_q, diq_list);
59	TAILQ_INSERT_TAIL(&_dispatch_introspection_queues,
60			&_dispatch_mgr_q, diq_list);
61#if DISPATCH_ENABLE_PTHREAD_ROOT_QUEUES
62	TAILQ_INSERT_TAIL(&_dispatch_introspection_queues,
63			_dispatch_mgr_q.do_targetq, diq_list);
64#endif
65	for (size_t i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) {
66		TAILQ_INSERT_TAIL(&_dispatch_introspection_queues,
67				&_dispatch_root_queues[i], diq_list);
68	}
69
70	// Hack to determine queue TSD offset from start of pthread structure
71	uintptr_t thread = _dispatch_thread_self();
72	thread_identifier_info_data_t tiid;
73	mach_msg_type_number_t cnt = THREAD_IDENTIFIER_INFO_COUNT;
74	kern_return_t kr = thread_info(pthread_mach_thread_np((void*)thread),
75			THREAD_IDENTIFIER_INFO, (thread_info_t)&tiid, &cnt);
76	if (!dispatch_assume_zero(kr)) {
77		_dispatch_introspection_thread_queue_offset =
78				(void*)(uintptr_t)tiid.dispatch_qaddr - (void*)thread;
79	}
80	_dispatch_thread_key_create(&dispatch_introspection_key,
81			_dispatch_introspection_thread_remove);
82	_dispatch_introspection_thread_add(); // add main thread
83}
84
85const struct dispatch_introspection_versions_s
86dispatch_introspection_versions = {
87	.introspection_version = 1,
88	.hooks_version = 2,
89	.hooks_size = sizeof(dispatch_introspection_hooks_s),
90	.queue_item_version = 1,
91	.queue_item_size = sizeof(dispatch_introspection_queue_item_s),
92	.queue_block_version = 1,
93	.queue_block_size = sizeof(dispatch_introspection_queue_block_s),
94	.queue_function_version = 1,
95	.queue_function_size = sizeof(dispatch_introspection_queue_function_s),
96	.queue_thread_version = 1,
97	.queue_thread_size = sizeof(dispatch_introspection_queue_thread_s),
98	.object_version = 1,
99	.object_size = sizeof(dispatch_introspection_object_s),
100	.queue_version = 1,
101	.queue_size = sizeof(dispatch_introspection_queue_s),
102	.source_version = 1,
103	.source_size = sizeof(dispatch_introspection_source_s),
104};
105
106#pragma mark -
107#pragma mark dispatch_introspection_threads
108
109void
110_dispatch_introspection_thread_add(void)
111{
112	if (_dispatch_thread_getspecific(dispatch_introspection_key)) {
113		return;
114	}
115	uintptr_t thread = _dispatch_thread_self();
116	dispatch_introspection_thread_t dit = (void*)_dispatch_continuation_alloc();
117	dit->dit_isa = (void*)0x41;
118	dit->thread = (void*)thread;
119	dit->queue = !_dispatch_introspection_thread_queue_offset ? NULL :
120			(void*)thread + _dispatch_introspection_thread_queue_offset;
121	_dispatch_thread_setspecific(dispatch_introspection_key, dit);
122	OSSpinLockLock(&_dispatch_introspection_threads_lock);
123	TAILQ_INSERT_TAIL(&_dispatch_introspection_threads, dit, dit_list);
124	OSSpinLockUnlock(&_dispatch_introspection_threads_lock);
125}
126
127static void
128_dispatch_introspection_thread_remove(void *ctxt)
129{
130	dispatch_introspection_thread_t dit = ctxt;
131	OSSpinLockLock(&_dispatch_introspection_threads_lock);
132	TAILQ_REMOVE(&_dispatch_introspection_threads, dit, dit_list);
133	OSSpinLockUnlock(&_dispatch_introspection_threads_lock);
134	_dispatch_continuation_free((void*)dit);
135	_dispatch_thread_setspecific(dispatch_introspection_key, NULL);
136}
137
138#pragma mark -
139#pragma mark dispatch_introspection_info
140
141static inline
142dispatch_introspection_queue_function_s
143_dispatch_introspection_continuation_get_info(dispatch_queue_t dq,
144		dispatch_continuation_t dc, unsigned long *type)
145{
146	void *ctxt = dc->dc_ctxt;
147	dispatch_function_t func = dc->dc_func;
148	pthread_t waiter = NULL;
149	bool apply = false;
150	long flags = (long)dc->do_vtable;
151	if (flags & DISPATCH_OBJ_SYNC_SLOW_BIT) {
152		waiter = pthread_from_mach_thread_np((mach_port_t)dc->dc_data);
153		if (flags & DISPATCH_OBJ_BARRIER_BIT) {
154			dc = dc->dc_ctxt;
155			dq = dc->dc_data;
156		}
157		ctxt = dc->dc_ctxt;
158		func = dc->dc_func;
159	}
160	if (func == _dispatch_sync_recurse_invoke) {
161		dc = dc->dc_ctxt;
162		dq = dc->dc_data;
163		ctxt = dc->dc_ctxt;
164		func = dc->dc_func;
165	} else if (func == _dispatch_async_redirect_invoke) {
166		dq = dc->dc_data;
167		dc = dc->dc_other;
168		ctxt = dc->dc_ctxt;
169		func = dc->dc_func;
170		flags = (long)dc->do_vtable;
171	} else if (func == _dispatch_mach_barrier_invoke) {
172		dq = dq->do_targetq;
173		ctxt = dc->dc_data;
174		func = dc->dc_other;
175	} else if (func == _dispatch_apply_invoke ||
176			func == _dispatch_apply_redirect_invoke) {
177		dispatch_apply_t da = ctxt;
178		if (da->da_todo) {
179			dc = da->da_dc;
180			if (func == _dispatch_apply_redirect_invoke) {
181				dq = dc->dc_data;
182			}
183			ctxt = dc->dc_ctxt;
184			func = dc->dc_func;
185			apply = true;
186		}
187	}
188	if (func == _dispatch_call_block_and_release) {
189		*type = dispatch_introspection_queue_item_type_block;
190		func = _dispatch_Block_invoke(ctxt);
191	} else {
192		*type = dispatch_introspection_queue_item_type_function;
193	}
194	dispatch_introspection_queue_function_s diqf= {
195		.continuation = dc,
196		.target_queue = dq,
197		.context = ctxt,
198		.function = func,
199		.group = flags & DISPATCH_OBJ_GROUP_BIT ? dc->dc_data : NULL,
200		.waiter = waiter,
201		.barrier = flags & DISPATCH_OBJ_BARRIER_BIT,
202		.sync = flags & DISPATCH_OBJ_SYNC_SLOW_BIT,
203		.apply = apply,
204	};
205	return diqf;
206}
207
208static inline
209dispatch_introspection_object_s
210_dispatch_introspection_object_get_info(dispatch_object_t dou)
211{
212	dispatch_introspection_object_s dio = {
213		.object = dou._dc,
214		.target_queue = dou._do->do_targetq,
215		.type = (void*)dou._do->do_vtable,
216		.kind = dx_kind(dou._do),
217	};
218	return dio;
219}
220
221DISPATCH_USED inline
222dispatch_introspection_queue_s
223dispatch_introspection_queue_get_info(dispatch_queue_t dq)
224{
225	bool global = (dq->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) ||
226			(dq->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT);
227	uint16_t width = dq->dq_width;
228	if (width > 1 && width != DISPATCH_QUEUE_WIDTH_MAX) width /= 2;
229	dispatch_introspection_queue_s diq = {
230		.queue = dq,
231		.target_queue = dq->do_targetq,
232		.label = dq->dq_label,
233		.serialnum = dq->dq_serialnum,
234		.width = width,
235		.suspend_count = dq->do_suspend_cnt / 2,
236		.enqueued = (dq->do_suspend_cnt & 1) && !global,
237		.barrier = (dq->dq_running & 1) && !global,
238		.draining = (dq->dq_items_head == (void*)~0ul) ||
239				(!dq->dq_items_head && dq->dq_items_tail),
240		.global = global,
241		.main = (dq == &_dispatch_main_q),
242	};
243	return diq;
244}
245
246static inline
247dispatch_introspection_source_s
248_dispatch_introspection_source_get_info(dispatch_source_t ds)
249{
250	dispatch_source_refs_t dr = ds->ds_refs;
251	dispatch_continuation_t dc = dr->ds_handler[DS_EVENT_HANDLER];
252	void *ctxt = NULL;
253	dispatch_function_t handler = NULL;
254	bool hdlr_is_block = false;
255	if (dc) {
256		ctxt = dc->dc_ctxt;
257		handler = dc->dc_func;
258		hdlr_is_block = ((long)dc->do_vtable & DISPATCH_OBJ_BLOCK_RELEASE_BIT);
259	}
260	bool after = (handler == _dispatch_after_timer_callback);
261	if (after && !(ds->ds_atomic_flags & DSF_CANCELED)) {
262		dc = ctxt;
263		ctxt = dc->dc_ctxt;
264		handler = dc->dc_func;
265		hdlr_is_block = (handler == _dispatch_call_block_and_release);
266		if (hdlr_is_block) {
267			handler = _dispatch_Block_invoke(ctxt);
268		}
269	}
270	dispatch_introspection_source_s dis = {
271		.source = ds,
272		.target_queue = ds->do_targetq,
273		.type = ds->ds_dkev ? (unsigned long)ds->ds_dkev->dk_kevent.filter : 0,
274		.handle = ds->ds_dkev ? (unsigned long)ds->ds_dkev->dk_kevent.ident : 0,
275		.context = ctxt,
276		.handler = handler,
277		.suspend_count = ds->do_suspend_cnt / 2,
278		.enqueued = (ds->do_suspend_cnt & 1),
279		.handler_is_block = hdlr_is_block,
280		.timer = ds->ds_is_timer,
281		.after = after,
282	};
283	return dis;
284}
285
286static inline
287dispatch_introspection_queue_thread_s
288_dispatch_introspection_thread_get_info(dispatch_introspection_thread_t dit)
289{
290	dispatch_introspection_queue_thread_s diqt = {
291		.object = (void*)dit,
292		.thread = dit->thread,
293	};
294	if (dit->queue && *dit->queue) {
295		diqt.queue = dispatch_introspection_queue_get_info(*dit->queue);
296	}
297	return diqt;
298}
299
300DISPATCH_USED inline
301dispatch_introspection_queue_item_s
302dispatch_introspection_queue_item_get_info(dispatch_queue_t dq,
303		dispatch_continuation_t dc)
304{
305	dispatch_introspection_queue_item_s diqi;
306	if (DISPATCH_OBJ_IS_VTABLE(dc)) {
307		dispatch_object_t dou = (dispatch_object_t)dc;
308		unsigned long type = dx_type(dou._do);
309		unsigned long metatype = type & _DISPATCH_META_TYPE_MASK;
310		if (metatype == _DISPATCH_QUEUE_TYPE &&
311				type != DISPATCH_QUEUE_SPECIFIC_TYPE) {
312			diqi.type = dispatch_introspection_queue_item_type_queue;
313			diqi.queue = dispatch_introspection_queue_get_info(dou._dq);
314		} else if (metatype == _DISPATCH_SOURCE_TYPE) {
315			diqi.type = dispatch_introspection_queue_item_type_source;
316			diqi.source = _dispatch_introspection_source_get_info(dou._ds);
317		} else {
318			diqi.type = dispatch_introspection_queue_item_type_object;
319			diqi.object = _dispatch_introspection_object_get_info(dou._do);
320		}
321	} else {
322		diqi.function = _dispatch_introspection_continuation_get_info(dq, dc,
323				&diqi.type);
324	}
325	return diqi;
326}
327
328#pragma mark -
329#pragma mark dispatch_introspection_iterators
330
331DISPATCH_USED
332dispatch_queue_t
333dispatch_introspection_get_queues(dispatch_queue_t start, size_t count,
334		dispatch_introspection_queue_t queues)
335{
336	dispatch_queue_t next;
337	next = start ? start : TAILQ_FIRST(&_dispatch_introspection_queues);
338	while (count--) {
339		if (!next) {
340			queues->queue = NULL;
341			break;
342		}
343		*queues++ = dispatch_introspection_queue_get_info(next);
344		next = TAILQ_NEXT(next, diq_list);
345	}
346	return next;
347}
348
349DISPATCH_USED
350dispatch_continuation_t
351dispatch_introspection_get_queue_threads(dispatch_continuation_t start,
352		size_t count, dispatch_introspection_queue_thread_t threads)
353{
354	dispatch_introspection_thread_t next = start ? (void*)start :
355			TAILQ_FIRST(&_dispatch_introspection_threads);
356	while (count--) {
357		if (!next) {
358			threads->object = NULL;
359			break;
360		}
361		*threads++ = _dispatch_introspection_thread_get_info(next);
362		next = TAILQ_NEXT(next, dit_list);
363	}
364	return (void*)next;
365}
366
367DISPATCH_USED
368dispatch_continuation_t
369dispatch_introspection_queue_get_items(dispatch_queue_t dq,
370		dispatch_continuation_t start, size_t count,
371		dispatch_introspection_queue_item_t items)
372{
373	dispatch_continuation_t next = start ? start :
374			dq->dq_items_head == (void*)~0ul ? NULL : (void*)dq->dq_items_head;
375	while (count--) {
376		if (!next) {
377			items->type = dispatch_introspection_queue_item_type_none;
378			break;
379		}
380		*items++ = dispatch_introspection_queue_item_get_info(dq, next);
381		next = next->do_next;
382	}
383	return next;
384}
385
386#pragma mark -
387#pragma mark dispatch_introspection_hooks
388
389#define DISPATCH_INTROSPECTION_NO_HOOK ((void*)~0ul)
390
391dispatch_introspection_hooks_s _dispatch_introspection_hooks;
392dispatch_introspection_hooks_s _dispatch_introspection_hook_callouts;
393static const
394dispatch_introspection_hooks_s _dispatch_introspection_hook_callouts_enabled = {
395	.queue_create = DISPATCH_INTROSPECTION_NO_HOOK,
396	.queue_dispose = DISPATCH_INTROSPECTION_NO_HOOK,
397	.queue_item_enqueue = DISPATCH_INTROSPECTION_NO_HOOK,
398	.queue_item_dequeue = DISPATCH_INTROSPECTION_NO_HOOK,
399	.queue_item_complete = DISPATCH_INTROSPECTION_NO_HOOK,
400};
401
402#define DISPATCH_INTROSPECTION_HOOKS_COUNT (( \
403		sizeof(_dispatch_introspection_hook_callouts_enabled) - \
404		sizeof(_dispatch_introspection_hook_callouts_enabled._reserved)) / \
405		sizeof(dispatch_function_t))
406
407#define DISPATCH_INTROSPECTION_HOOK_ENABLED(h) \
408		(slowpath(_dispatch_introspection_hooks.h))
409
410#define DISPATCH_INTROSPECTION_HOOK_CALLOUT(h, ...) ({ \
411		typeof(_dispatch_introspection_hooks.h) _h; \
412		_h = _dispatch_introspection_hooks.h; \
413		if (slowpath((void*)(_h) != DISPATCH_INTROSPECTION_NO_HOOK)) { \
414			_h(__VA_ARGS__); \
415		} })
416
417#define DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(h) \
418		DISPATCH_EXPORT void _dispatch_introspection_hook_##h(void) \
419		asm("_dispatch_introspection_hook_" #h); \
420		void _dispatch_introspection_hook_##h(void) {}
421
422#define DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(h, ...)\
423		dispatch_introspection_hook_##h(__VA_ARGS__)
424
425DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_create);
426DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_destroy);
427DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_enqueue);
428DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_dequeue);
429DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_complete);
430DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_callout_begin);
431DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_callout_end);
432
433DISPATCH_USED
434void
435dispatch_introspection_hooks_install(dispatch_introspection_hooks_t hooks)
436{
437	dispatch_introspection_hooks_s old_hooks = _dispatch_introspection_hooks;
438	_dispatch_introspection_hooks = *hooks;
439	dispatch_function_t *e = (void*)&_dispatch_introspection_hook_callouts,
440			*h = (void*)&_dispatch_introspection_hooks, *oh = (void*)&old_hooks;
441	for (size_t i = 0; i < DISPATCH_INTROSPECTION_HOOKS_COUNT; i++) {
442		if (!h[i] && e[i]) {
443			h[i] = DISPATCH_INTROSPECTION_NO_HOOK;
444		}
445		if (oh[i] == DISPATCH_INTROSPECTION_NO_HOOK) {
446			oh[i] = NULL;
447		}
448	}
449	*hooks = old_hooks;
450}
451
452DISPATCH_USED
453void
454dispatch_introspection_hook_callouts_enable(
455		dispatch_introspection_hooks_t enable)
456{
457	_dispatch_introspection_hook_callouts = enable ? *enable :
458			_dispatch_introspection_hook_callouts_enabled;
459	dispatch_function_t *e = (void*)&_dispatch_introspection_hook_callouts,
460			*h = (void*)&_dispatch_introspection_hooks;
461	for (size_t i = 0; i < DISPATCH_INTROSPECTION_HOOKS_COUNT; i++) {
462		if (e[i] && !h[i]) {
463			h[i] = DISPATCH_INTROSPECTION_NO_HOOK;
464		} else if (!e[i] && h[i] == DISPATCH_INTROSPECTION_NO_HOOK) {
465			h[i] = NULL;
466		}
467	}
468}
469
470DISPATCH_NOINLINE
471void
472dispatch_introspection_hook_callout_queue_create(
473		dispatch_introspection_queue_t queue_info)
474{
475	DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_create, queue_info);
476}
477
478DISPATCH_NOINLINE
479static void
480_dispatch_introspection_queue_create_hook(dispatch_queue_t dq)
481{
482	dispatch_introspection_queue_s diq;
483	diq = dispatch_introspection_queue_get_info(dq);
484	dispatch_introspection_hook_callout_queue_create(&diq);
485}
486
487dispatch_queue_t
488_dispatch_introspection_queue_create(dispatch_queue_t dq)
489{
490	OSSpinLockLock(&_dispatch_introspection_queues_lock);
491	TAILQ_INSERT_TAIL(&_dispatch_introspection_queues, dq, diq_list);
492	OSSpinLockUnlock(&_dispatch_introspection_queues_lock);
493
494	DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_create, dq);
495	if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_create)) {
496		_dispatch_introspection_queue_create_hook(dq);
497	}
498	return dq;
499}
500
501DISPATCH_NOINLINE
502void
503dispatch_introspection_hook_callout_queue_dispose(
504		dispatch_introspection_queue_t queue_info)
505{
506	DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_dispose, queue_info);
507}
508
509DISPATCH_NOINLINE
510static void
511_dispatch_introspection_queue_dispose_hook(dispatch_queue_t dq)
512{
513	dispatch_introspection_queue_s diq;
514	diq = dispatch_introspection_queue_get_info(dq);
515	dispatch_introspection_hook_callout_queue_dispose(&diq);
516}
517
518void
519_dispatch_introspection_queue_dispose(dispatch_queue_t dq)
520{
521	DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_destroy, dq);
522	if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_dispose)) {
523		_dispatch_introspection_queue_dispose_hook(dq);
524	}
525
526	OSSpinLockLock(&_dispatch_introspection_queues_lock);
527	TAILQ_REMOVE(&_dispatch_introspection_queues, dq, diq_list);
528	OSSpinLockUnlock(&_dispatch_introspection_queues_lock);
529}
530
531DISPATCH_NOINLINE
532void
533dispatch_introspection_hook_callout_queue_item_enqueue(dispatch_queue_t queue,
534		dispatch_introspection_queue_item_t item)
535{
536	DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_item_enqueue, queue, item);
537}
538
539DISPATCH_NOINLINE
540static void
541_dispatch_introspection_queue_item_enqueue_hook(dispatch_queue_t dq,
542		dispatch_object_t dou)
543{
544	dispatch_introspection_queue_item_s diqi;
545	diqi = dispatch_introspection_queue_item_get_info(dq, dou._dc);
546	dispatch_introspection_hook_callout_queue_item_enqueue(dq, &diqi);
547}
548
549void
550_dispatch_introspection_queue_item_enqueue(dispatch_queue_t dq,
551		dispatch_object_t dou)
552{
553	DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(
554			queue_item_enqueue, dq, dou);
555	if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_item_enqueue)) {
556		_dispatch_introspection_queue_item_enqueue_hook(dq, dou);
557	}
558}
559
560DISPATCH_NOINLINE
561void
562dispatch_introspection_hook_callout_queue_item_dequeue(dispatch_queue_t queue,
563		dispatch_introspection_queue_item_t item)
564{
565	DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_item_dequeue, queue, item);
566}
567
568DISPATCH_NOINLINE
569static void
570_dispatch_introspection_queue_item_dequeue_hook(dispatch_queue_t dq,
571		dispatch_object_t dou)
572{
573	dispatch_introspection_queue_item_s diqi;
574	diqi = dispatch_introspection_queue_item_get_info(dq, dou._dc);
575	dispatch_introspection_hook_callout_queue_item_dequeue(dq, &diqi);
576}
577
578void
579_dispatch_introspection_queue_item_dequeue(dispatch_queue_t dq,
580		dispatch_object_t dou)
581{
582	DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(
583			queue_item_dequeue, dq, dou);
584	if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_item_dequeue)) {
585		_dispatch_introspection_queue_item_dequeue_hook(dq, dou);
586	}
587}
588
589DISPATCH_NOINLINE
590void
591dispatch_introspection_hook_callout_queue_item_complete(
592		dispatch_continuation_t object)
593{
594	DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_item_complete, object);
595}
596
597DISPATCH_NOINLINE
598static void
599_dispatch_introspection_queue_item_complete_hook(dispatch_object_t dou)
600{
601	dispatch_introspection_hook_callout_queue_item_complete(dou._dc);
602}
603
604void
605_dispatch_introspection_queue_item_complete(dispatch_object_t dou)
606{
607	DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_item_complete, dou);
608	if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_item_complete)) {
609		_dispatch_introspection_queue_item_complete_hook(dou);
610	}
611}
612
613void
614_dispatch_introspection_callout_entry(void *ctxt, dispatch_function_t f) {
615	dispatch_queue_t dq = _dispatch_queue_get_current();
616	DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(
617			queue_callout_begin, dq, ctxt, f);
618}
619
620void
621_dispatch_introspection_callout_return(void *ctxt, dispatch_function_t f) {
622	dispatch_queue_t dq = _dispatch_queue_get_current();
623	DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(
624			queue_callout_end, dq, ctxt, f);
625}
626
627#endif // DISPATCH_INTROSPECTION
628