Skip to content

Commit

Permalink
port to macOS
Browse files Browse the repository at this point in the history
  • Loading branch information
jiixyj committed Jul 11, 2022
1 parent 4b9f7ce commit 36f34cb
Show file tree
Hide file tree
Showing 27 changed files with 700 additions and 46 deletions.
3 changes: 3 additions & 0 deletions external/tree-macros/CMakeLists.txt
Expand Up @@ -4,6 +4,9 @@ project(tree-macros LANGUAGES C)
add_library(tree-macros INTERFACE)
target_include_directories(tree-macros
INTERFACE "${CMAKE_CURRENT_LIST_DIR}/include")
if(APPLE)
target_compile_definitions(tree-macros INTERFACE __uintptr_t=uintptr_t)
endif()

#

Expand Down
32 changes: 25 additions & 7 deletions src/CMakeLists.txt
Expand Up @@ -4,6 +4,9 @@ set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)

find_package(tree-macros REQUIRED)
find_package(queue-macros REQUIRED)

add_library(rwlock OBJECT rwlock.c)
set_property(TARGET rwlock PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(rwlock PUBLIC Threads::Threads)
Expand All @@ -18,11 +21,15 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
return()
endif()

find_package(tree-macros REQUIRED)
find_package(queue-macros REQUIRED)
add_library(wrap OBJECT wrap.c)
set_property(TARGET wrap PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(wrap PUBLIC Threads::Threads)
target_include_directories(wrap
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>)

macro(add_compat_target _name _condition)
add_library(compat_${_name} OBJECT compat_${_name}.c)
target_link_libraries(compat_${_name} PUBLIC wrap)
set_property(TARGET compat_${_name} PROPERTY POSITION_INDEPENDENT_CODE ON)
target_compile_options(
compat_${_name}
Expand All @@ -39,12 +46,13 @@ macro(add_compat_target _name _condition)
endmacro()

include(CheckSymbolExists)

# FreeBSD 13 and NetBSD 10 support native eventfd descriptors. NetBSD 10
# supports native timerfd descriptors. Prefer them if available.
check_symbol_exists(eventfd "sys/eventfd.h" HAVE_EVENTFD)
check_symbol_exists(timerfd_create "sys/timerfd.h" HAVE_TIMERFD)

check_symbol_exists(kqueue1 "sys/event.h" HAVE_KQUEUE1)
check_symbol_exists(kqueue1 "sys/event.h;sys/time.h" HAVE_KQUEUE1)
add_compat_target(kqueue1 "NOT;HAVE_KQUEUE1")
check_symbol_exists(sigandset "signal.h" HAVE_SIGANDSET)
check_symbol_exists(sigorset "signal.h" HAVE_SIGORSET)
Expand All @@ -71,6 +79,14 @@ if(NOT ALLOWS_ONESHOT_TIMERS_WITH_TIMEOUT_ZERO)
evfilt_timer_quirks
INTERFACE QUIRK_EVFILT_TIMER_DISALLOWS_ONESHOT_TIMEOUT_ZERO)
endif()
add_compat_target(pipe2 "APPLE")
add_compat_target(socket "APPLE")
add_compat_target(socketpair "APPLE")
add_compat_target(itimerspec "APPLE")
add_compat_target(sem "APPLE")
add_compat_target(ppoll "APPLE")

target_link_libraries(rwlock PUBLIC $<BUILD_INTERFACE:compat_enable_sem>)

add_library(
epoll-shim
Expand All @@ -80,8 +96,7 @@ add_library(
kqueue_event.c
signalfd.c
signalfd_ctx.c
timespec_util.c
wrap.c)
timespec_util.c)
if(NOT HAVE_EVENTFD)
target_sources(epoll-shim PRIVATE eventfd.c eventfd_ctx.c)
endif()
Expand All @@ -94,11 +109,14 @@ target_link_libraries(
epoll-shim
PRIVATE Threads::Threads #
$<BUILD_INTERFACE:queue-macros::queue-macros>
$<BUILD_INTERFACE:tree-macros::tree-macros>
$<BUILD_INTERFACE:tree-macros::tree-macros> #
$<BUILD_INTERFACE:evfilt_timer_quirks>
$<BUILD_INTERFACE:compat_enable_kqueue1>
$<BUILD_INTERFACE:compat_enable_ppoll>
$<BUILD_INTERFACE:compat_enable_itimerspec>
$<BUILD_INTERFACE:compat_enable_sigops>
$<BUILD_INTERFACE:rwlock>)
$<BUILD_INTERFACE:rwlock>
$<BUILD_INTERFACE:wrap>)
if(HAVE_TIMERFD)
target_compile_definitions(epoll-shim PRIVATE HAVE_TIMERFD)
endif()
Expand Down
1 change: 1 addition & 0 deletions src/compat_itimerspec.c
@@ -0,0 +1 @@
#include "compat_itimerspec.h"
14 changes: 14 additions & 0 deletions src/compat_itimerspec.h
@@ -0,0 +1,14 @@
#ifndef COMPAT_ITIMERSPEC_H
#define COMPAT_ITIMERSPEC_H

#ifdef COMPAT_ENABLE_ITIMERSPEC

#include <time.h>

struct itimerspec {
struct timespec it_interval;
struct timespec it_value;
};
#endif

#endif
9 changes: 9 additions & 0 deletions src/compat_kqueue1.c
Expand Up @@ -38,6 +38,15 @@ compat_kqueue1_impl(int *fd_out, int flags)
goto out;
}
}
#ifdef __APPLE__
else {
if ((r = fcntl(fd, F_GETFD, 0)) < 0 ||
fcntl(fd, F_SETFD, r & ~FD_CLOEXEC) < 0) {
ec = errno;
goto out;
}
}
#endif

