Skip to content

Commit 9f58d82

Browse files
committed
Add support for CFRunLoop integration APIs
1 parent fefb6cf commit 9f58d82

File tree

6 files changed

+174
-10
lines changed

6 files changed

+174
-10
lines changed

private/queue_private.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,53 @@ dispatch_async_enforce_qos_class_f(dispatch_queue_t queue,
369369
void *context,
370370
dispatch_function_t work);
371371

372+
#if DISPATCH_ENABLE_RUNLOOP_SUPPORT
373+
/*!
374+
* @functiongroup Runloop integration APIs
375+
* APIs to provide integration of the main queue into run loop implementations
376+
* such as Swift Foundation.
377+
*/
378+
379+
/*!
380+
* @function dispatch_get_main_queue_eventfd_4CF
381+
*
382+
* @abstract
383+
* Returns a file descriptor representing an eventfd object that provides a
384+
* wait/notify mechanism to signal pending tasks on the main queue.
385+
*
386+
* @discussion
387+
* The file descriptor can be monitored with epoll() and becomes readable when
388+
* there are pending tasks on the queue. Once readable you should call
389+
* eventfd_read() to acknowledge the notification and then call
390+
* dispatch_main_queue_drain_np() to perform the pending tasks on the queue.
391+
*
392+
* @availability
393+
* Linux only.
394+
*
395+
* @result
396+
* A file descriptor representing the eventfd object.
397+
*/
398+
DISPATCH_EXPORT DISPATCH_WARN_RESULT DISPATCH_NOTHROW
399+
int
400+
dispatch_get_main_queue_eventfd_4CF();
401+
402+
/*!
403+
* @function dispatch_main_queue_drain_4CF
404+
*
405+
* @abstract
406+
* Executes pending tasks enqueued to the main queue.
407+
*
408+
* @availability
409+
* Linux only.
410+
*
411+
* @discussion
412+
* The run loop should invoke this function to execute pending tasks on the
413+
* main queue.
414+
*/
415+
DISPATCH_EXPORT DISPATCH_NOTHROW
416+
void
417+
dispatch_main_queue_drain_4CF();
418+
#endif
372419

373420
__END_DECLS
374421

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ libdispatch_la_SOURCES= \
4444
shims/perfmon.h \
4545
shims/time.h \
4646
shims/tsd.h \
47+
shims/pthread_main_np.h \
4748
shims/yield.h
4849

4950
AM_CPPFLAGS=-I$(top_builddir) -I$(top_srcdir) \

src/internal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@
3737
#include <TargetConditionals.h>
3838
#endif
3939

40+
#ifdef __linux__
41+
#include <sys/eventfd.h>
42+
#define DISPATCH_LINUX_COMPAT 1
43+
#endif
44+
45+
#define DISPATCH_ENABLE_RUNLOOP_SUPPORT (DISPATCH_LINUX_COMPAT || DISPATCH_COCOA_COMPAT)
4046

4147
#if !defined(DISPATCH_MACH_SPI) && TARGET_OS_MAC
4248
#define DISPATCH_MACH_SPI 1

src/queue.c

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,18 @@ static void *_dispatch_worker_thread(void *context);
7575
static int _dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset);
7676
#endif
7777

78+
#if DISPATCH_ENABLE_RUNLOOP_SUPPORT
79+
static dispatch_queue_t _dispatch_main_queue_wakeup(void);
7880
#if DISPATCH_COCOA_COMPAT
7981
static dispatch_once_t _dispatch_main_q_port_pred;
80-
static dispatch_queue_t _dispatch_main_queue_wakeup(void);
8182
unsigned long _dispatch_runloop_queue_wakeup(dispatch_queue_t dq);
8283
static void _dispatch_runloop_queue_port_init(void *ctxt);
8384
static void _dispatch_runloop_queue_port_dispose(dispatch_queue_t dq);
85+
#elif DISPATCH_LINUX_COMPAT
86+
static dispatch_once_t _dispatch_main_q_eventfd_pred;
87+
static void _dispatch_main_q_eventfd_init(void *ctxt);
88+
static int main_q_eventfd = -1;
89+
#endif
8490
#endif
8591

8692
static void _dispatch_root_queues_init(void *context);
@@ -3466,6 +3472,7 @@ _dispatch_wakeup(dispatch_object_t dou)
34663472
// probe does
34673473
}
34683474

