Skip to content

Enable CF runloop support for linux #101

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

Merged
merged 1 commit into from
Jul 12, 2016
Merged
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
34 changes: 23 additions & 11 deletions private/private.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,31 +166,43 @@ void _dispatch_prohibit_transition_to_multithreaded(bool prohibit);
* SPI for CoreFoundation/Foundation ONLY
*/

#define DISPATCH_COCOA_COMPAT (TARGET_OS_MAC || TARGET_OS_WIN32)
#if TARGET_OS_MAC
#define DISPATCH_COCOA_COMPAT 1
#elif defined(__linux__)
#define DISPATCH_COCOA_COMPAT 1
#else
#define DISPATCH_COCOA_COMPAT 0
#endif

#if DISPATCH_COCOA_COMPAT

#define DISPATCH_CF_SPI_VERSION 20160712

#if TARGET_OS_MAC
typedef mach_port_t dispatch_runloop_handle_t;
#elif defined(__linux__)
typedef int dispatch_runloop_handle_t;
#else
#error "runloop support not implemented on this platform"
#endif

#if TARGET_OS_MAC
__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
DISPATCH_EXPORT DISPATCH_CONST DISPATCH_WARN_RESULT DISPATCH_NOTHROW
mach_port_t
dispatch_runloop_handle_t
_dispatch_get_main_queue_port_4CF(void);
#endif

__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
DISPATCH_EXPORT DISPATCH_NOTHROW
void
_dispatch_main_queue_callback_4CF(mach_msg_header_t *_Null_unspecified msg);
#elif TARGET_OS_WIN32
__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
__OSX_AVAILABLE(10.12) __IOS_AVAILABLE(10.0)
__TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0)
DISPATCH_EXPORT DISPATCH_NOTHROW
HANDLE
dispatch_runloop_handle_t
_dispatch_get_main_queue_handle_4CF(void);

__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
DISPATCH_EXPORT DISPATCH_NOTHROW
void
_dispatch_main_queue_callback_4CF(void);
#endif // TARGET_OS_WIN32
_dispatch_main_queue_callback_4CF(void *_Null_unspecified msg);

__OSX_AVAILABLE_STARTING(__MAC_10_9,__IPHONE_7_0)
DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT
Expand Down
3 changes: 3 additions & 0 deletions src/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ DISPATCH_EXPORT DISPATCH_NOTHROW void dispatch_atfork_child(void);
#include <sys/mman.h>
#include <netinet/in.h>
#endif
#if defined(__linux__)
#include <sys/eventfd.h>
#endif

#ifdef __BLOCKS__
#include <Block_private.h>
Expand Down
165 changes: 129 additions & 36 deletions src/queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ static int _dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset);
#endif

#if DISPATCH_COCOA_COMPAT
static dispatch_once_t _dispatch_main_q_port_pred;
static dispatch_once_t _dispatch_main_q_handle_pred;
static void _dispatch_runloop_queue_poke(dispatch_queue_t dq,
pthread_priority_t pp, dispatch_wakeup_flags_t flags);
static void _dispatch_runloop_queue_port_init(void *ctxt);
static void _dispatch_runloop_queue_port_dispose(dispatch_queue_t dq);
static void _dispatch_runloop_queue_handle_init(void *ctxt);
static void _dispatch_runloop_queue_handle_dispose(dispatch_queue_t dq);
#endif

static void _dispatch_root_queues_init_once(void *context);
Expand Down Expand Up @@ -4063,6 +4063,48 @@ _dispatch_queue_wakeup(dispatch_queue_t dq, pthread_priority_t pp,
}

#if DISPATCH_COCOA_COMPAT

DISPATCH_ALWAYS_INLINE
static inline bool
_dispatch_runloop_handle_is_valid(dispatch_runloop_handle_t handle)
{
#if TARGET_OS_MAC
return MACH_PORT_VALID(handle);
#elif defined(__linux__)
return handle >= 0;
#else
#error "runloop support not implemented on this platform"
#endif
}

