Skip to content

Implement NSRunLoop #133

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 4 commits into from
Dec 16, 2015
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
2 changes: 1 addition & 1 deletion CoreFoundation/Base.subproj/CFRuntime.c
Original file line number Diff line number Diff line change
Expand Up @@ -990,7 +990,7 @@ CF_PRIVATE Boolean __CFInitialized = 0;
// move the next 2 lines down into the #if below, and make it static, after Foundation gets off this symbol on other platforms
CF_EXPORT pthread_t _CFMainPThread;
pthread_t _CFMainPThread = kNilPthreadT;
#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_IPHONESIMULATOR
#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_IPHONESIMULATOR || DEPLOYMENT_TARGET_LINUX

CF_EXPORT pthread_t _CF_pthread_main_thread_np(void);
pthread_t _CF_pthread_main_thread_np(void) {
Expand Down
7 changes: 7 additions & 0 deletions CoreFoundation/Base.subproj/CoreFoundation_Prefix.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,13 @@ CF_INLINE size_t malloc_size(void *memblock) {
return malloc_usable_size(memblock);
}

#include <time.h>
CF_INLINE uint64_t mach_absolute_time() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_nsec + (uint64_t)ts.tv_sec * 1000000000UL;
}

#endif

#if DEPLOYMENT_TARGET_FREEBSD
Expand Down
4 changes: 2 additions & 2 deletions CoreFoundation/NumberDate.subproj/CFDate.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const CFTimeInterval kCFAbsoluteTimeIntervalSince1904 = 3061152000.0L;
CF_PRIVATE double __CFTSRRate = 0.0;
static double __CF1_TSRRate = 0.0;

#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX

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

// Technically this is 'TSR units' not a strict 'TSR' absolute time
CF_PRIVATE uint64_t __CFTSRToNanoseconds(uint64_t tsr) {
double tsrInNanoseconds = floor(tsr * __CF1_TSRRate * NSEC_PER_SEC);
double tsrInNanoseconds = floor(tsr * __CF1_TSRRate * 1000000000UL);
uint64_t ns = (uint64_t)tsrInNanoseconds;
return ns;
}
Expand Down
204 changes: 199 additions & 5 deletions CoreFoundation/RunLoop.subproj/CFRunLoop.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,18 @@ DISPATCH_EXPORT void _dispatch_main_queue_callback_4CF(void);

#define AbsoluteTime LARGE_INTEGER

#elif DEPLOYMENT_TARGET_LINUX
#include <dlfcn.h>
#include <poll.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/timerfd.h>

#define _dispatch_get_main_queue_port_4CF dispatch_get_main_queue_eventfd_np
#define _dispatch_main_queue_callback_4CF(x) dispatch_main_queue_drain_np()
#endif

#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_IPHONESIMULATOR
#if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_IPHONESIMULATOR || DEPLOYMENT_TARGET_LINUX
CF_EXPORT pthread_t _CF_pthread_main_thread_np(void);
#define pthread_main_thread_np() _CF_pthread_main_thread_np()
#endif
Expand Down Expand Up @@ -117,6 +126,13 @@ static pthread_t kNilPthreadT = { nil, nil };
typedef int kern_return_t;
#define KERN_SUCCESS 0

#elif DEPLOYMENT_TARGET_LINUX

static pthread_t kNilPthreadT = (pthread_t)0;
#define pthreadPointer(a) ((void*)a)
typedef int kern_return_t;
#define KERN_SUCCESS 0

#else

static pthread_t kNilPthreadT = (pthread_t)0;
Expand Down Expand Up @@ -424,6 +440,50 @@ static kern_return_t __CFPortSetRemove(__CFPort port, __CFPortSet portSet) {
return KERN_SUCCESS;
}

#elif DEPLOYMENT_TARGET_LINUX
// eventfd/timerfd descriptor
typedef int __CFPort;
#define CFPORT_NULL -1
#define MACH_PORT_NULL CFPORT_NULL

// epoll file descriptor
typedef int __CFPortSet;
#define CFPORTSET_NULL -1

static __CFPort __CFPortAllocate(void) {
return eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK);
}

CF_INLINE void __CFPortFree(__CFPort port) {
close(port);
}

CF_INLINE __CFPortSet __CFPortSetAllocate(void) {
return epoll_create1(EPOLL_CLOEXEC);
}

