Skip to content

Add support for CFRunLoop integration APIs #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions private/queue_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
6 changes: 6 additions & 0 deletions src/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
#include <TargetConditionals.h>
#endif

#ifdef __linux__
#include <sys/eventfd.h>
#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
Expand Down
88 changes: 78 additions & 10 deletions src/queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
}
Expand All @@ -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
Expand Down Expand Up @@ -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
}

Expand Down
2 changes: 2 additions & 0 deletions src/shims.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 40 additions & 0 deletions src/shims/pthread_main_np.h
Original file line number Diff line number Diff line change
@@ -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 <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#endif

static inline int
pthread_main_np()
{
#if __linux__
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is very slow, you should instead base this patch on top of the __thread infrastructure that is built built.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is indeed slow. Right now I am building a CFRunLoop based GUI in Swift, and with no input added to the runloop I am getting 0ms per loop on Mac and 17ms on Linux. With actual input im getting up to 3000ms only on Linux, crazy.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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__ */