* 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