CF_INLINE kern_return_t __CFPortSetInsert(__CFPort port, __CFPortSet portSet) {
if (CFPORT_NULL == port) {
return -1;
}
struct epoll_event event;
memset(&event, 0, sizeof(event));
event.data.fd = port;
event.events = EPOLLIN|EPOLLET;

return epoll_ctl(portSet, EPOLL_CTL_ADD, port, &event);
}

CF_INLINE kern_return_t __CFPortSetRemove(__CFPort port, __CFPortSet portSet) {
if (CFPORT_NULL == port) {
return -1;
}
return epoll_ctl(portSet, EPOLL_CTL_DEL, port, NULL);
}

CF_INLINE void __CFPortSetFree(__CFPortSet portSet) {
close(portSet);
}
#endif

#if !defined(__MACTYPES__) && !defined(_OS_OSTYPES_H)
Expand Down Expand Up @@ -464,6 +524,36 @@ static uint32_t __CFSendTrivialMachMessage(mach_port_t port, uint32_t msg_id, CF
if (result == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header);
return result;
}
#elif DEPLOYMENT_TARGET_LINUX

static int mk_timer_create(void) {
return timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
}

static kern_return_t mk_timer_destroy(int timer) {
return close(timer);
}

static kern_return_t mk_timer_arm(int timer, int64_t expire_time) {
struct itimerspec ts;
ts.it_value.tv_sec = expire_time / 1000000000UL;
ts.it_value.tv_nsec = expire_time % 1000000000UL;

// Non-repeating timer
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = 0;

return timerfd_settime(timer, TFD_TIMER_ABSTIME, &ts, NULL);
}

static kern_return_t mk_timer_cancel(int timer, const void *unused) {
return mk_timer_arm(timer, 0);
}

CF_INLINE int64_t __CFUInt64ToAbsoluteTime(int64_t x) {
return x;
}

#elif DEPLOYMENT_TARGET_WINDOWS

static HANDLE mk_timer_create(void) {
Expand Down Expand Up @@ -547,7 +637,7 @@ struct __CFRunLoopMode {
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
mach_port_t _timerPort;
__CFPort _timerPort;
Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
Expand Down Expand Up @@ -2256,6 +2346,95 @@ static Boolean __CFRunLoopServiceMachPort(mach_port_name_t port, mach_msg_header
return false;
}

#elif DEPLOYMENT_TARGET_LINUX

#define TIMEOUT_INFINITY UINT64_MAX

static int __CFPollFileDescriptors(struct pollfd *fds, nfds_t nfds, uint64_t timeout) {
uint64_t elapsed = 0;
uint64_t start = mach_absolute_time();
int result = 0;
while (1) {
struct timespec ts = {0};
struct timespec *tsPtr = &ts;
if (timeout == TIMEOUT_INFINITY) {
tsPtr = NULL;

} else if (elapsed < timeout) {
uint64_t delta = timeout - elapsed;
ts.tv_sec = delta / 1000000000UL;
ts.tv_nsec = delta % 1000000000UL;
}

result = ppoll(fds, 1, tsPtr, NULL);

if (result == -1 && errno == EINTR) {
uint64_t end = mach_absolute_time();
elapsed += (end - start);
start = end;

} else {
return result;
}
}
}

// pass in either a portSet or onePort. portSet is an epollfd, onePort is either a timerfd or an eventfd.
// TODO: Better error handling. What should happen if we get an error on a file descriptor?
static Boolean __CFRunLoopServiceFileDescriptors(__CFPortSet portSet, __CFPort onePort, uint64_t timeout, int *livePort) {
struct pollfd fdInfo = {
.fd = (onePort == CFPORT_NULL) ? portSet : onePort,
.events = POLLIN
};

ssize_t result = __CFPollFileDescriptors(&fdInfo, 1, timeout);
if (result == 0)
return false;

CFAssert2(result != -1, __kCFLogAssertion, "%s(): error %d from ppoll", __PRETTY_FUNCTION__, errno);

int awokenFd;

if (onePort != CFPORT_NULL) {
CFAssert1(0 == (fdInfo.revents & (POLLERR|POLLHUP)), __kCFLogAssertion, "%s(): ppoll reported error for fd", __PRETTY_FUNCTION__);
awokenFd = onePort;

} else {
struct epoll_event event;
do {
result = epoll_wait(portSet, &event, 1 /*numEvents*/, 0 /*timeout*/);
} while (result == -1 && errno == EINTR);
CFAssert2(result >= 0, __kCFLogAssertion, "%s(): error %d from epoll_wait", __PRETTY_FUNCTION__, errno);

if (result == 0) {
return false;
Copy link
Contributor

Choose a reason for hiding this comment

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

style note - please use { } instead of dangling if statements here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

}

awokenFd = event.data.fd;
}

// Now we acknowledge the wakeup. awokenFd is an eventfd (or possibly a
// timerfd ?). In either case, we read an 8-byte integer, as per eventfd(2)
// and timerfd_create(2).
uint64_t value;
do {
result = read(awokenFd, &value, sizeof(value));
} while (result == -1 && errno == EINTR);

if (result == -1 && errno == EAGAIN) {
// Another thread stole the wakeup for this fd. (FIXME Can this actually
// happen?)
return false;
}

CFAssert2(result == sizeof(value), __kCFLogAssertion, "%s(): error %d from read(2) while acknowledging wakeup", __PRETTY_FUNCTION__, errno);

if (livePort)
*livePort = awokenFd;

return true;
}

#elif DEPLOYMENT_TARGET_WINDOWS

#define TIMEOUT_INFINITY INFINITE
Expand Down Expand Up @@ -2409,6 +2588,8 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
#elif DEPLOYMENT_TARGET_WINDOWS
HANDLE livePort = NULL;
Boolean windowsMessageReceived = false;
#elif DEPLOYMENT_TARGET_LINUX
int livePort = -1;
#endif
__CFPortSet waitSet = rlm->_portSet;

Expand All @@ -2433,6 +2614,10 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;
}
#elif DEPLOYMENT_TARGET_LINUX
if (__CFRunLoopServiceFileDescriptors(CFPORTSET_NULL, dispatchPort, 0, &livePort)) {
goto handle_msg;
}
#elif DEPLOYMENT_TARGET_WINDOWS
if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) {
goto handle_msg;
Expand Down Expand Up @@ -2501,6 +2686,8 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
#elif DEPLOYMENT_TARGET_WINDOWS
// Here, use the app-supplied message queue mask. They will set this if they are interested in having this run loop receive windows messages.
__CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived);
#elif DEPLOYMENT_TARGET_LINUX
__CFRunLoopServiceFileDescriptors(waitSet, CFPORT_NULL, TIMEOUT_INFINITY, &livePort);
#endif

