From 9f58d8281baca0146fdeecb3947fa956b862afb0 Mon Sep 17 00:00:00 2001 From: seabaylea Date: Mon, 8 Feb 2016 12:49:37 +0000 Subject: [PATCH] Add support for CFRunLoop integration APIs --- private/queue_private.h | 47 ++++++++++++++++++++ src/Makefile.am | 1 + src/internal.h | 6 +++ src/queue.c | 88 ++++++++++++++++++++++++++++++++----- src/shims.h | 2 + src/shims/pthread_main_np.h | 40 +++++++++++++++++ 6 files changed, 174 insertions(+), 10 deletions(-) create mode 100644 src/shims/pthread_main_np.h diff --git a/private/queue_private.h b/private/queue_private.h index f2bb69132..4559e551c 100644 --- a/private/queue_private.h +++ b/private/queue_private.h @@ -369,6 +369,53 @@ dispatch_async_enforce_qos_class_f(dispatch_queue_t queue, void *context, dispatch_function_t work); +#if DISPATCH_ENABLE_RUNLOOP_SUPPORT +/*! + * @functiongroup Runloop integration APIs + * APIs to provide integration of the main queue into run loop implementations + * such as Swift Foundation. + */ + +/*! + * @function dispatch_get_main_queue_eventfd_4CF + * + * @abstract + * Returns a file descriptor representing an eventfd object that provides a + * wait/notify mechanism to signal pending tasks on the main queue. + * + * @discussion + * The file descriptor can be monitored with epoll() and becomes readable when + * there are pending tasks on the queue. Once readable you should call + * eventfd_read() to acknowledge the notification and then call + * dispatch_main_queue_drain_np() to perform the pending tasks on the queue. + * + * @availability + * Linux only. + * + * @result + * A file descriptor representing the eventfd object. + */ +DISPATCH_EXPORT DISPATCH_WARN_RESULT DISPATCH_NOTHROW +int +dispatch_get_main_queue_eventfd_4CF(); + +/*! + * @function dispatch_main_queue_drain_4CF + * + * @abstract + * Executes pending tasks enqueued to the main queue. + * + * @availability + * Linux only. + * + * @discussion + * The run loop should invoke this function to execute pending tasks on the + * main queue. + */ +DISPATCH_EXPORT DISPATCH_NOTHROW +void +dispatch_main_queue_drain_4CF(); +#endif __END_DECLS diff --git a/src/Makefile.am b/src/Makefile.am index ab1998a36..b087be2b9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,6 +44,7 @@ libdispatch_la_SOURCES= \ shims/perfmon.h \ shims/time.h \ shims/tsd.h \ + shims/pthread_main_np.h \ shims/yield.h AM_CPPFLAGS=-I$(top_builddir) -I$(top_srcdir) \ diff --git a/src/internal.h b/src/internal.h index 9ac431504..8c01b2137 100644 --- a/src/internal.h +++ b/src/internal.h @@ -37,6 +37,12 @@ #include #endif +#ifdef __linux__ +#include +#define DISPATCH_LINUX_COMPAT 1 +#endif + +#define DISPATCH_ENABLE_RUNLOOP_SUPPORT (DISPATCH_LINUX_COMPAT || DISPATCH_COCOA_COMPAT) #if !defined(DISPATCH_MACH_SPI) && TARGET_OS_MAC #define DISPATCH_MACH_SPI 1 diff --git a/src/queue.c b/src/queue.c index db414f69d..088eb89fe 100644 --- a/src/queue.c +++ b/src/queue.c @@ -75,12 +75,18 @@ static void *_dispatch_worker_thread(void *context); static int _dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset); #endif +#if DISPATCH_ENABLE_RUNLOOP_SUPPORT +static dispatch_queue_t _dispatch_main_queue_wakeup(void); #if DISPATCH_COCOA_COMPAT static dispatch_once_t _dispatch_main_q_port_pred; -static dispatch_queue_t _dispatch_main_queue_wakeup(void); unsigned long _dispatch_runloop_queue_wakeup(dispatch_queue_t dq); static void _dispatch_runloop_queue_port_init(void *ctxt); static void _dispatch_runloop_queue_port_dispose(dispatch_queue_t dq); +#elif DISPATCH_LINUX_COMPAT +static dispatch_once_t _dispatch_main_q_eventfd_pred; +static void _dispatch_main_q_eventfd_init(void *ctxt); +static int main_q_eventfd = -1; +#endif #endif static void _dispatch_root_queues_init(void *context); @@ -3466,6 +3472,7 @@ _dispatch_wakeup(dispatch_object_t dou) // probe does } +#if DISPATCH_ENABLE_RUNLOOP_SUPPORT #if DISPATCH_COCOA_COMPAT static inline void _dispatch_runloop_queue_wakeup_thread(dispatch_queue_t dq) @@ -3493,6 +3500,7 @@ _dispatch_runloop_queue_wakeup(dispatch_queue_t dq) _dispatch_runloop_queue_wakeup_thread(dq); return false; } +#endif DISPATCH_NOINLINE static dispatch_queue_t @@ -3502,9 +3510,21 @@ _dispatch_main_queue_wakeup(void) if (!dq->dq_is_thread_bound) { return NULL; } +#if DISPATCH_COCOA_COMPAT dispatch_once_f(&_dispatch_main_q_port_pred, dq, _dispatch_runloop_queue_port_init); _dispatch_runloop_queue_wakeup_thread(dq); +#elif DISPATCH_LINUX_COMPAT + dispatch_once_f(&_dispatch_main_q_eventfd_pred, dq, + _dispatch_main_q_eventfd_init); + if (main_q_eventfd != -1) { + int result; + do { + result = eventfd_write(main_q_eventfd, 1); + } while (result == -1 && errno == EINTR); + (void)dispatch_assume_zero(result); + } +#endif return NULL; } #endif @@ -3752,7 +3772,7 @@ _dispatch_queue_drain(dispatch_object_t dou) return sema; } -#if DISPATCH_COCOA_COMPAT +#if DISPATCH_ENABLE_RUNLOOP_SUPPORT static void _dispatch_main_queue_drain(void) { @@ -3803,6 +3823,7 @@ _dispatch_main_queue_drain(void) _dispatch_force_cache_cleanup(); } +#if DISPATCH_COCOA_COMPAT static bool _dispatch_runloop_queue_drain_one(dispatch_queue_t dq) { @@ -3831,6 +3852,7 @@ _dispatch_runloop_queue_drain_one(dispatch_queue_t dq) return next_dc; } #endif +#endif DISPATCH_ALWAYS_INLINE_NDEBUG static inline _dispatch_thread_semaphore_t @@ -4485,10 +4507,24 @@ _dispatch_runloop_queue_port_dispose(dispatch_queue_t dq) DISPATCH_VERIFY_MIG(kr); (void)dispatch_assume_zero(kr); } +#endif #pragma mark - #pragma mark dispatch_main_queue +#if DISPATCH_ENABLE_RUNLOOP_SUPPORT +static bool main_q_is_draining; + +// 6618342 Contact the team that owns the Instrument DTrace probe before +// renaming this symbol +DISPATCH_NOINLINE +static void +_dispatch_queue_set_mainq_drain_state(bool arg) +{ + main_q_is_draining = arg; +} + +#if DISPATCH_COCOA_COMPAT mach_port_t _dispatch_get_main_queue_port_4CF(void) { @@ -4498,20 +4534,34 @@ _dispatch_get_main_queue_port_4CF(void) return (mach_port_t)dq->do_ctxt; } -static bool main_q_is_draining; +void +_dispatch_main_queue_callback_4CF(mach_msg_header_t *msg DISPATCH_UNUSED) +{ + if (main_q_is_draining) { + return; + } + _dispatch_queue_set_mainq_drain_state(true); + _dispatch_main_queue_drain(); + _dispatch_queue_set_mainq_drain_state(false); +} -// 6618342 Contact the team that owns the Instrument DTrace probe before -// renaming this symbol -DISPATCH_NOINLINE -static void -_dispatch_queue_set_mainq_drain_state(bool arg) +#elif DISPATCH_LINUX_COMPAT +int +dispatch_get_main_queue_eventfd_4CF() { - main_q_is_draining = arg; + dispatch_once_f(&_dispatch_main_q_eventfd_pred, NULL, + _dispatch_main_q_eventfd_init); + return main_q_eventfd; } void -_dispatch_main_queue_callback_4CF(mach_msg_header_t *msg DISPATCH_UNUSED) +dispatch_main_queue_drain_4CF() { + if (!pthread_main_np()) { + DISPATCH_CLIENT_CRASH("dispatch_main_queue_drain_np() must be called on " + "the main thread"); + } + if (main_q_is_draining) { return; } @@ -4520,6 +4570,15 @@ _dispatch_main_queue_callback_4CF(mach_msg_header_t *msg DISPATCH_UNUSED) _dispatch_queue_set_mainq_drain_state(false); } +static +void _dispatch_main_q_eventfd_init(void *ctxt DISPATCH_UNUSED) +{ + _dispatch_safe_fork = false; + main_q_eventfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + (void)dispatch_assume(main_q_eventfd != -1); + _dispatch_program_is_probably_callback_driven = true; +} +#endif #endif void @@ -4585,6 +4644,15 @@ _dispatch_queue_cleanup2(void) dispatch_once_f(&_dispatch_main_q_port_pred, dq, _dispatch_runloop_queue_port_init); _dispatch_runloop_queue_port_dispose(dq); +#elif DISPATCH_LINUX_COMPAT + dispatch_once_f(&_dispatch_main_q_eventfd_pred, NULL, + _dispatch_main_q_eventfd_init); + int fd = main_q_eventfd; + main_q_eventfd = -1; + + if (fd != -1) { + close(fd); + } #endif } diff --git a/src/shims.h b/src/shims.h index d90b775cb..8d363b672 100644 --- a/src/shims.h +++ b/src/shims.h @@ -174,6 +174,8 @@ void __builtin_trap(void); #include "shims/getprogname.h" #include "shims/time.h" +#include "shims/pthread_main_np.h" + #ifdef __APPLE__ // Clear the stack before calling long-running thread-handler functions that // never return (and don't take arguments), to facilitate leak detection and diff --git a/src/shims/pthread_main_np.h b/src/shims/pthread_main_np.h new file mode 100644 index 000000000..962ca9a4f --- /dev/null +++ b/src/shims/pthread_main_np.h @@ -0,0 +1,40 @@ +/* + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +#ifndef __DISPATCH_SHIMS_PTHREAD_MAIN_NP__ +#define __DISPATCH_SHIMS_PTHREAD_MAIN_NP__ + +#if !HAVE_PTHREAD_MAIN_NP + +#if __linux__ +#include +#include +#include +#endif + +static inline int +pthread_main_np() +{ +#if __linux__ + return syscall(SYS_gettid) == getpid() ? 1 : 0; +#else +#error "No suported way to determine if the current thread is the main thread." +#endif +} +#endif /* !HAVE_PTHREAD_MAIN_NP */ +#endif /* __DISPATCH_SHIMS_PTHREAD_MAIN_NP__ */