DISPATCH_ALWAYS_INLINE
static inline dispatch_runloop_handle_t
_dispatch_runloop_queue_get_handle(dispatch_queue_t dq)
{
#if TARGET_OS_MAC
return ((dispatch_runloop_handle_t)(uintptr_t)dq->do_ctxt);
#elif defined(__linux__)
// decode: 0 is a valid fd, so offset by 1 to distinguish from NULL
return ((dispatch_runloop_handle_t)(uintptr_t)dq->do_ctxt) - 1;
#else
#error "runloop support not implemented on this platform"
#endif
}

DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_runloop_queue_set_handle(dispatch_queue_t dq, dispatch_runloop_handle_t handle)
{
#if TARGET_OS_MAC
dq->do_ctxt = (void *)(uintptr_t)handle;
#elif defined(__linux__)
// encode: 0 is a valid fd, so offset by 1 to distinguish from NULL
dq->do_ctxt = (void *)(uintptr_t)(handle + 1);
Copy link
Contributor

Choose a reason for hiding this comment

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

a one-line comment about why this is here would probably be helpful

#else
#error "runloop support not implemented on this platform"
#endif
}

void
_dispatch_runloop_queue_wakeup(dispatch_queue_t dq, pthread_priority_t pp,
dispatch_wakeup_flags_t flags)
Expand Down Expand Up @@ -4090,13 +4132,6 @@ _dispatch_runloop_queue_wakeup(dispatch_queue_t dq, pthread_priority_t pp,
return _dispatch_release_tailcall(dq);
}
}
#else
void
_dispatch_runloop_queue_wakeup(dispatch_queue_t dq, pthread_priority_t pp,
dispatch_wakeup_flags_t flags)
{
LINUX_PORT_ERROR();
}
#endif