if (flags & O_NONBLOCK) {
if ((r = real_fcntl(fd, F_GETFL)) < 0) {
Expand Down
68 changes: 68 additions & 0 deletions src/compat_pipe2.c
@@ -0,0 +1,68 @@
#include "compat_pipe2.h"

#include <errno.h>
#include <stdlib.h>

#include <unistd.h>

#include "wrap.h"

static errno_t
compat_pipe2_impl(int pipefd[2], int flags)
{
errno_t ec;

if (flags & ~(O_CLOEXEC | O_NONBLOCK)) {
return EINVAL;
}

int p[2];
if (pipe(p) < 0) {
return errno;
}

{
int r;

if (flags & O_NONBLOCK) {
if ((r = fcntl(p[0], F_GETFL, 0)) < 0 ||
fcntl(p[0], F_SETFL, r | O_NONBLOCK) < 0 ||
(r = fcntl(p[1], F_GETFL, 0)) < 0 ||
fcntl(p[1], F_SETFL, r | O_NONBLOCK) < 0) {
ec = errno;
goto out;
}
}

if (flags & O_CLOEXEC) {
if ((r = fcntl(p[0], F_GETFD, 0)) < 0 ||
fcntl(p[0], F_SETFD, r | FD_CLOEXEC) < 0 ||
(r = fcntl(p[1], F_GETFD, 0)) < 0 ||
fcntl(p[1], F_SETFD, r | FD_CLOEXEC) < 0) {
ec = errno;
goto out;
}
}
}

pipefd[0] = p[0];
pipefd[1] = p[1];

return 0;

out:
(void)real_close(p[0]);
(void)real_close(p[1]);
return ec;
}

int
compat_pipe2(int pipefd[2], int flags)
{
errno_t ec = compat_pipe2_impl(pipefd, flags);
if (ec != 0) {
errno = ec;
return -1;
}
return 0;
}
12 changes: 12 additions & 0 deletions src/compat_pipe2.h
@@ -0,0 +1,12 @@
#ifndef COMPAT_PIPE2_H
#define COMPAT_PIPE2_H

#include <fcntl.h>
#include <unistd.h>

int compat_pipe2(int pipefd[2], int flags);
#ifdef COMPAT_ENABLE_PIPE2
#define pipe2 compat_pipe2
#endif

#endif
181 changes: 181 additions & 0 deletions src/compat_ppoll.c
@@ -0,0 +1,181 @@
#include "compat_ppoll.h"

#include <sys/types.h>

#include <sys/event.h>

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>

#include "wrap.h"

static errno_t
compat_ppoll_impl(struct pollfd fds[], nfds_t nfds,
struct timespec const *restrict timeout, sigset_t const *restrict sigmask,
int *n)
{
errno_t ec;

int timeout_ms = -1;
if (timeout != NULL) {
if (timeout->tv_sec < 0 || timeout->tv_nsec < 0 ||
timeout->tv_nsec >= 1000000000) {
return EINVAL;
}

timeout_ms = timeout->tv_nsec / 1000000;
if (timeout->tv_nsec % 1000000) {
++timeout_ms;
}

int sec_ms;
if (__builtin_mul_overflow(timeout->tv_sec, 1000, &sec_ms) ||
__builtin_add_overflow(sec_ms, timeout_ms, &timeout_ms)) {
timeout_ms = -1;
}
}

struct kevent kevs[NSIG];
int kevs_length = 0;

sigset_t origmask;

if (timeout_ms != 0 && sigmask != NULL) {
if ((ec = pthread_sigmask(SIG_SETMASK, /**/
NULL, &origmask)) != 0) {
return ec;
}

for (int i = 1; i < NSIG; ++i) {
if (i == SIGKILL || i == SIGSTOP) {
continue;
}

if (!sigismember(&origmask, i) ||
sigismember(sigmask, i)) {
continue;
}

struct sigaction act;
if (sigaction(i, NULL, &act) < 0) {
return errno;
}

if (!(act.sa_flags & SA_SIGINFO) &&
(act.sa_handler == SIG_DFL ||
act.sa_handler == SIG_IGN)) {
continue;
}

if (act.sa_flags & SA_RESTART) {
continue;
}

EV_SET(&kevs[kevs_length++], i, EVFILT_SIGNAL,
EV_ADD | EV_ONESHOT, 0, 0, 0);
}
}

int kq = -1;
struct pollfd *fds2 = NULL;

if (kevs_length > 0) {
kq = kqueue();
if (kq < 0) {
return errno;
}

if (kevent(kq, kevs, kevs_length, NULL, 0, NULL) < 0) {
ec = errno;
goto out;
}

#ifdef EVFILT_USER
sigset_t pending;
if (sigpending(&pending) < 0) {
ec = errno;
goto out;
}
for (int i = 0; i < kevs_length; ++i) {
if (sigismember(&pending, (int)kevs[i].ident)) {
EV_SET(&kevs[0], 0, EVFILT_USER, /**/
EV_ADD | EV_ONESHOT, 0, 0, 0);
EV_SET(&kevs[1], 0, EVFILT_USER, /**/
0, NOTE_TRIGGER, 0, 0);
if (kevent(kq, kevs, 2, NULL, 0, NULL) < 0) {
ec = errno;
goto out;
}
break;
}
}
#endif

size_t pfds2_bytes;
if (__builtin_add_overflow(nfds, 1, &pfds2_bytes) ||
__builtin_mul_overflow(pfds2_bytes, sizeof(struct pollfd),
&pfds2_bytes)) {
ec = EINVAL;
goto out;
}

fds2 = malloc(pfds2_bytes);
if (!fds2) {
ec = errno;
goto out;
}
memcpy(fds2, fds, nfds * sizeof(struct pollfd));
fds2[nfds] = (struct pollfd) {
.fd = kq,
.events = POLLIN,
};
}

if (sigmask != NULL) {
(void)pthread_sigmask(SIG_SETMASK, sigmask, &origmask);
}

int ready = real_poll(fds2 ? fds2 : fds, nfds + !!(fds2), timeout_ms);
ec = ready < 0 ? errno : 0;

if (sigmask != NULL) {
(void)pthread_sigmask(SIG_SETMASK, &origmask, NULL);
}

if (ec == 0 && fds2 && fds2[nfds].revents) {
--ready;
if (ready == 0) {
ec = EINTR;
}
}

if (ec == 0) {
*n = ready;
if (fds2) {
memcpy(fds, fds2, nfds * sizeof(struct pollfd));
}
}

out:
free(fds2);
if (kq >= 0) {
real_close(kq);
}
return ec;
}

int
compat_ppoll(struct pollfd fds[], nfds_t nfds,
struct timespec const *restrict timeout, sigset_t const *restrict sigmask)
{
int n;
errno_t ec = compat_ppoll_impl(fds, nfds, timeout, sigmask, &n);
if (ec != 0) {
errno = ec;
return -1;
}
return n;
}

0 comments on commit 36f34cb

Please sign in to comment.