Skip to content

Commit

Permalink
feat: add kevent for macos compatibility
Browse files Browse the repository at this point in the history
- Make nix env macos compatible
  • Loading branch information
steve-chavez committed Nov 3, 2024
1 parent ce41338 commit 4f0d5e5
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 12 deletions.
16 changes: 15 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ on: [push, pull_request]
jobs:

test:

runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -22,3 +21,18 @@ jobs:

#- name: Check C files are formatted
#run: nix-shell --run "net-check-format"

test-on-macos:
runs-on: macos-13

strategy:
matrix:
pg-version: ['17']

steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Run tests
run: nix-shell --run "net-with-nginx net-with-pg-${{ matrix.pg-version }} python -m pytest -vv"
2 changes: 1 addition & 1 deletion nix/pg_net.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ stdenv.mkDerivation {

installPhase = ''
mkdir -p $out/bin
install -D pg_net.so -t $out/lib
install -D *.{dylib,so} -t $out/lib
install -D -t $out/share/postgresql/extension sql/*.sql
install -D -t $out/share/postgresql/extension pg_net.control
Expand Down
2 changes: 1 addition & 1 deletion nix/postgresql/generic.nix
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ let
wrapProgram $out/bin/initdb --prefix PATH ":" ${glibc.bin}/bin
'';

doCheck = !stdenv'.isDarwin;
doCheck = false;
# autodetection doesn't seem to able to find this, but it's there.
checkTarget = "check";

Expand Down
5 changes: 3 additions & 2 deletions shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ mkShell {
pythonDeps
format.do format.doCheck
nginxCustom.nginxScript
gdbScript
] ++ nixopsScripts;
] ++
nixopsScripts ++
lib.optional stdenv.isLinux [gdbScript];
shellHook = ''
export HISTFILE=.history
'';
Expand Down
119 changes: 114 additions & 5 deletions src/event.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
#include <postgres.h>
#include <curl/multi.h>
#include <stdbool.h>
#include <stddef.h>
#include <errno.h>
#include <unistd.h>

#include <sys/epoll.h>
#include <sys/timerfd.h>

#include "event.h"

#ifdef WAIT_USE_EPOLL

static int timerfd = 0;
static bool timer_created = false;

Expand Down Expand Up @@ -120,3 +119,113 @@ int get_curl_event(event ev){
int get_socket_fd(event ev){
return ev.data.fd;
}

#else

typedef struct {
curl_socket_t sockfd;
int action;
} SocketInfo ;

int inline wait_event(int fd, event *events, size_t maxevents, int wait_milliseconds){
return kevent(fd, NULL, 0, events, maxevents, &(struct timespec){.tv_sec = wait_milliseconds/1000});
}

int inline event_monitor(){
return kqueue();
}

void ev_monitor_close(LoopState *lstate){
close(lstate->epfd);
}

int multi_timer_cb(CURLM *multi, long timeout_ms, LoopState *lstate) {
elog(DEBUG2, "multi_timer_cb: Setting timeout to %ld ms\n", timeout_ms);
event timer_event;
int id = 1;

if (timeout_ms > 0) {
EV_SET(&timer_event, id, EVFILT_TIMER, EV_ADD, 0, timeout_ms, NULL); //0 means milliseconds (the default)
} else if (timeout_ms == 0){
/* libcurl wants us to timeout now, however setting both fields of
* new_value.it_value to zero disarms the timer. The closest we can
* do is to schedule the timer to fire in 1 ns. */
EV_SET(&timer_event, id, EVFILT_TIMER, EV_ADD, NOTE_NSECONDS, 1, NULL);
} else {
// libcurl passes a -1 to indicate the timer should be deleted
EV_SET(&timer_event, id, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
}

if (kevent(lstate->epfd, &timer_event, 1, NULL, 0, NULL) < 0) {
int save_errno = errno;
ereport(ERROR, errmsg("kevent with EVFILT_TIMER failed: %s", strerror(save_errno)));
}

return 0;
}

int multi_socket_cb(CURL *easy, curl_socket_t sockfd, int what, LoopState *lstate, void *socketp) {
static char *whatstrs[] = { "NONE", "CURL_POLL_IN", "CURL_POLL_OUT", "CURL_POLL_INOUT", "CURL_POLL_REMOVE" };
elog(DEBUG2, "multi_socket_cb: sockfd %d received %s", sockfd, whatstrs[what]);

SocketInfo *sock_info = (SocketInfo *)socketp;
struct kevent ev[2];
int count = 0;

if (what == CURL_POLL_REMOVE) {
if (sock_info->action & CURL_POLL_IN)
EV_SET(&ev[count++], sockfd, EVFILT_READ, EV_DELETE, 0, 0, sock_info);

if (sock_info->action & CURL_POLL_OUT)
EV_SET(&ev[count++], sockfd, EVFILT_WRITE, EV_DELETE, 0, 0, sock_info);

curl_multi_assign(lstate->curl_mhandle, sockfd, NULL);
pfree(sock_info);
} else {
if (!sock_info) {
sock_info = palloc(sizeof(SocketInfo));
sock_info->sockfd = sockfd;
sock_info->action = what;
curl_multi_assign(lstate->curl_mhandle, sockfd, sock_info);
}

if (what & CURL_POLL_IN)
EV_SET(&ev[count++], sockfd, EVFILT_READ, EV_ADD, 0, 0, sock_info);

if (what & CURL_POLL_OUT)
EV_SET(&ev[count++], sockfd, EVFILT_WRITE, EV_ADD, 0, 0, sock_info);
}

Assert(count <= 2);

if (kevent(lstate->epfd, &ev[0], count, NULL, 0, NULL) < 0) {
int save_errno = errno;
ereport(ERROR, errmsg("kevent with %s failed for sockfd %d: %s", whatstrs[what], sockfd, strerror(save_errno)));
}

return 0;
}

bool is_timer(event ev){
return ev.filter == EVFILT_TIMER;
}

int get_curl_event(event ev){
int ev_bitmask = 0;
if (ev.filter == EVFILT_READ)
ev_bitmask |= CURL_CSELECT_IN;
else if (ev.filter == EVFILT_WRITE)
ev_bitmask |= CURL_CSELECT_OUT;
else
ev_bitmask = CURL_CSELECT_ERR;

return ev_bitmask;
}

int get_socket_fd(event ev){
SocketInfo *sock_info = (SocketInfo *) ev.udata;

return sock_info->sockfd;
}

#endif
21 changes: 19 additions & 2 deletions src/event.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,29 @@

#include <curl/multi.h>

#include <sys/epoll.h>

#include "core.h"

#ifdef __linux__
#define WAIT_USE_EPOLL
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#define WAIT_USE_KQUEUE
#else
#error "no event wait implementation available"
#endif

#ifdef WAIT_USE_EPOLL

#include <sys/epoll.h>
#include <sys/timerfd.h>
typedef struct epoll_event event;

#else

#include <sys/event.h>
typedef struct kevent event;

#endif

int wait_event(int fd, event *events, size_t maxevents, int wait_milliseconds);
int event_monitor(void);
void ev_monitor_close(LoopState *lstate);
Expand Down

0 comments on commit 4f0d5e5

Please sign in to comment.