Skip to content

Commit 8a95b80

Browse files
committed
Add support for CFRunLoop integration APIs
1 parent fefb6cf commit 8a95b80

File tree

5 files changed

+187
-10
lines changed

5 files changed

+187
-10
lines changed

dispatch/queue.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,54 @@ DISPATCH_EXPORT DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW
990990
void *
991991
dispatch_get_specific(const void *key);
992992

993+
#if __linux__
994+
/*!
995+
* @functiongroup Runloop integration APIs
996+
* APIs to provide integration of the main queue into run loop implementations
997+
* such as Swift Foundation.
998+
*/
999+
1000+
/*!
1001+
* @function dispatch_get_main_queue_eventfd_np
1002+
*
1003+
* @abstract
1004+
* Returns a file descriptor representing an eventfd object that provides a
1005+
* wait/notify mechanism to signal pending tasks on the main queue.
1006+
*
1007+
* @discussion
1008+
* The file descriptor can be monitored with epoll() and becomes readable when
1009+
* there are pending tasks on the queue. Once readable you should call
1010+
* eventfd_read() to acknowledge the notification and then call
1011+
* dispatch_main_queue_drain_np() to perform the pending tasks on the queue.
1012+
*
1013+
* @availability
1014+
* Linux only.
1015+
*
1016+
* @result
1017+
* A file descriptor representing the eventfd object.
1018+
*/
1019+
DISPATCH_EXPORT DISPATCH_WARN_RESULT DISPATCH_NOTHROW
1020+
int
1021+
dispatch_get_main_queue_eventfd_np();
1022+
1023+
/*!
1024+
* @function dispatch_main_queue_drain_np
1025+
*
1026+
* @abstract
1027+
* Executes pending tasks enqueued to the main queue.
1028+
*
1029+
* @availability
1030+
* Linux only.
1031+
*
1032+
* @discussion
1033+
* The run loop should invoke this function to execute pending tasks on the
1034+
* main queue.
1035+
*/
1036+
DISPATCH_EXPORT DISPATCH_NOTHROW
1037+
void
1038+
dispatch_main_queue_drain_np();
1039+
#endif
1040+
9931041
__END_DECLS
9941042

9951043
#endif

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/queue.c

Lines changed: 96 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
#include "protocol.h"
2424
#endif
2525

26+
#if __linux__
27+
#include <sys/eventfd.h>
28+
#define DISPATCH_LINUX_COMPAT 1
29+
#endif
30+
2631
#if (!HAVE_PTHREAD_WORKQUEUES || DISPATCH_DEBUG) && \
2732
!defined(DISPATCH_ENABLE_THREAD_POOL)
2833
#define DISPATCH_ENABLE_THREAD_POOL 1
@@ -77,12 +82,21 @@ static int _dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset);
7782

7883
#if DISPATCH_COCOA_COMPAT
7984
static dispatch_once_t _dispatch_main_q_port_pred;
80-
static dispatch_queue_t _dispatch_main_queue_wakeup(void);
8185
unsigned long _dispatch_runloop_queue_wakeup(dispatch_queue_t dq);
8286
static void _dispatch_runloop_queue_port_init(void *ctxt);
8387
static void _dispatch_runloop_queue_port_dispose(dispatch_queue_t dq);
8488
#endif
8589

90+
#if DISPATCH_COCOA_COMPAT || DISPATCH_LINUX_COMPAT
91+
static dispatch_queue_t _dispatch_main_queue_wakeup(void);
92+
#endif
93+
94+
#if DISPATCH_LINUX_COMPAT
95+
static dispatch_once_t _dispatch_main_q_eventfd_pred;
96+
static void _dispatch_main_q_eventfd_init(void *ctxt);
97+
static int main_q_eventfd = -1;
98+
#endif
99+
86100
static void _dispatch_root_queues_init(void *context);
87101
static dispatch_once_t _dispatch_root_queues_pred;
88102

@@ -3509,6 +3523,29 @@ _dispatch_main_queue_wakeup(void)
35093523
}
35103524
#endif
35113525

3526+
#if DISPATCH_LINUX_COMPAT
3527+
DISPATCH_NOINLINE
3528+
static dispatch_queue_t
3529+
_dispatch_main_queue_wakeup(void)
3530+
{
3531+
dispatch_queue_t dq = &_dispatch_main_q;
3532+
f (!dq->dq_is_thread_bound) {
3533+
return NULL;
3534+
}
3535+
dispatch_once_f(&_dispatch_main_q_eventfd_pred, dq,
3536+
_dispatch_main_q_eventfd_init);
3537+
if (main_q_eventfd != -1) {
3538+
int result;
3539+
do {
3540+
result = eventfd_write(main_q_eventfd, 1);
3541+
} while (result == -1 && errno == EINTR);
3542+
(void)dispatch_assume_zero(result);
3543+
}
3544+
return NULL;
3545+
}
3546+
#endif
3547+
3548+
35123549
DISPATCH_NOINLINE
35133550
static void
35143551
_dispatch_queue_wakeup_global_slow(dispatch_queue_t dq, unsigned int n)
@@ -3752,7 +3789,7 @@ _dispatch_queue_drain(dispatch_object_t dou)
37523789
return sema;
37533790
}
37543791