__CFRunLoopLock(rl);
Expand Down Expand Up @@ -2624,7 +2811,7 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter
(void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
}
#elif DEPLOYMENT_TARGET_WINDOWS
#elif DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
#endif
}
Expand Down Expand Up @@ -2740,6 +2927,13 @@ void CFRunLoopWakeUp(CFRunLoopRef rl) {
* wakeup pending, since the queue length is 1. */
ret = __CFSendTrivialMachMessage(rl->_wakeUpPort, 0, MACH_SEND_TIMEOUT, 0);
if (ret != MACH_MSG_SUCCESS && ret != MACH_SEND_TIMED_OUT) CRASH("*** Unable to send message to wake up port. (%d) ***", ret);
#elif DEPLOYMENT_TARGET_LINUX
int ret;
do {
ret = eventfd_write(rl->_wakeUpPort, 1);
} while (ret == -1 && errno == EINTR);

CFAssert1(0 == ret, __kCFLogAssertion, "%s(): Unable to send wake message to eventfd", __PRETTY_FUNCTION__);
#elif DEPLOYMENT_TARGET_WINDOWS
SetEvent(rl->_wakeUpPort);
#endif
Expand Down Expand Up @@ -3278,7 +3472,7 @@ static CFStringRef __CFRunLoopSourceCopyDescription(CFTypeRef cf) { /* DOES CALL
void *addr = rls->_context.version0.version == 0 ? (void *)rls->_context.version0.perform : (rls->_context.version0.version == 1 ? (void *)rls->_context.version1.perform : NULL);
#if DEPLOYMENT_TARGET_WINDOWS
contextDesc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFRunLoopSource context>{version = %ld, info = %p, callout = %p}"), rls->_context.version0.version, rls->_context.version0.info, addr);
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
Dl_info info;
const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???";
contextDesc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFRunLoopSource context>{version = %ld, info = %p, callout = %s (%p)}"), rls->_context.version0.version, rls->_context.version0.info, name, addr);
Expand Down Expand Up @@ -3479,7 +3673,7 @@ static CFStringRef __CFRunLoopObserverCopyDescription(CFTypeRef cf) { /* DOES CA
}
#if DEPLOYMENT_TARGET_WINDOWS
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);
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
void *addr = rlo->_callout;
Dl_info info;
const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???";
Expand Down
Loading