void
Expand Down Expand Up @@ -4130,10 +4165,13 @@ _dispatch_root_queue_wakeup(dispatch_queue_t dq,
static inline void
_dispatch_runloop_queue_class_poke(dispatch_queue_t dq)
{
mach_port_t mp = (mach_port_t)dq->do_ctxt;
if (!mp) {
dispatch_runloop_handle_t handle = _dispatch_runloop_queue_get_handle(dq);
if (!_dispatch_runloop_handle_is_valid(handle)) {
return;
}

#if TARGET_OS_MAC
mach_port_t mp = handle;
kern_return_t kr = _dispatch_send_wakeup_runloop_thread(mp, 0);
switch (kr) {
case MACH_SEND_TIMEOUT:
Expand All @@ -4144,6 +4182,15 @@ _dispatch_runloop_queue_class_poke(dispatch_queue_t dq)
(void)dispatch_assume_zero(kr);
break;
}
#elif defined(__linux__)
int result;
do {
result = eventfd_write(handle, 1);
} while (result == -1 && errno == EINTR);
(void)dispatch_assume_zero(result);
#else
#error "runloop support not implemented on this platform"
#endif
}

DISPATCH_NOINLINE
Expand All @@ -4158,8 +4205,8 @@ _dispatch_runloop_queue_poke(dispatch_queue_t dq,
// or in _dispatch_queue_cleanup2() for the main thread.

if (dq == &_dispatch_main_q) {
dispatch_once_f(&_dispatch_main_q_port_pred, dq,
_dispatch_runloop_queue_port_init);
dispatch_once_f(&_dispatch_main_q_handle_pred, dq,
_dispatch_runloop_queue_handle_init);
}
_dispatch_queue_override_priority(dq, /* inout */ &pp, /* inout */ &flags);
if (flags & DISPATCH_WAKEUP_OVERRIDING) {
Expand Down Expand Up @@ -4501,8 +4548,8 @@ _dispatch_main_queue_drain(void)
" from the wrong thread");
}

dispatch_once_f(&_dispatch_main_q_port_pred, dq,
_dispatch_runloop_queue_port_init);
dispatch_once_f(&_dispatch_main_q_handle_pred, dq,
_dispatch_runloop_queue_handle_init);

_dispatch_perfmon_start();
// <rdar://problem/23256682> hide the frame chaining when CFRunLoop
Expand Down Expand Up @@ -5547,7 +5594,7 @@ _dispatch_runloop_root_queue_create_4CF(const char *label, unsigned long flags)
_dispatch_queue_init(dq, DQF_THREAD_BOUND | DQF_CANNOT_TRYSYNC, 1, false);
dq->do_targetq = _dispatch_get_root_queue(_DISPATCH_QOS_CLASS_DEFAULT,true);
dq->dq_label = label ? label : "runloop-queue"; // no-copy contract
_dispatch_runloop_queue_port_init(dq);
_dispatch_runloop_queue_handle_init(dq);
_dispatch_queue_set_bound_thread(dq);
_dispatch_object_debug(dq, "%s", __func__);
return _dispatch_introspection_queue_create(dq);
Expand All @@ -5569,7 +5616,7 @@ _dispatch_runloop_queue_dispose(dispatch_queue_t dq)
{
_dispatch_object_debug(dq, "%s", __func__);
_dispatch_introspection_queue_dispose(dq);
_dispatch_runloop_queue_port_dispose(dq);
_dispatch_runloop_queue_handle_dispose(dq);
_dispatch_queue_destroy(dq);
}

Expand All @@ -5594,23 +5641,26 @@ _dispatch_runloop_root_queue_wakeup_4CF(dispatch_queue_t dq)
_dispatch_runloop_queue_wakeup(dq, 0, false);
}

mach_port_t
dispatch_runloop_handle_t
_dispatch_runloop_root_queue_get_port_4CF(dispatch_queue_t dq)
{
if (slowpath(dq->do_vtable != DISPATCH_VTABLE(queue_runloop))) {
DISPATCH_CLIENT_CRASH(dq->do_vtable, "Not a runloop queue");
}
return (mach_port_t)dq->do_ctxt;
return _dispatch_runloop_queue_get_handle(dq);
}

static void
_dispatch_runloop_queue_port_init(void *ctxt)
_dispatch_runloop_queue_handle_init(void *ctxt)
{
dispatch_queue_t dq = (dispatch_queue_t)ctxt;
mach_port_t mp;
kern_return_t kr;
dispatch_runloop_handle_t handle;

_dispatch_fork_becomes_unsafe();

#if TARGET_OS_MAC
mach_port_t mp;
kern_return_t kr;
Copy link
Contributor

Choose a reason for hiding this comment

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

keeping the mach_port_t mp local in this #if and only setting the generic handle var at the end of the block would read more cleanly to me (there is a lot of other code dealing with mp & mach_port_t in this project that this now clashes with style-wise)

kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
DISPATCH_VERIFY_MIG(kr);
(void)dispatch_assume_zero(kr);
Expand All @@ -5628,38 +5678,81 @@ _dispatch_runloop_queue_port_init(void *ctxt)
DISPATCH_VERIFY_MIG(kr);
(void)dispatch_assume_zero(kr);
}
dq->do_ctxt = (void*)(uintptr_t)mp;
handle = mp;
#elif defined(__linux__)
int fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (fd == -1) {
int err = errno;
switch (err) {
case EMFILE:
DISPATCH_CLIENT_CRASH(err, "eventfd() failure: "
"process is out of file descriptors");
break;
case ENFILE:
DISPATCH_CLIENT_CRASH(err, "eventfd() failure: "
"system is out of file descriptors");
break;
case ENOMEM:
DISPATCH_CLIENT_CRASH(err, "eventfd() failure: "
"kernel is out of memory");
break;
default:
DISPATCH_INTERNAL_CRASH(err, "eventfd() failure");
break;
}
}
handle = fd;
#else
#error "runloop support not implemented on this platform"
#endif
_dispatch_runloop_queue_set_handle(dq, handle);
Copy link
Contributor

Choose a reason for hiding this comment

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

do you really want to set the handle if it is invalid ? if seems like this wants to be inside an if for the assume above ?

alternatively add a hard failure (CRASH macro) here, similarly to what we have in _dispatch_kq_init() when we cannot create the kq file descriptor (e.g. due the process or system being out of fd's)


_dispatch_program_is_probably_callback_driven = true;
}

static void
_dispatch_runloop_queue_port_dispose(dispatch_queue_t dq)
_dispatch_runloop_queue_handle_dispose(dispatch_queue_t dq)
{
mach_port_t mp = (mach_port_t)dq->do_ctxt;
if (!mp) {
dispatch_runloop_handle_t handle = _dispatch_runloop_queue_get_handle(dq);
if (!_dispatch_runloop_handle_is_valid(handle)) {
return;
}
dq->do_ctxt = NULL;
#if TARGET_OS_MAC
mach_port_t mp = handle;
kern_return_t kr = mach_port_deallocate(mach_task_self(), mp);
DISPATCH_VERIFY_MIG(kr);
(void)dispatch_assume_zero(kr);
kr = mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
Copy link
Contributor

Choose a reason for hiding this comment

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

this still uses mp and will not compile as is. As above, I think this should keep a mach_port_t mp local inside this #if for symmetry with other such code (and to avoid mistakes like this)

DISPATCH_VERIFY_MIG(kr);
(void)dispatch_assume_zero(kr);
#elif defined(__linux__)
int rc = close(handle);
(void)dispatch_assume_zero(rc);
#else
#error "runloop support not implemented on this platform"
#endif
}

#pragma mark -
#pragma mark dispatch_main_queue

mach_port_t
_dispatch_get_main_queue_port_4CF(void)
dispatch_runloop_handle_t
_dispatch_get_main_queue_handle_4CF(void)
Copy link
Contributor

Choose a reason for hiding this comment

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

as in the header comment above, IMO this could be the primary version and _dispatch_get_main_queue_port_4CF just a legacy alias

{
dispatch_queue_t dq = &_dispatch_main_q;
dispatch_once_f(&_dispatch_main_q_port_pred, dq,
_dispatch_runloop_queue_port_init);
return (mach_port_t)dq->do_ctxt;
dispatch_once_f(&_dispatch_main_q_handle_pred, dq,
_dispatch_runloop_queue_handle_init);
return _dispatch_runloop_queue_get_handle(dq);
}

#if TARGET_OS_MAC
dispatch_runloop_handle_t
_dispatch_get_main_queue_port_4CF(void)
{
return _dispatch_get_main_queue_handle_4CF();
}
#endif

static bool main_q_is_draining;

Expand All @@ -5673,7 +5766,7 @@ _dispatch_queue_set_mainq_drain_state(bool arg)
}

void
_dispatch_main_queue_callback_4CF(mach_msg_header_t *msg DISPATCH_UNUSED)
_dispatch_main_queue_callback_4CF(void *ignored DISPATCH_UNUSED)
{
if (main_q_is_draining) {
return;
Expand Down Expand Up @@ -5798,9 +5891,9 @@ _dispatch_queue_cleanup2(void)
#endif

#if DISPATCH_COCOA_COMPAT
dispatch_once_f(&_dispatch_main_q_port_pred, dq,
_dispatch_runloop_queue_port_init);
_dispatch_runloop_queue_port_dispose(dq);
dispatch_once_f(&_dispatch_main_q_handle_pred, dq,
_dispatch_runloop_queue_handle_init);
_dispatch_runloop_queue_handle_dispose(dq);
#endif
}

Expand Down
6 changes: 0 additions & 6 deletions src/shims/linux_stubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@
#undef LINUX_PORT_ERROR
#define LINUX_PORT_ERROR() do { printf("LINUX_PORT_ERROR_CALLED %s:%d: %s\n",__FILE__,__LINE__,__FUNCTION__); abort(); } while (0)

unsigned long _dispatch_runloop_queue_probe(dispatch_queue_t dq) {
LINUX_PORT_ERROR();
}
void _dispatch_runloop_queue_xref_dispose() { LINUX_PORT_ERROR(); }

void _dispatch_runloop_queue_dispose() { LINUX_PORT_ERROR(); }

/*
* Stubbed out static data
Expand Down