3755-
#if DISPATCH_COCOA_COMPAT
3792+
#if DISPATCH_COCOA_COMPAT || DISPATCH_LINUX_COMPAT
37563793
static void
37573794
_dispatch_main_queue_drain(void)
37583795
{
@@ -4485,10 +4522,25 @@ _dispatch_runloop_queue_port_dispose(dispatch_queue_t dq)
44854522
DISPATCH_VERIFY_MIG(kr);
44864523
(void)dispatch_assume_zero(kr);
44874524
}
4525+
#endif
44884526

44894527
#pragma mark -
44904528
#pragma mark dispatch_main_queue
44914529

4530+
#if DISPATCH_COCOA_COMPAT || DISPATCH_LINUX_COMPAT
4531+
static bool main_q_is_draining;
4532+
4533+
// 6618342 Contact the team that owns the Instrument DTrace probe before
4534+
// renaming this symbol
4535+
DISPATCH_NOINLINE
4536+
static void
4537+
_dispatch_queue_set_mainq_drain_state(bool arg)
4538+
{
4539+
main_q_is_draining = arg;
4540+
}
4541+
#endif
4542+
4543+
#if DISPATCH_COCOA_COMPAT
44924544
mach_port_t
44934545
_dispatch_get_main_queue_port_4CF(void)
44944546
{
@@ -4498,20 +4550,36 @@ _dispatch_get_main_queue_port_4CF(void)
44984550
return (mach_port_t)dq->do_ctxt;
44994551
}
45004552

4501-
static bool main_q_is_draining;
4553+
void
4554+
_dispatch_main_queue_callback_4CF(mach_msg_header_t *msg DISPATCH_UNUSED)
4555+
{
4556+
if (main_q_is_draining) {
4557+
return;
4558+
}
4559+
_dispatch_queue_set_mainq_drain_state(true);
4560+
_dispatch_main_queue_drain();
4561+
_dispatch_queue_set_mainq_drain_state(false);
4562+
}
45024563

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)
4564+
#endif
4565+
4566+
#if DISPATCH_LINUX_COMPAT
4567+
int
4568+
dispatch_get_main_queue_eventfd_np()
45084569
{
4509-
main_q_is_draining = arg;
4570+
dispatch_once_f(&_dispatch_main_q_eventfd_pred, NULL,
4571+
_dispatch_main_q_eventfd_init);
4572+
return main_q_eventfd;
45104573
}
45114574

45124575
void
4513-
_dispatch_main_queue_callback_4CF(mach_msg_header_t *msg DISPATCH_UNUSED)
4576+
dispatch_main_queue_drain_np()
45144577
{
4578+
if (!pthread_main_np()) {
4579+
DISPATCH_CLIENT_CRASH("dispatch_main_queue_drain_np() must be called on "
4580+
"the main thread");
4581+
}
4582+
45154583
if (main_q_is_draining) {
45164584
return;
45174585
}
@@ -4520,6 +4588,14 @@ _dispatch_main_queue_callback_4CF(mach_msg_header_t *msg DISPATCH_UNUSED)
45204588
_dispatch_queue_set_mainq_drain_state(false);
45214589
}
45224590

4591+
static
4592+
void _dispatch_main_q_eventfd_init(void *ctxt DISPATCH_UNUSED)
4593+
{
4594+
_dispatch_safe_fork = false;
4595+
main_q_eventfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
4596+
(void)dispatch_assume(main_q_eventfd != -1);
4597+
_dispatch_program_is_probably_callback_driven = true;
4598+
}
45234599
#endif
45244600

45254601
void
@@ -4586,6 +4662,16 @@ _dispatch_queue_cleanup2(void)
45864662
_dispatch_runloop_queue_port_init);
45874663
_dispatch_runloop_queue_port_dispose(dq);
45884664
#endif
4665+
#if DISPATCH_LINUX_COMPAT
4666+
dispatch_once_f(&_dispatch_main_q_eventfd_pred, NULL,
4667+
_dispatch_main_q_eventfd_init);
4668+
int fd = main_q_eventfd;
4669+
main_q_eventfd = -1;
4670+
4671+
if (fd != -1) {
4672+
close(fd);
4673+
}
4674+
#endif
45894675
}
45904676

45914677
static void

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)