3475+
#if DISPATCH_ENABLE_RUNLOOP_SUPPORT
34693476
#if DISPATCH_COCOA_COMPAT
34703477
static inline void
34713478
_dispatch_runloop_queue_wakeup_thread(dispatch_queue_t dq)
@@ -3493,6 +3500,7 @@ _dispatch_runloop_queue_wakeup(dispatch_queue_t dq)
34933500
_dispatch_runloop_queue_wakeup_thread(dq);
34943501
return false;
34953502
}
3503+
#endif
34963504

34973505
DISPATCH_NOINLINE
34983506
static dispatch_queue_t
@@ -3502,9 +3510,21 @@ _dispatch_main_queue_wakeup(void)
35023510
if (!dq->dq_is_thread_bound) {
35033511
return NULL;
35043512
}
3513+
#if DISPATCH_COCOA_COMPAT
35053514
dispatch_once_f(&_dispatch_main_q_port_pred, dq,
35063515
_dispatch_runloop_queue_port_init);
35073516
_dispatch_runloop_queue_wakeup_thread(dq);
3517+
#elif DISPATCH_LINUX_COMPAT
3518+
dispatch_once_f(&_dispatch_main_q_eventfd_pred, dq,
3519+
_dispatch_main_q_eventfd_init);
3520+
if (main_q_eventfd != -1) {
3521+
int result;
3522+
do {
3523+
result = eventfd_write(main_q_eventfd, 1);
3524+
} while (result == -1 && errno == EINTR);
3525+
(void)dispatch_assume_zero(result);
3526+
}
3527+
#endif
35083528
return NULL;
35093529
}
35103530
#endif
@@ -3752,7 +3772,7 @@ _dispatch_queue_drain(dispatch_object_t dou)
37523772
return sema;
37533773
}
37543774

3755-
#if DISPATCH_COCOA_COMPAT
3775+
#if DISPATCH_ENABLE_RUNLOOP_SUPPORT
37563776
static void
37573777
_dispatch_main_queue_drain(void)
37583778
{
@@ -3803,6 +3823,7 @@ _dispatch_main_queue_drain(void)
38033823
_dispatch_force_cache_cleanup();
38043824
}
38053825

3826+
#if DISPATCH_COCOA_COMPAT
38063827
static bool
38073828
_dispatch_runloop_queue_drain_one(dispatch_queue_t dq)
38083829
{
@@ -3831,6 +3852,7 @@ _dispatch_runloop_queue_drain_one(dispatch_queue_t dq)
38313852
return next_dc;
38323853
}
38333854
#endif
3855+
#endif
38343856

38353857
DISPATCH_ALWAYS_INLINE_NDEBUG
38363858
static inline _dispatch_thread_semaphore_t
@@ -4485,10 +4507,24 @@ _dispatch_runloop_queue_port_dispose(dispatch_queue_t dq)
44854507
DISPATCH_VERIFY_MIG(kr);
44864508
(void)dispatch_assume_zero(kr);
44874509
}
4510+
#endif
44884511

44894512
#pragma mark -
44904513
#pragma mark dispatch_main_queue
44914514

4515+
#if DISPATCH_ENABLE_RUNLOOP_SUPPORT
4516+
static bool main_q_is_draining;
4517+
4518+
// 6618342 Contact the team that owns the Instrument DTrace probe before
4519+
// renaming this symbol
4520+
DISPATCH_NOINLINE
4521+
static void
4522+
_dispatch_queue_set_mainq_drain_state(bool arg)
4523+
{
4524+
main_q_is_draining = arg;
4525+
}
4526+
4527+
#if DISPATCH_COCOA_COMPAT
44924528
mach_port_t
44934529
_dispatch_get_main_queue_port_4CF(void)
44944530
{
@@ -4498,20 +4534,34 @@ _dispatch_get_main_queue_port_4CF(void)
44984534
return (mach_port_t)dq->do_ctxt;
44994535
}
45004536

