add http.cm and probe

This commit is contained in:
2026-02-24 21:04:03 -06:00
parent 3d4c0ec3d3
commit 2b877e6b0c
11 changed files with 846 additions and 7 deletions

View File

@@ -912,6 +912,14 @@ int js_is_blob(JSContext *js, JSValue v);
#include "blob.h"
/* ============================================================
Actor I/O Watch — event-driven fd monitoring
============================================================ */
void actor_watch_readable(JSContext *actor, int fd, JSValue fn);
void actor_watch_writable(JSContext *actor, int fd, JSValue fn);
void actor_unwatch(JSContext *actor, int fd);
/* ============================================================
Convenience Functions
============================================================ */

View File

@@ -859,6 +859,14 @@ typedef struct letter {
};
} letter;
/* I/O watch entry — one per watched file descriptor */
typedef struct {
int fd;
short events; /* POLLIN, POLLOUT */
JSContext *actor;
JSValue callback;
} io_watch;
/* Actor state machine constants */
#define ACTOR_IDLE 0
#define ACTOR_READY 1
@@ -1049,6 +1057,10 @@ void enqueue_actor_priority(JSContext *actor);
void actor_clock(JSContext *actor, JSValue fn);
uint32_t actor_delay(JSContext *actor, JSValue fn, double seconds);
JSValue actor_remove_timer(JSContext *actor, uint32_t timer_id);
void actor_watch(JSContext *actor, int fd, short events, JSValue fn);
void actor_watch_readable(JSContext *actor, int fd, JSValue fn);
void actor_watch_writable(JSContext *actor, int fd, JSValue fn);
void actor_unwatch(JSContext *actor, int fd);
void exit_handler(void);
void actor_loop(void);
void actor_initialize(void);

View File

