Skip to content

Commit a619b96

Browse files
committed
CFRunLoop implementation for Linux
1 parent fc72c16 commit a619b96

File tree

6 files changed

+214
-15
lines changed

6 files changed

+214
-15
lines changed

CoreFoundation/Base.subproj/CFRuntime.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -988,7 +988,7 @@ CF_PRIVATE Boolean __CFInitialized = 0;
988988
// move the next 2 lines down into the #if below, and make it static, after Foundation gets off this symbol on other platforms
989989
CF_EXPORT pthread_t _CFMainPThread;
990990
pthread_t _CFMainPThread = kNilPthreadT;
991-
#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_IPHONESIMULATOR
991+
#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_IPHONESIMULATOR || DEPLOYMENT_TARGET_LINUX
992992

993993
CF_EXPORT pthread_t _CF_pthread_main_thread_np(void);
994994
pthread_t _CF_pthread_main_thread_np(void) {

CoreFoundation/Base.subproj/CoreFoundation_Prefix.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,13 @@ CF_INLINE size_t malloc_size(void *memblock) {
221221
return malloc_usable_size(memblock);
222222
}
223223

224+
#include <time.h>
225+
CF_INLINE uint64_t mach_absolute_time() {
226+
struct timespec ts;
227+
clock_gettime(CLOCK_MONOTONIC, &ts);
228+
return (uint64_t)ts.tv_nsec + (uint64_t)ts.tv_sec * 1000000000UL;
229+
}
230+
224231
#endif
225232

226233
#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX

CoreFoundation/NumberDate.subproj/CFDate.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const CFTimeInterval kCFAbsoluteTimeIntervalSince1904 = 3061152000.0L;
4343
CF_PRIVATE double __CFTSRRate = 0.0;
4444
static double __CF1_TSRRate = 0.0;
4545

46-
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
46+
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
4747

4848
CF_PRIVATE uint64_t __CFTimeIntervalToTSR(CFTimeInterval ti) {
4949
if ((ti * __CFTSRRate) > INT64_MAX / 2) return (INT64_MAX / 2);
@@ -66,7 +66,7 @@ CF_PRIVATE CFTimeInterval __CFTimeIntervalUntilTSR(uint64_t tsr) {
6666

6767
// Technically this is 'TSR units' not a strict 'TSR' absolute time
6868
CF_PRIVATE uint64_t __CFTSRToNanoseconds(uint64_t tsr) {
69-
double tsrInNanoseconds = floor(tsr * __CF1_TSRRate * NSEC_PER_SEC);
69+
double tsrInNanoseconds = floor(tsr * __CF1_TSRRate * 1000000000UL);
7070
uint64_t ns = (uint64_t)tsrInNanoseconds;
7171
return ns;
7272
}

CoreFoundation/RunLoop.subproj/CFRunLoop.c

Lines changed: 198 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,18 @@ DISPATCH_EXPORT void _dispatch_main_queue_callback_4CF(void);
7171

7272
#define AbsoluteTime LARGE_INTEGER
7373

74+
#elif DEPLOYMENT_TARGET_LINUX
75+
#include <dlfcn.h>
76+
#include <poll.h>
77+
#include <sys/epoll.h>
78+
#include <sys/eventfd.h>
79+
#include <sys/timerfd.h>
80+
81+
#define _dispatch_get_main_queue_port_4CF dispatch_get_main_queue_eventfd_np
82+
#define _dispatch_main_queue_callback_4CF(x) dispatch_main_queue_drain_np()
7483
#endif
7584

76-
#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_IPHONESIMULATOR
85+
#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_IPHONESIMULATOR || DEPLOYMENT_TARGET_LINUX
7786
CF_EXPORT pthread_t _CF_pthread_main_thread_np(void);
7887
#define pthread_main_thread_np() _CF_pthread_main_thread_np()
7988
#endif
@@ -117,6 +126,13 @@ static pthread_t kNilPthreadT = { nil, nil };
117126
typedef int kern_return_t;
118127
#define KERN_SUCCESS 0
119128

129+
#elif DEPLOYMENT_TARGET_LINUX
130+
131+
static pthread_t kNilPthreadT = (pthread_t)0;
132+
#define pthreadPointer(a) ((void*)a)
133+
typedef int kern_return_t;
134+
#define KERN_SUCCESS 0
135+
120136
#else
121137

122138
static pthread_t kNilPthreadT = (pthread_t)0;
@@ -424,6 +440,50 @@ static kern_return_t __CFPortSetRemove(__CFPort port, __CFPortSet portSet) {
424440
return KERN_SUCCESS;
425441
}
426442

443+
#elif DEPLOYMENT_TARGET_LINUX
444+
// eventfd/timerfd descriptor
445+
typedef int __CFPort;
446+
#define CFPORT_NULL -1
447+
#define MACH_PORT_NULL CFPORT_NULL
448+
449+
// epoll file descriptor
450+
typedef int __CFPortSet;
451+
#define CFPORTSET_NULL -1
452+
453+
static __CFPort __CFPortAllocate(void) {
454+
return eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK);
455+
}
456+
457+
CF_INLINE void __CFPortFree(__CFPort port) {
458+
close(port);
459+
}
460+
461+
CF_INLINE __CFPortSet __CFPortSetAllocate(void) {
462+
return epoll_create1(EPOLL_CLOEXEC);
463+
}
464+
465+
CF_INLINE kern_return_t __CFPortSetInsert(__CFPort port, __CFPortSet portSet) {
466+
if (CFPORT_NULL == port) {
467+
return -1;
468+
}
469+
struct epoll_event event;
470+
memset(&event, 0, sizeof(event));
471+
event.data.fd = port;
472+
event.events = EPOLLIN|EPOLLET;
473+
474+
return epoll_ctl(portSet, EPOLL_CTL_ADD, port, &event);
475+
}
476+
477+
CF_INLINE kern_return_t __CFPortSetRemove(__CFPort port, __CFPortSet portSet) {
478+
if (CFPORT_NULL == port) {
479+
return -1;
480+
}
481+
return epoll_ctl(portSet, EPOLL_CTL_DEL, port, NULL);
482+
}
483+
484+
CF_INLINE void __CFPortSetFree(__CFPortSet portSet) {
485+
close(portSet);
486+
}
427487
#endif
428488

429489
#if !defined(__MACTYPES__) && !defined(_OS_OSTYPES_H)
@@ -464,6 +524,36 @@ static uint32_t __CFSendTrivialMachMessage(mach_port_t port, uint32_t msg_id, CF
464524
if (result == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header);
465525
return result;
466526
}
527+
#elif DEPLOYMENT_TARGET_LINUX
528+
529+
static int mk_timer_create(void) {
530+
return timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
531+
}
532+
533+
static kern_return_t mk_timer_destroy(int timer) {
534+
return close(timer);
535+
}
536+
537+
static kern_return_t mk_timer_arm(int timer, int64_t expire_time) {
538+
struct itimerspec ts;
539+
ts.it_value.tv_sec = expire_time / 1000000000UL;
540+
ts.it_value.tv_nsec = expire_time % 1000000000UL;
541+
542+
// Non-repeating timer
543+
ts.it_interval.tv_sec = 0;
544+
ts.it_interval.tv_nsec = 0;
545+
546+
return timerfd_settime(timer, TFD_TIMER_ABSTIME, &ts, NULL);
547+
}
548+
549+
static kern_return_t mk_timer_cancel(int timer, const void *unused) {
550+
return mk_timer_arm(timer, 0);
551+
}
552+
553+
CF_INLINE int64_t __CFUInt64ToAbsoluteTime(int64_t x) {
554+
return x;
555+
}
556+
467557
#elif DEPLOYMENT_TARGET_WINDOWS
468558

469559
static HANDLE mk_timer_create(void) {
@@ -547,7 +637,7 @@ struct __CFRunLoopMode {
547637
Boolean _dispatchTimerArmed;
548638
#endif
549639
#if USE_MK_TIMER_TOO
550-
mach_port_t _timerPort;
640+
__CFPort _timerPort;
551641
Boolean _mkTimerArmed;
552642
#endif
553643
#if DEPLOYMENT_TARGET_WINDOWS
@@ -2256,6 +2346,94 @@ static Boolean __CFRunLoopServiceMachPort(mach_port_name_t port, mach_msg_header
22562346
return false;
22572347
}
22582348

2349+
#elif DEPLOYMENT_TARGET_LINUX
2350+
2351+
#define TIMEOUT_INFINITY UINT64_MAX
2352+
2353+
static int __CFPollFileDescriptors(struct pollfd *fds, nfds_t nfds, uint64_t timeout) {
2354+
uint64_t elapsed = 0;
2355+
uint64_t start = mach_absolute_time();
2356+
int result = 0;
2357+
while (1) {
2358+
struct timespec ts = {0};
2359+
struct timespec *tsPtr = &ts;
2360+
if (timeout == TIMEOUT_INFINITY) {
2361+
tsPtr = NULL;
2362+
2363+
} else if (elapsed < timeout) {
2364+
uint64_t delta = timeout - elapsed;
2365+
ts.tv_sec = delta / 1000000000UL;
2366+
ts.tv_nsec = delta % 1000000000UL;
2367+
}
2368+
2369+
result = ppoll(fds, 1, tsPtr, NULL);
2370+
2371+
if (result == -1 && errno == EINTR) {
2372+
uint64_t end = mach_absolute_time();
2373+
elapsed += (end - start);
2374+
start = end;
2375+
2376+
} else {
2377+
return result;
2378+
}
2379+
}
2380+
}
2381+
2382+
// pass in either a portSet or onePort. portSet is an epollfd, onePort is either a timerfd or an eventfd.
2383+
// TODO: Better error handling. What should happen if we get an error on a file descriptor?
2384+
static Boolean __CFRunLoopServiceFileDescriptors(__CFPortSet portSet, __CFPort onePort, uint64_t timeout, int *livePort) {
2385+
struct pollfd fdInfo = {
2386+
.fd = (onePort == CFPORT_NULL) ? portSet : onePort,
2387+
.events = POLLIN
2388+
};
2389+
2390+
ssize_t result = __CFPollFileDescriptors(&fdInfo, 1, timeout);
2391+
if (result == 0)
2392+
return false;
2393+
2394+
CFAssert2(result != -1, __kCFLogAssertion, "%s(): error %d from ppoll", __PRETTY_FUNCTION__, errno);
2395+
2396+
int awokenFd;
2397+
2398+
if (onePort != CFPORT_NULL) {
2399+
CFAssert1(0 == (fdInfo.revents & (POLLERR|POLLHUP)), __kCFLogAssertion, "%s(): ppoll reported error for fd", __PRETTY_FUNCTION__);
2400+
awokenFd = onePort;
2401+
2402+
} else {
2403+
struct epoll_event event;
2404+
do {
2405+
result = epoll_wait(portSet, &event, 1 /*numEvents*/, 0 /*timeout*/);
2406+
} while (result == -1 && errno == EINTR);
2407+
CFAssert2(result >= 0, __kCFLogAssertion, "%s(): error %d from epoll_wait", __PRETTY_FUNCTION__, errno);
2408+
2409+
if (result == 0)
2410+
return false;
2411+
2412+
awokenFd = event.data.fd;
2413+
}
2414+
2415+
// Now we acknowledge the wakeup. awokenFd is an eventfd (or possibly a
2416+
// timerfd ?). In either case, we read an 8-byte integer, as per eventfd(2)
2417+
// and timerfd_create(2).
2418+
uint64_t value;
2419+
do {
2420+
result = read(awokenFd, &value, sizeof(value));
2421+
} while (result == -1 && errno == EINTR);
2422+
2423+
if (result == -1 && errno == EAGAIN) {
2424+
// Another thread stole the wakeup for this fd. (FIXME Can this actually
2425+
// happen?)
2426+
return false;
2427+
}
2428+
2429+
CFAssert2(result == sizeof(value), __kCFLogAssertion, "%s(): error %d from read(2) while acknowledging wakeup", __PRETTY_FUNCTION__, errno);
2430+
2431+
if (livePort)
2432+
*livePort = awokenFd;
2433+
2434+
return true;
2435+
}
2436+
22592437
#elif DEPLOYMENT_TARGET_WINDOWS
22602438

22612439
#define TIMEOUT_INFINITY INFINITE
@@ -2409,6 +2587,8 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
24092587
#elif DEPLOYMENT_TARGET_WINDOWS
24102588
HANDLE livePort = NULL;
24112589
Boolean windowsMessageReceived = false;
2590+
#elif DEPLOYMENT_TARGET_LINUX
2591+
int livePort = -1;
24122592
#endif
24132593
__CFPortSet waitSet = rlm->_portSet;
24142594

@@ -2433,6 +2613,10 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
24332613
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
24342614
goto handle_msg;
24352615
}
2616+
#elif DEPLOYMENT_TARGET_LINUX
2617+
if (__CFRunLoopServiceFileDescriptors(CFPORTSET_NULL, dispatchPort, 0, &livePort)) {
2618+
goto handle_msg;
2619+
}
24362620
#elif DEPLOYMENT_TARGET_WINDOWS
24372621
if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) {
24382622
goto handle_msg;
@@ -2501,6 +2685,8 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
25012685
#elif DEPLOYMENT_TARGET_WINDOWS
25022686
// Here, use the app-supplied message queue mask. They will set this if they are interested in having this run loop receive windows messages.
25032687
__CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived);
2688+
#elif DEPLOYMENT_TARGET_LINUX
2689+
__CFRunLoopServiceFileDescriptors(waitSet, CFPORT_NULL, TIMEOUT_INFINITY, &livePort);
25042690
#endif
25052691

25062692
__CFRunLoopLock(rl);
@@ -2624,7 +2810,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
26242810
(void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
26252811
CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
26262812
}
2627-
#elif DEPLOYMENT_TARGET_WINDOWS
2813+
#elif DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
26282814
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
26292815
#endif
26302816
}
@@ -2740,6 +2926,13 @@ void CFRunLoopWakeUp(CFRunLoopRef rl) {
27402926
* wakeup pending, since the queue length is 1. */
27412927
ret = __CFSendTrivialMachMessage(rl->_wakeUpPort, 0, MACH_SEND_TIMEOUT, 0);
27422928
if (ret != MACH_MSG_SUCCESS && ret != MACH_SEND_TIMED_OUT) CRASH("*** Unable to send message to wake up port. (%d) ***", ret);
2929+
#elif DEPLOYMENT_TARGET_LINUX
2930+
int ret;
2931+
do {
2932+
ret = eventfd_write(rl->_wakeUpPort, 1);
2933+
} while (ret == -1 && errno == EINTR);
2934+
2935+
CFAssert1(0 == ret, __kCFLogAssertion, "%s(): Unable to send wake message to eventfd", __PRETTY_FUNCTION__);
27432936
#elif DEPLOYMENT_TARGET_WINDOWS
27442937
SetEvent(rl->_wakeUpPort);
27452938
#endif
@@ -3278,7 +3471,7 @@ static CFStringRef __CFRunLoopSourceCopyDescription(CFTypeRef cf) { /* DOES CALL
32783471
void *addr = rls->_context.version0.version == 0 ? (void *)rls->_context.version0.perform : (rls->_context.version0.version == 1 ? (void *)rls->_context.version1.perform : NULL);
32793472
#if DEPLOYMENT_TARGET_WINDOWS
32803473
contextDesc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFRunLoopSource context>{version = %ld, info = %p, callout = %p}"), rls->_context.version0.version, rls->_context.version0.info, addr);
3281-
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
3474+
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
32823475
Dl_info info;
32833476
const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???";
32843477
contextDesc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFRunLoopSource context>{version = %ld, info = %p, callout = %s (%p)}"), rls->_context.version0.version, rls->_context.version0.info, name, addr);
@@ -3479,7 +3672,7 @@ static CFStringRef __CFRunLoopObserverCopyDescription(CFTypeRef cf) { /* DOES CA
34793672
}
34803673
#if DEPLOYMENT_TARGET_WINDOWS
34813674
result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFRunLoopObserver %p [%p]>{valid = %s, activities = 0x%x, repeats = %s, order = %d, callout = %p, context = %@}"), cf, CFGetAllocator(rlo), __CFIsValid(rlo) ? "Yes" : "No", rlo->_activities, __CFRunLoopObserverRepeats(rlo) ? "Yes" : "No", rlo->_order, rlo->_callout, contextDesc);
3482-
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
3675+
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
34833676
void *addr = rlo->_callout;
34843677
Dl_info info;
34853678
const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???";

Foundation/NSRunLoop.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,8 @@ extension NSRunLoop {
7171
}
7272

7373
public func runMode(mode: String, beforeDate limitDate: NSDate) -> Bool {
74-
let result: Int32 = CFRunLoopRunSpecific(_cfObject, mode._cfObject, limitDate.timeIntervalSinceNow, false)
75-
let runloopResult = CFRunLoopRunResult(rawValue: result)
76-
return runloopResult == .HandledSource || runloopResult == .TimedOut
74+
let runloopResult = CFRunLoopRunInMode(mode._cfObject, limitDate.timeIntervalSinceNow, false)
75+
return runloopResult == 2 || runloopResult == 3
7776
}
7877

7978
}

build.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
foundation.GCC_PREFIX_HEADER = 'CoreFoundation/Base.subproj/CoreFoundation_Prefix.h'
1515

1616
if Configuration.current.target.sdk == OSType.Linux:
17-
foundation.CFLAGS = '-DDEPLOYMENT_TARGET_LINUX '
17+
foundation.CFLAGS = '-DDEPLOYMENT_TARGET_LINUX -D_GNU_SOURCE '
1818
foundation.LDFLAGS = '-Wl,@./CoreFoundation/linux.ld -lswiftGlibc `icu-config --ldflags` -Wl,-defsym,__CFConstantStringClassReference=_TMC10Foundation19_NSCFConstantString '
1919
elif Configuration.current.target.sdk == OSType.MacOSX:
2020
foundation.CFLAGS = '-DDEPLOYMENT_TARGET_MACOSX '
@@ -208,7 +208,7 @@
208208
'CoreFoundation/Preferences.subproj/CFPreferences.c',
209209
# 'CoreFoundation/RunLoop.subproj/CFMachPort.c',
210210
# 'CoreFoundation/RunLoop.subproj/CFMessagePort.c',
211-
# 'CoreFoundation/RunLoop.subproj/CFRunLoop.c',
211+
'CoreFoundation/RunLoop.subproj/CFRunLoop.c',
212212
# 'CoreFoundation/RunLoop.subproj/CFSocket.c',
213213
# 'CoreFoundation/Stream.subproj/CFConcreteStreams.c',
214214
# 'CoreFoundation/Stream.subproj/CFSocketStream.c',
@@ -242,6 +242,7 @@
242242

243243
swift_sources = CompileSwiftSources([
244244
'Foundation/NSObject.swift',
245+
'Foundation/NSRunLoop.swift',
245246
'Foundation/NSAffineTransform.swift',
246247
'Foundation/NSArray.swift',
247248
'Foundation/NSAttributedString.swift',
@@ -303,7 +304,6 @@
303304
'Foundation/NSPropertyList.swift',
304305
'Foundation/NSRange.swift',
305306
'Foundation/NSRegularExpression.swift',
306-
# 'Foundation/NSRunLoop.swift',
307307
'Foundation/NSScanner.swift',
308308
'Foundation/NSSet.swift',
309309
'Foundation/NSSortDescriptor.swift',
@@ -313,7 +313,7 @@
313313
'Foundation/NSTask.swift',
314314
'Foundation/NSTextCheckingResult.swift',
315315
'Foundation/NSThread.swift',
316-
# 'Foundation/NSTimer.swift',
316+
'Foundation/NSTimer.swift',
317317
'Foundation/NSTimeZone.swift',
318318
'Foundation/NSURL.swift',
319319
'Foundation/NSURLAuthenticationChallenge.swift',

0 commit comments

Comments
 (0)