* loaded by running a process with the environment variable
  * DYLD_LIBRARY_PATH=/usr/lib/system/introspection
  *
- * NOTE: these functions are _not_ exported from the shared library, they are
- * only intended to be called from a debugger context while the rest of the
- * process is suspended.
+ * NOTE: most of these functions are _not_ exported from the shared library,
+ * the unexported functions are intended to only be called from a debugger
+ * context while the rest of the process is suspended.
  */
 
 #ifndef __BEGIN_DECLS
 typedef struct dispatch_source_s *dispatch_source_t;
 typedef struct dispatch_group_s *dispatch_group_t;
 typedef struct dispatch_object_s *dispatch_object_t;
+#ifndef __OSX_AVAILABLE_STARTING
+#define __OSX_AVAILABLE_STARTING(x,y)
 #endif
+#ifndef DISPATCH_EXPORT
+#define DISPATCH_EXPORT extern
+#endif
+#endif // __DISPATCH_INDIRECT__
 
 /*!
  * @typedef dispatch_introspection_versions_s
  * @field introspection_version
  * Version of overall dispatch_introspection SPI.
  *
+ * @field hooks_version
+ * Version of dispatch_introspection_hooks_s structure.
+ * Version 2 adds the queue_item_complete member.
+ *
+ * @field hooks_size
+ * Size of dispatch_introspection_hooks_s structure.
+ *
+ * @field queue_item_version
+ * Version of dispatch_introspection_queue_item_s structure.
+ *
+ * @field queue_item_size
+ * Size of dispatch_introspection_queue_item_s structure.
+ *
+ * @field queue_block_version
+ * Version of dispatch_introspection_queue_block_s structure.
+ *
+ * @field queue_block_size
+ * Size of dispatch_introspection_queue_block_s structure.
+ *
+ * @field queue_function_version
+ * Version of dispatch_introspection_queue_function_s structure.
+ *
+ * @field queue_function_size
+ * Size of dispatch_introspection_queue_function_s structure.
+ *
+ * @field queue_thread_version
+ * Version of dispatch_introspection_queue_thread_s structure.
+ *
+ * @field queue_thread_size
+ * Size of dispatch_introspection_queue_thread_s structure.
+ *
+ * @field object_version
+ * Version of dispatch_introspection_object_s structure.
+ *
+ * @field object_size
+ * Size of dispatch_introspection_object_s structure.
+ *
  * @field queue_version
  * Version of dispatch_introspection_queue_s structure.
  *
 typedef void (*dispatch_introspection_hook_queue_item_dequeue_t)(
                dispatch_queue_t queue, dispatch_introspection_queue_item_t item);
 
+/*!
+ * @typedef dispatch_introspection_hook_queue_item_complete_t
+ *
+ * @abstract
+ * A function pointer called when an item previously dequeued from a dispatch
+ * queue has completed processing.
+ *
+ * @discussion
+ * The object pointer value passed to this function pointer must be treated as a
+ * value only. It is intended solely for matching up with an earlier call to a
+ * dequeue hook function pointer by comparing to the first member of the
+ * dispatch_introspection_queue_item_t structure. It must NOT be dereferenced
+ * or e.g. passed to dispatch_introspection_queue_item_get_info(), the memory
+ * that was backing it may have been reused at the time this hook is called.
+ *
+ * @param object
+ * Opaque dentifier for completed item. Must NOT be dereferenced.
+ */
+typedef void (*dispatch_introspection_hook_queue_item_complete_t)(
+               dispatch_continuation_t object);
+
 /*!
  * @typedef dispatch_introspection_hooks_s
  *
        dispatch_introspection_hook_queue_dispose_t queue_dispose;
        dispatch_introspection_hook_queue_item_enqueue_t queue_item_enqueue;
        dispatch_introspection_hook_queue_item_dequeue_t queue_item_dequeue;
-       void *_reserved[6];
+       dispatch_introspection_hook_queue_item_complete_t queue_item_complete;
+       void *_reserved[5];
 } dispatch_introspection_hooks_s;
 typedef dispatch_introspection_hooks_s *dispatch_introspection_hooks_t;
 
  *
  * @discussion
  * Installing hook functions must take place from a debugger context (while the
- * rest of the process is suspended).
+ * rest of the process is suspended) or early enough in the process lifecycle
+ * that the process is still single-threaded.
  *
  * The caller is responsible for implementing chaining to the hooks that were
  * previously installed (if any).
  * hooks on output.
  */
 
