Skip to content

Instantly share code, notes, and snippets.

@daurnimator
Last active August 4, 2023 00:22
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save daurnimator/8cc2ef09ad72a5577b66f34957559e47 to your computer and use it in GitHub Desktop.
Save daurnimator/8cc2ef09ad72a5577b66f34957559e47 to your computer and use it in GitHub Desktop.
Use your own main loop on OSX. Related blog post: http://daurnimator.com/post/147024385399/using-your-own-main-loop-on-osx
#include <mach/port.h> /* mach_port_t */
#include <mach/mach.h> /* mach_port_allocate(), mach_task_self(), mach_port_insert_member(), MACH_PORT_RIGHT_PORT_SET */
#include <sys/event.h> /* kqueue(), kevent64(), struct kevent64_s, EVFILT_MACHPORT, EV_SET64, EV_ADD */
#include <sys/time.h> /* struct timespec */
//#include <dispatch/private.h>
extern mach_port_t _dispatch_get_main_queue_port_4CF(void);
extern void _dispatch_main_queue_callback_4CF(void);
#include <stdio.h>
#include <errno.h>
#include <unistd.h> /* close() */
#include <dispatch/dispatch.h>
int main() {
int fd;
struct kevent64_s event;
int n;
if (-1 == (fd = kqueue()))
return (perror("kqueue"), 1);
/* TODO: set cloexec */
mach_port_t x = _dispatch_get_main_queue_port_4CF();
printf("PORT 4CF=%d\n", x);
/* EVFILT_MACHPORT does not allow ports; only portsets */
mach_port_t y;
if (KERN_SUCCESS != mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &y))
return 1;
printf("PORT SET=%d\n", y);
EV_SET64(&event, y, EVFILT_MACHPORT, EV_ADD|EV_CLEAR, MACH_RCV_MSG, 0, 0, 0, 0);
if (0 != kevent64(fd, &event, 1, NULL, 0, 0, &(struct timespec){0,0}))
return (perror("kevent"), 1);
/* XXX: a port can only belong to one portset at once. this needs additional hackery when in a CF-using application */
if (KERN_SUCCESS != mach_port_insert_member(mach_task_self(), x, y))
return 1;
/* say hello */
dispatch_async(
dispatch_get_main_queue(),
^{
printf("hello\n");
}
);
/* in 2 seconds say hello again. */
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, 2LL*NSEC_PER_SEC),
dispatch_get_main_queue(),
^{
printf("hello from timer\n");
}
);
while (1) {
/* You could poll/select on `fd` now. instead, I'm just going to use a blocking kevent call. */
if (-1 == (n = kevent64(fd, NULL, 0, &event, 1, 0, NULL)))
return (perror("kevent"), 1);
if (n) {
printf("GOT EVENT. ident=%llu filter=%d\n",
event.ident, event.filter);
_dispatch_main_queue_callback_4CF();
} else
return 2;
}
close(fd);
return 0;
}
@daurnimator
Copy link
Author

From https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/event.h

Only portsets are support at this time.

From http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_msg.html

A port may not be- long to more than one port set, and if a port is a member of a port set, the holder of the receive right can't receive directly from the port.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment