add http.cm and probe
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user