-extern void
+__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0)
+DISPATCH_EXPORT void
 dispatch_introspection_hooks_install(dispatch_introspection_hooks_t hooks);
 
 /*!
 dispatch_introspection_hook_callout_queue_item_dequeue(
                dispatch_queue_t queue, dispatch_introspection_queue_item_t item);
 
+/*!
+ * @function dispatch_introspection_hook_callout_queue_item_complete
+ *
+ * @abstract
+ * Callout to queue item complete hook that a debugger can break on.
+ */
+
+extern void
+dispatch_introspection_hook_callout_queue_item_complete(
+               dispatch_continuation_t object);
+
+/*!
+ * @function dispatch_introspection_hook_queue_item_complete
+ *
+ * @abstract
+ * Interposable hook function called when an item previously dequeued from a
+ * dispatch queue has completed processing.
+ *
+ * @discussion
+ * The object pointer value passed to this function must be treated as a value
+ * only. It is intended solely for matching up with an earlier call to a
+ * dequeue hook function and must NOT be dereferenced.
+ *
+ * @param item
+ * Opaque dentifier for completed item. Must NOT be dereferenced.
+ */
+
+DISPATCH_EXPORT
+void
+dispatch_introspection_hook_queue_item_complete(dispatch_object_t item);
+
 __END_DECLS
 
 #endif
 
 const struct dispatch_introspection_versions_s
 dispatch_introspection_versions = {
        .introspection_version = 1,
-       .hooks_version = 1,
+       .hooks_version = 2,
        .hooks_size = sizeof(dispatch_introspection_hooks_s),
        .queue_item_version = 1,
        .queue_item_size = sizeof(dispatch_introspection_queue_item_s),
        .queue_dispose = DISPATCH_INTROSPECTION_NO_HOOK,
        .queue_item_enqueue = DISPATCH_INTROSPECTION_NO_HOOK,
        .queue_item_dequeue = DISPATCH_INTROSPECTION_NO_HOOK,
+       .queue_item_complete = DISPATCH_INTROSPECTION_NO_HOOK,
 };
 
 #define DISPATCH_INTROSPECTION_HOOKS_COUNT (( \
 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_destroy);
 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_enqueue);
 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_dequeue);
+DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_item_complete);
 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_callout_begin);
 DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK(queue_callout_end);
 
 {
        dispatch_introspection_queue_item_s diqi;
        diqi = dispatch_introspection_queue_item_get_info(dq, dou._dc);
-       dispatch_introspection_hook_callout_queue_item_enqueue(dq, &diqi);
+       dispatch_introspection_hook_callout_queue_item_dequeue(dq, &diqi);
 }
 
 void
        }
 }
 
+DISPATCH_NOINLINE
+void
+dispatch_introspection_hook_callout_queue_item_complete(
+               dispatch_continuation_t object)
+{
+       DISPATCH_INTROSPECTION_HOOK_CALLOUT(queue_item_complete, object);
+}
+
+DISPATCH_NOINLINE
+static void
+_dispatch_introspection_queue_item_complete_hook(dispatch_object_t dou)
+{
+       dispatch_introspection_hook_callout_queue_item_complete(dou._dc);
+}
+
+void
+_dispatch_introspection_queue_item_complete(dispatch_object_t dou)
+{
+       DISPATCH_INTROSPECTION_INTERPOSABLE_HOOK_CALLOUT(queue_item_complete, dou);
+       if (DISPATCH_INTROSPECTION_HOOK_ENABLED(queue_item_complete)) {
+               _dispatch_introspection_queue_item_complete_hook(dou);
+       }
+}
+
 void
 _dispatch_introspection_callout_entry(void *ctxt, dispatch_function_t f) {
        dispatch_queue_t dq = _dispatch_queue_get_current();
 
                dispatch_object_t dou);
 void _dispatch_introspection_queue_item_dequeue(dispatch_queue_t dq,
                dispatch_object_t dou);
+void _dispatch_introspection_queue_item_complete(dispatch_object_t dou);
 void _dispatch_introspection_callout_entry(void *ctxt, dispatch_function_t f);
 void _dispatch_introspection_callout_return(void *ctxt, dispatch_function_t f);
 
 _dispatch_introspection_queue_pop(dispatch_queue_t dq DISPATCH_UNUSED,
                dispatch_object_t dou DISPATCH_UNUSED) {}
 