@@ -6,6 +6,8 @@
#include <stdio.h>
#include <unistd.h>
#include <stdatomic.h>
#include <poll.h>
#include <fcntl.h>
#include "stb_ds.h"
#include "cell.h"
@@ -13,6 +15,7 @@
#ifdef _WIN32
#include <windows.h>
#include <winsock2.h>
#endif
typedef struct actor_node {
@@ -93,6 +96,95 @@ static int has_any_work(actor_node *heads[]) {
static pthread_mutex_t *actors_mutex;
static struct { char *key; JSContext *value; } *actors = NULL;
/* ============================================================
I/O Watch Thread — poll()-based fd monitoring
============================================================ */
static io_watch *g_io_watches = NULL; /* stb_ds dynamic array */
static pthread_mutex_t io_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_t io_thread;
static int io_pipe[2] = {-1, -1}; /* self-pipe to wake poll() */
static void io_wake(void) {
char c = 1;
(void)write(io_pipe[1], &c, 1);
}
static void *io_thread_func(void *arg) {
(void)arg;
while (1) {
pthread_mutex_lock(&io_mutex);
if (engine.shutting_down) {
pthread_mutex_unlock(&io_mutex);
return NULL;
}
int n = arrlen(g_io_watches);
/* +1 for the wakeup pipe */
struct pollfd *fds = malloc(sizeof(struct pollfd) * (n + 1));
fds[0].fd = io_pipe[0];
fds[0].events = POLLIN;
for (int i = 0; i < n; i++) {
fds[i + 1].fd = g_io_watches[i].fd;
fds[i + 1].events = g_io_watches[i].events;
}
pthread_mutex_unlock(&io_mutex);
int ready = poll(fds, n + 1, 500); /* 500ms timeout for shutdown check */
if (ready <= 0) {
free(fds);
continue;
}
/* Drain wakeup pipe */
if (fds[0].revents & POLLIN) {
char buf[64];
(void)read(io_pipe[0], buf, sizeof(buf));
}
/* Fire callbacks for ready fds */
pthread_mutex_lock(&io_mutex);
for (int i = n - 1; i >= 0; i--) {
if (i >= arrlen(g_io_watches)) continue;
if (fds[i + 1].revents & g_io_watches[i].events) {
io_watch w = g_io_watches[i];
arrdel(g_io_watches, i); /* one-shot: remove before firing */
pthread_mutex_unlock(&io_mutex);
actor_clock(w.actor, w.callback);
pthread_mutex_lock(&io_mutex);
}
}
pthread_mutex_unlock(&io_mutex);
free(fds);
}
return NULL;
}
void actor_watch(JSContext *actor, int fd, short events, JSValue fn) {
io_watch w = { .fd = fd, .events = events, .actor = actor, .callback = fn };
pthread_mutex_lock(&io_mutex);
arrput(g_io_watches, w);
pthread_mutex_unlock(&io_mutex);
io_wake();
}
void actor_watch_readable(JSContext *actor, int fd, JSValue fn) {
actor_watch(actor, fd, POLLIN, fn);
}
void actor_watch_writable(JSContext *actor, int fd, JSValue fn) {
actor_watch(actor, fd, POLLOUT, fn);
}
void actor_unwatch(JSContext *actor, int fd) {
pthread_mutex_lock(&io_mutex);
for (int i = arrlen(g_io_watches) - 1; i >= 0; i--) {
if (g_io_watches[i].actor == actor && g_io_watches[i].fd == fd) {
arrdel(g_io_watches, i);
}
}
pthread_mutex_unlock(&io_mutex);
io_wake();
}
#define lockless_shdel(NAME, KEY) pthread_mutex_lock(NAME##_mutex); shdel(NAME, KEY); pthread_mutex_unlock(NAME##_mutex);
#define lockless_shlen(NAME) ({ \
pthread_mutex_lock(NAME##_mutex); \
@@ -307,6 +399,13 @@ void actor_initialize(void) {
// Start Timer Thread
pthread_create(&engine.timer_thread, NULL, timer_thread_func, NULL);
// Start I/O Watch Thread
if (pipe(io_pipe) == 0) {
fcntl(io_pipe[0], F_SETFL, O_NONBLOCK);
fcntl(io_pipe[1], F_SETFL, O_NONBLOCK);
pthread_create(&io_thread, NULL, io_thread_func, NULL);
}
// Start Workers
#ifdef _WIN32
SYSTEM_INFO sysinfo;
@@ -359,6 +458,14 @@ void actor_free(JSContext *actor)
pthread_mutex_unlock(&engine.lock);
}
/* Remove I/O watches for this actor */
pthread_mutex_lock(&io_mutex);
for (int i = arrlen(g_io_watches) - 1; i >= 0; i--) {
if (g_io_watches[i].actor == actor)
arrdel(g_io_watches, i);
}
pthread_mutex_unlock(&io_mutex);
// Do not go forward with actor destruction until the actor is completely free
pthread_mutex_lock(actor->msg_mutex);
pthread_mutex_lock(actor->mutex);
@@ -438,6 +545,16 @@ void exit_handler(void) {
pthread_join(engine.timer_thread, NULL);
/* Shut down I/O thread */
if (io_pipe[1] >= 0) {
io_wake();
pthread_join(io_thread, NULL);
close(io_pipe[0]);
close(io_pipe[1]);
io_pipe[0] = io_pipe[1] = -1;
}
arrfree(g_io_watches);
for (int i=0; i < engine.num_workers; i++) {
pthread_join(engine.worker_threads[i], NULL);
}
@@ -883,6 +1000,17 @@ void actor_gc_scan(JSContext *ctx,
if (ctx->msg_mutex)
pthread_mutex_unlock(ctx->msg_mutex);
/* Scan I/O watch callbacks belonging to this actor */
pthread_mutex_lock(&io_mutex);
for (int i = 0; i < arrlen(g_io_watches); i++) {
if (g_io_watches[i].actor == ctx) {
g_io_watches[i].callback = gc_copy_value(ctx,
g_io_watches[i].callback,
from_base, from_end, to_base, to_free, to_end);
}
}
pthread_mutex_unlock(&io_mutex);
}
void actor_clock(JSContext *actor, JSValue fn)