Skip to content

Commit 4f0d5e5

Browse files
committed
feat: add kevent for macos compatibility
- Make nix env macos compatible
1 parent ce41338 commit 4f0d5e5

File tree

6 files changed

+153
-12
lines changed

6 files changed

+153
-12
lines changed

.github/workflows/main.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ on: [push, pull_request]
55
jobs:
66

77
test:
8-
98
runs-on: ubuntu-latest
109
strategy:
1110
matrix:
@@ -22,3 +21,18 @@ jobs:
2221

2322
#- name: Check C files are formatted
2423
#run: nix-shell --run "net-check-format"
24+
25+
test-on-macos:
26+
runs-on: macos-13
27+
28+
strategy:
29+
matrix:
30+
pg-version: ['17']
31+
32+
steps:
33+
- uses: actions/checkout@v4
34+
- uses: cachix/install-nix-action@v30
35+
with:
36+
nix_path: nixpkgs=channel:nixos-unstable
37+
- name: Run tests
38+
run: nix-shell --run "net-with-nginx net-with-pg-${{ matrix.pg-version }} python -m pytest -vv"

nix/pg_net.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ stdenv.mkDerivation {
1212

1313
installPhase = ''
1414
mkdir -p $out/bin
15-
install -D pg_net.so -t $out/lib
15+
install -D *.{dylib,so} -t $out/lib
1616
1717
install -D -t $out/share/postgresql/extension sql/*.sql
1818
install -D -t $out/share/postgresql/extension pg_net.control

nix/postgresql/generic.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ let
196196
wrapProgram $out/bin/initdb --prefix PATH ":" ${glibc.bin}/bin
197197
'';
198198

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

shell.nix

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ mkShell {
3939
pythonDeps
4040
format.do format.doCheck
4141
nginxCustom.nginxScript
42-
gdbScript
43-
] ++ nixopsScripts;
42+
] ++
43+
nixopsScripts ++
44+
lib.optional stdenv.isLinux [gdbScript];
4445
shellHook = ''
4546
export HISTFILE=.history
4647
'';

src/event.c

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
#include <postgres.h>
2-
#include <curl/multi.h>
3-
#include <stdbool.h>
2+
#include <stddef.h>
3+
#include <errno.h>
44
#include <unistd.h>
55

6-
#include <sys/epoll.h>
7-
#include <sys/timerfd.h>
8-
96
#include "event.h"
107

8+
#ifdef WAIT_USE_EPOLL
9+
1110
static int timerfd = 0;
1211
static bool timer_created = false;
1312

@@ -120,3 +119,113 @@ int get_curl_event(event ev){
120119
int get_socket_fd(event ev){
121120
return ev.data.fd;
122121
}
122+
123+
#else
124+
125+
typedef struct {
126+
curl_socket_t sockfd;
127+
int action;
128+
} SocketInfo ;
129+
130+
int inline wait_event(int fd, event *events, size_t maxevents, int wait_milliseconds){
131+
return kevent(fd, NULL, 0, events, maxevents, &(struct timespec){.tv_sec = wait_milliseconds/1000});
132+
}
133+
134+
int inline event_monitor(){
135+
return kqueue();
136+
}
137+
138+
void ev_monitor_close(LoopState *lstate){
139+
close(lstate->epfd);
140+
}
141+
142+
int multi_timer_cb(CURLM *multi, long timeout_ms, LoopState *lstate) {
143+
elog(DEBUG2, "multi_timer_cb: Setting timeout to %ld ms\n", timeout_ms);
144+
event timer_event;
145+
int id = 1;
146+
147+
if (timeout_ms > 0) {
148+
EV_SET(&timer_event, id, EVFILT_TIMER, EV_ADD, 0, timeout_ms, NULL); //0 means milliseconds (the default)
149+
} else if (timeout_ms == 0){
150+
/* libcurl wants us to timeout now, however setting both fields of
151+
* new_value.it_value to zero disarms the timer. The closest we can
152+
* do is to schedule the timer to fire in 1 ns. */
153+
EV_SET(&timer_event, id, EVFILT_TIMER, EV_ADD, NOTE_NSECONDS, 1, NULL);
154+
} else {
155+
// libcurl passes a -1 to indicate the timer should be deleted
156+
EV_SET(&timer_event, id, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
157+
}
158+
159+
if (kevent(lstate->epfd, &timer_event, 1, NULL, 0, NULL) < 0) {
160+
int save_errno = errno;
161+
ereport(ERROR, errmsg("kevent with EVFILT_TIMER failed: %s", strerror(save_errno)));
162+
}
163+
164+
return 0;
165+
}
166+
167+
int multi_socket_cb(CURL *easy, curl_socket_t sockfd, int what, LoopState *lstate, void *socketp) {
168+
static char *whatstrs[] = { "NONE", "CURL_POLL_IN", "CURL_POLL_OUT", "CURL_POLL_INOUT", "CURL_POLL_REMOVE" };
169+
elog(DEBUG2, "multi_socket_cb: sockfd %d received %s", sockfd, whatstrs[what]);
170+
171+
SocketInfo *sock_info = (SocketInfo *)socketp;
172+
struct kevent ev[2];
173+
int count = 0;
174+
175+
if (what == CURL_POLL_REMOVE) {
176+
if (sock_info->action & CURL_POLL_IN)
177+
EV_SET(&ev[count++], sockfd, EVFILT_READ, EV_DELETE, 0, 0, sock_info);
178+
179+
if (sock_info->action & CURL_POLL_OUT)
180+
EV_SET(&ev[count++], sockfd, EVFILT_WRITE, EV_DELETE, 0, 0, sock_info);
181+
182+
curl_multi_assign(lstate->curl_mhandle, sockfd, NULL);
183+
pfree(sock_info);
184+
} else {
185+
if (!sock_info) {
186+
sock_info = palloc(sizeof(SocketInfo));
187+
sock_info->sockfd = sockfd;
188+
sock_info->action = what;
189+
curl_multi_assign(lstate->curl_mhandle, sockfd, sock_info);
190+
}
191+
192+
if (what & CURL_POLL_IN)
193+
EV_SET(&ev[count++], sockfd, EVFILT_READ, EV_ADD, 0, 0, sock_info);
194+
195+
if (what & CURL_POLL_OUT)
196+
EV_SET(&ev[count++], sockfd, EVFILT_WRITE, EV_ADD, 0, 0, sock_info);
197+
}
198+
199+
Assert(count <= 2);
200+
201+
if (kevent(lstate->epfd, &ev[0], count, NULL, 0, NULL) < 0) {
202+
int save_errno = errno;
203+
ereport(ERROR, errmsg("kevent with %s failed for sockfd %d: %s", whatstrs[what], sockfd, strerror(save_errno)));
204+
}
205+
206+
return 0;
207+
}
208+
209+
bool is_timer(event ev){
210+
return ev.filter == EVFILT_TIMER;
211+
}
212+
213+
int get_curl_event(event ev){
214+
int ev_bitmask = 0;
215+
if (ev.filter == EVFILT_READ)
216+
ev_bitmask |= CURL_CSELECT_IN;
217+
else if (ev.filter == EVFILT_WRITE)
218+
ev_bitmask |= CURL_CSELECT_OUT;
219+
else
220+
ev_bitmask = CURL_CSELECT_ERR;
221+
222+
return ev_bitmask;
223+
}
224+
225+
int get_socket_fd(event ev){
226+
SocketInfo *sock_info = (SocketInfo *) ev.udata;
227+
228+
return sock_info->sockfd;
229+
}
230+
231+
#endif

src/event.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,29 @@
33

44
#include <curl/multi.h>
55

6-
#include <sys/epoll.h>
7-
86
#include "core.h"
97

8+
#ifdef __linux__
9+
#define WAIT_USE_EPOLL
10+
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
11+
#define WAIT_USE_KQUEUE
12+
#else
13+
#error "no event wait implementation available"
14+
#endif
15+
16+
#ifdef WAIT_USE_EPOLL
17+
18+
#include <sys/epoll.h>
19+
#include <sys/timerfd.h>
1020
typedef struct epoll_event event;
1121

22+
#else
23+
24+
#include <sys/event.h>
25+
typedef struct kevent event;
26+
27+
#endif
28+
1229
int wait_event(int fd, event *events, size_t maxevents, int wait_milliseconds);
1330
int event_monitor(void);
1431
void ev_monitor_close(LoopState *lstate);

0 commit comments

Comments
 (0)