4501-
static bool main_q_is_draining;
4537+
void
4538+
_dispatch_main_queue_callback_4CF(mach_msg_header_t *msg DISPATCH_UNUSED)
4539+
{
4540+
if (main_q_is_draining) {
4541+
return;
4542+
}
4543+
_dispatch_queue_set_mainq_drain_state(true);
4544+
_dispatch_main_queue_drain();
4545+
_dispatch_queue_set_mainq_drain_state(false);
4546+
}
45024547

4503-
// 6618342 Contact the team that owns the Instrument DTrace probe before
4504-
// renaming this symbol
4505-
DISPATCH_NOINLINE
4506-
static void
4507-
_dispatch_queue_set_mainq_drain_state(bool arg)
4548+
#elif DISPATCH_LINUX_COMPAT
4549+
int
4550+
dispatch_get_main_queue_eventfd_4CF()
45084551
{
4509-
main_q_is_draining = arg;
4552+
dispatch_once_f(&_dispatch_main_q_eventfd_pred, NULL,
4553+
_dispatch_main_q_eventfd_init);
4554+
return main_q_eventfd;
45104555
}
45114556

45124557
void
4513-
_dispatch_main_queue_callback_4CF(mach_msg_header_t *msg DISPATCH_UNUSED)
4558+
dispatch_main_queue_drain_4CF()
45144559
{
4560+
if (!pthread_main_np()) {
4561+
DISPATCH_CLIENT_CRASH("dispatch_main_queue_drain_np() must be called on "
4562+
"the main thread");
4563+
}
4564+
45154565
if (main_q_is_draining) {
45164566
return;
45174567
}
@@ -4520,6 +4570,15 @@ _dispatch_main_queue_callback_4CF(mach_msg_header_t *msg DISPATCH_UNUSED)
45204570
_dispatch_queue_set_mainq_drain_state(false);
45214571
}
45224572

4573+
static
4574+
void _dispatch_main_q_eventfd_init(void *ctxt DISPATCH_UNUSED)
4575+
{
4576+
_dispatch_safe_fork = false;
4577+
main_q_eventfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
4578+
(void)dispatch_assume(main_q_eventfd != -1);
4579+
_dispatch_program_is_probably_callback_driven = true;
4580+
}
4581+
#endif
45234582
#endif
45244583

45254584
void
@@ -4585,6 +4644,15 @@ _dispatch_queue_cleanup2(void)
45854644
dispatch_once_f(&_dispatch_main_q_port_pred, dq,
45864645
_dispatch_runloop_queue_port_init);
45874646
_dispatch_runloop_queue_port_dispose(dq);
4647+
#elif DISPATCH_LINUX_COMPAT
4648+
dispatch_once_f(&_dispatch_main_q_eventfd_pred, NULL,
4649+
_dispatch_main_q_eventfd_init);
4650+
int fd = main_q_eventfd;
4651+
main_q_eventfd = -1;
4652+
4653+
if (fd != -1) {
4654+
close(fd);
4655+
}
45884656
#endif
45894657
}
45904658

src/shims.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ void __builtin_trap(void);
174174
#include "shims/getprogname.h"
175175
#include "shims/time.h"
176176

177+
#include "shims/pthread_main_np.h"
178+
177179
#ifdef __APPLE__
178180
// Clear the stack before calling long-running thread-handler functions that
179181
// never return (and don't take arguments), to facilitate leak detection and

src/shims/pthread_main_np.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* @APPLE_APACHE_LICENSE_HEADER_START@
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* @APPLE_APACHE_LICENSE_HEADER_END@
17+
*/
18+
19+
#ifndef __DISPATCH_SHIMS_PTHREAD_MAIN_NP__
20+
#define __DISPATCH_SHIMS_PTHREAD_MAIN_NP__
21+
22+
#if !HAVE_PTHREAD_MAIN_NP
23+
24+
#if __linux__
25+
#include <unistd.h>
26+
#include <sys/syscall.h>
27+
#include <sys/types.h>
28+
#endif
29+
30+
static inline int
31+
pthread_main_np()
32+
{
33+
#if __linux__
34+
return syscall(SYS_gettid) == getpid() ? 1 : 0;
35+
#else
36+
#error "No suported way to determine if the current thread is the main thread."
37+
#endif
38+
}
39+
#endif /* !HAVE_PTHREAD_MAIN_NP */
40+
#endif /* __DISPATCH_SHIMS_PTHREAD_MAIN_NP__ */

0 commit comments

Comments
 (0)