+DISPATCH_ALWAYS_INLINE
+static inline void
+_dispatch_introspection_queue_item_complete(
+               dispatch_object_t dou DISPATCH_UNUSED) {}
+
 DISPATCH_ALWAYS_INLINE
 static inline void
 _dispatch_introspection_callout_entry(void *ctxt DISPATCH_UNUSED,
 
 {
        dispatch_continuation_t dc = dou._dc;
 
-       _dispatch_trace_continuation_pop(dq, dou);
        (void)dispatch_atomic_add2o(dq, dq_running, 2, acquire);
        if (!DISPATCH_OBJ_IS_VTABLE(dc) &&
                        (long)dc->do_vtable & DISPATCH_OBJ_SYNC_SLOW_BIT) {
+               _dispatch_trace_continuation_pop(dq, dou);
                _dispatch_thread_semaphore_signal(
                                (_dispatch_thread_semaphore_t)dc->dc_other);
+               _dispatch_introspection_queue_item_complete(dou);
        } else {
                _dispatch_async_f_redirect(dq, dc);
        }
+       _dispatch_perfmon_workitem_inc();
 }
 
 DISPATCH_ALWAYS_INLINE_NDEBUG
                dispatch_group_leave(dg);
                _dispatch_release(dg);
        }
+        _dispatch_introspection_queue_item_complete(dou);
        if (slowpath(dc1)) {
                _dispatch_continuation_free_to_cache_limit(dc1);
        }
                // returns
                (void)dispatch_atomic_add2o(dq, dq_running, 2, relaxed);
        }
+       _dispatch_introspection_queue_item_complete(dou);
        return sema ? sema : MACH_PORT_DEAD;
 }
 
 #if DISPATCH_COCOA_COMPAT
        if (slowpath(dq->dq_is_thread_bound)) {
                // The queue is bound to a non-dispatch thread (e.g. main thread)
-               dc->dc_func(dc->dc_ctxt);
+               _dispatch_client_callout(dc->dc_ctxt, dc->dc_func);
                dispatch_atomic_store2o(dc, dc_func, NULL, release);
                _dispatch_thread_semaphore_signal(sema); // release
                return;
                return _dispatch_sync_f_slow(dq, ctxt, func, false);
        }
        uint32_t running = dispatch_atomic_add2o(dq, dq_running, 2, relaxed);
-       if (slowpath(running & 1)) {
+       // re-check suspension after barrier check <rdar://problem/15242126>
+       if (slowpath(running & 1) || slowpath(DISPATCH_OBJECT_SUSPENDED(dq))) {
                running = dispatch_atomic_sub2o(dq, dq_running, 2, relaxed);
                return _dispatch_sync_f_slow(dq, ctxt, func, running == 0);
        }
 
                if (sema) {
                        _dispatch_thread_semaphore_signal(sema);
                } else if (tq) {
+                       _dispatch_introspection_queue_item_complete(dq);
                        return _dispatch_queue_push(tq, dq);
                }
        }
                        _dispatch_wakeup(dq); // verify that the queue is idle
                }
        }
+       _dispatch_introspection_queue_item_complete(dq);
        _dispatch_release(dq); // added when the queue is put on the list
 }
 
 
 #if USE_APPLE_TSD_OPTIMIZATIONS && HAVE_PTHREAD_KEY_INIT_NP && \
        !defined(DISPATCH_USE_DIRECT_TSD)
 #define DISPATCH_USE_DIRECT_TSD 1
+#if __has_include(<os/tsd.h>)
+#include <os/tsd.h>
+#endif
 #endif
 
 #if DISPATCH_USE_DIRECT_TSD
 static inline unsigned int
 _dispatch_cpu_number(void)
 {
+#if TARGET_IPHONE_SIMULATOR && IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED < 1090
+       return 0;
+#elif __has_include(<os/tsd.h>)
+       return _os_cpu_number();
+#elif defined(__x86_64__) || defined(__i386__)
+       struct { uintptr_t p1, p2; } p;
+       __asm__("sidt %[p]" : [p] "=&m" (p));
+       return (unsigned int)(p.p1 & 0xfff);
+#else
+       // Not yet implemented.
+       return 0;
+#endif
+}
+
+#undef DISPATCH_TSD_INLINE
+
+#endif
 
        _dispatch_client_callout4(dr->dm_handler_ctxt, reason, dmsg, err,
                        dr->dm_handler_func);
        _dispatch_thread_setspecific(dispatch_queue_key, (dispatch_queue_t)dm);
+       _dispatch_introspection_queue_item_complete(dmsg);
        dispatch_release(dmsg);
 }
 
 
 _dispatch_introspection_hook_queue_destroy
 _dispatch_introspection_hook_queue_item_enqueue
 _dispatch_introspection_hook_queue_item_dequeue
+_dispatch_introspection_hook_queue_item_complete
 _dispatch_introspection_hook_queue_callout_begin
 _dispatch_introspection_hook_queue_callout_end