add multicore support
This commit is contained in:
parent
5b374ebc45
commit
f0cf4eacac
134
app.c
134
app.c
@ -1,3 +1,4 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "reactor/pool.h"
|
#include "reactor/pool.h"
|
||||||
#include "reactor/epoll.h"
|
#include "reactor/epoll.h"
|
||||||
@ -10,6 +11,9 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
#define DEFAULT_MAX_CONNS 10000
|
#define DEFAULT_MAX_CONNS 10000
|
||||||
#define DEFAULT_MAX_EVENTS 4096
|
#define DEFAULT_MAX_EVENTS 4096
|
||||||
@ -93,6 +97,12 @@ app_t* app_new(void) {
|
|||||||
a->arena_size = 4096;
|
a->arena_size = 4096;
|
||||||
a->not_found = default_not_found;
|
a->not_found = default_not_found;
|
||||||
|
|
||||||
|
// Default to single process mode
|
||||||
|
a->num_workers = 0;
|
||||||
|
a->cpu_affinity = 1;
|
||||||
|
a->worker_pids = NULL;
|
||||||
|
a->worker_id = -1;
|
||||||
|
|
||||||
// Create router
|
// Create router
|
||||||
a->router = router_new();
|
a->router = router_new();
|
||||||
if (!a->router) {
|
if (!a->router) {
|
||||||
@ -129,7 +139,10 @@ void app_free(app_t *a) {
|
|||||||
|
|
||||||
// app_listen sets up the listening socket.
|
// app_listen sets up the listening socket.
|
||||||
int app_listen(app_t *a, int port) {
|
int app_listen(app_t *a, int port) {
|
||||||
// Create runtime and pool if not already done
|
// Don't create runtime/pool yet if using workers
|
||||||
|
// They need to be created after fork() for proper isolation
|
||||||
|
if (a->num_workers == 0) {
|
||||||
|
// Single process mode - create runtime and pool now
|
||||||
if (!a->rt) {
|
if (!a->rt) {
|
||||||
a->rt = runtime_new(DEFAULT_MAX_EVENTS);
|
a->rt = runtime_new(DEFAULT_MAX_EVENTS);
|
||||||
if (!a->rt) return -1;
|
if (!a->rt) return -1;
|
||||||
@ -147,6 +160,7 @@ int app_listen(app_t *a, int port) {
|
|||||||
// Set router as request handler
|
// Set router as request handler
|
||||||
a->pool->on_request = route_request;
|
a->pool->on_request = route_request;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create socket
|
// Create socket
|
||||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
@ -183,25 +197,126 @@ int app_listen(app_t *a, int port) {
|
|||||||
// Make non-blocking
|
// Make non-blocking
|
||||||
set_nonblocking(fd);
|
set_nonblocking(fd);
|
||||||
|
|
||||||
// Add to epoll
|
// Add to epoll only if single process mode
|
||||||
|
if (a->num_workers == 0 && a->rt) {
|
||||||
runtime_add(a->rt, fd, EPOLLIN | EPOLLET, accept_handler, a);
|
runtime_add(a->rt, fd, EPOLLIN | EPOLLET, accept_handler, a);
|
||||||
|
}
|
||||||
|
|
||||||
a->listen_fd = fd;
|
a->listen_fd = fd;
|
||||||
a->port = port;
|
a->port = port;
|
||||||
|
|
||||||
|
if (a->num_workers == 0) {
|
||||||
printf("Server listening on :%d\n", port);
|
printf("Server listening on :%d\n", port);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// run_worker runs a single worker process
|
||||||
|
static int run_worker(app_t *a, int worker_id) {
|
||||||
|
a->worker_id = worker_id;
|
||||||
|
|
||||||
|
// Pin to CPU if requested
|
||||||
|
if (a->cpu_affinity) {
|
||||||
|
cpu_set_t cpuset;
|
||||||
|
CPU_ZERO(&cpuset);
|
||||||
|
CPU_SET(worker_id % sysconf(_SC_NPROCESSORS_ONLN), &cpuset);
|
||||||
|
sched_setaffinity(0, sizeof(cpuset), &cpuset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create runtime and pool for this worker
|
||||||
|
a->rt = runtime_new(DEFAULT_MAX_EVENTS);
|
||||||
|
if (!a->rt) return -1;
|
||||||
|
|
||||||
|
a->pool = pool_new(a->rt, a->max_conns);
|
||||||
|
if (!a->pool) return -1;
|
||||||
|
|
||||||
|
// Configure pool
|
||||||
|
a->pool->read_buf_size = a->read_buf_size;
|
||||||
|
a->pool->write_buf_size = a->write_buf_size;
|
||||||
|
a->pool->arena_size = a->arena_size;
|
||||||
|
a->pool->on_request = route_request;
|
||||||
|
|
||||||
|
// Add listen socket to this worker's epoll
|
||||||
|
runtime_add(a->rt, a->listen_fd, EPOLLIN | EPOLLET, accept_handler, a);
|
||||||
|
|
||||||
|
printf("Worker %d: Started on CPU %ld, listening on :%d\n", worker_id,
|
||||||
|
worker_id % sysconf(_SC_NPROCESSORS_ONLN), a->port);
|
||||||
|
|
||||||
|
return runtime_run(a->rt);
|
||||||
|
}
|
||||||
|
|
||||||
// app_run starts the event loop.
|
// app_run starts the event loop.
|
||||||
int app_run(app_t *a) {
|
int app_run(app_t *a) {
|
||||||
if (!a->rt || a->listen_fd <= 0) {
|
if (a->listen_fd <= 0) {
|
||||||
|
fprintf(stderr, "Error: Must call app_listen() before app_run()\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override pool callback with router
|
// Multi-worker mode
|
||||||
a->pool->on_request = route_request;
|
if (a->num_workers > 0) {
|
||||||
|
a->worker_pids = calloc(a->num_workers, sizeof(pid_t));
|
||||||
|
if (!a->worker_pids) return -1;
|
||||||
|
|
||||||
|
printf("Starting %d worker processes\n", a->num_workers);
|
||||||
|
|
||||||
|
for (int i = 0; i < a->num_workers; i++) {
|
||||||
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
// Child process
|
||||||
|
free(a->worker_pids); // Don't need this in child
|
||||||
|
a->worker_pids = NULL;
|
||||||
|
return run_worker(a, i);
|
||||||
|
} else if (pid > 0) {
|
||||||
|
// Parent process
|
||||||
|
a->worker_pids[i] = pid;
|
||||||
|
} else {
|
||||||
|
perror("fork");
|
||||||
|
// Kill already started workers
|
||||||
|
for (int j = 0; j < i; j++) {
|
||||||
|
if (a->worker_pids[j] > 0) {
|
||||||
|
kill(a->worker_pids[j], SIGTERM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(a->worker_pids);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parent process - wait for workers
|
||||||
|
printf("All workers started. Running...\n");
|
||||||
|
|
||||||
|
// Wait for any child to exit
|
||||||
|
int status;
|
||||||
|
pid_t pid;
|
||||||
|
while ((pid = wait(&status)) > 0) {
|
||||||
|
printf("Worker process %d exited with status %d\n", pid, WEXITSTATUS(status));
|
||||||
|
|
||||||
|
// Find and restart the worker
|
||||||
|
for (int i = 0; i < a->num_workers; i++) {
|
||||||
|
if (a->worker_pids[i] == pid) {
|
||||||
|
printf("Restarting worker %d\n", i);
|
||||||
|
pid_t new_pid = fork();
|
||||||
|
if (new_pid == 0) {
|
||||||
|
// New worker process
|
||||||
|
free(a->worker_pids);
|
||||||
|
a->worker_pids = NULL;
|
||||||
|
return run_worker(a, i);
|
||||||
|
} else if (new_pid > 0) {
|
||||||
|
a->worker_pids[i] = new_pid;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(a->worker_pids);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single process mode
|
||||||
|
printf("Running in single-process mode\n");
|
||||||
|
a->pool->on_request = route_request;
|
||||||
return runtime_run(a->rt);
|
return runtime_run(a->rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,6 +365,15 @@ void app_set_not_found(app_t *a, app_handler_t handler) {
|
|||||||
a->not_found = handler;
|
a->not_found = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Worker configuration
|
||||||
|
void app_set_workers(app_t *a, int num_workers) {
|
||||||
|
a->num_workers = num_workers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_set_cpu_affinity(app_t *a, int enabled) {
|
||||||
|
a->cpu_affinity = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
// Helper response functions
|
// Helper response functions
|
||||||
static void send_text(conn_t *c, int status, const char *text) {
|
static void send_text(conn_t *c, int status, const char *text) {
|
||||||
conn_write_status(c, status);
|
conn_write_status(c, status);
|
||||||
|
|||||||
13
app.h
13
app.h
@ -5,6 +5,7 @@
|
|||||||
#include "router.h"
|
#include "router.h"
|
||||||
#include "reactor/pool.h"
|
#include "reactor/pool.h"
|
||||||
#include "reactor/epoll.h"
|
#include "reactor/epoll.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
typedef struct app app_t;
|
typedef struct app app_t;
|
||||||
typedef void (*app_handler_t)(context_t *ctx);
|
typedef void (*app_handler_t)(context_t *ctx);
|
||||||
@ -23,6 +24,14 @@ struct app {
|
|||||||
size_t write_buf_size;
|
size_t write_buf_size;
|
||||||
size_t arena_size;
|
size_t arena_size;
|
||||||
|
|
||||||
|
// Worker configuration
|
||||||
|
int num_workers; // Number of worker processes (0 = single process)
|
||||||
|
int cpu_affinity; // Pin workers to CPU cores
|
||||||
|
|
||||||
|
// Worker management
|
||||||
|
pid_t *worker_pids; // Array of worker process IDs
|
||||||
|
int worker_id; // Current worker ID (-1 for parent)
|
||||||
|
|
||||||
// Default handlers
|
// Default handlers
|
||||||
app_handler_t not_found;
|
app_handler_t not_found;
|
||||||
app_handler_t error_handler;
|
app_handler_t error_handler;
|
||||||
@ -48,4 +57,8 @@ void app_set_arena_size(app_t *a, size_t size);
|
|||||||
void app_set_not_found(app_t *a, app_handler_t handler);
|
void app_set_not_found(app_t *a, app_handler_t handler);
|
||||||
void app_set_error(app_t *a, app_handler_t handler);
|
void app_set_error(app_t *a, app_handler_t handler);
|
||||||
|
|
||||||
|
// Worker configuration
|
||||||
|
void app_set_workers(app_t *a, int num_workers);
|
||||||
|
void app_set_cpu_affinity(app_t *a, int enabled);
|
||||||
|
|
||||||
#endif // APP_H
|
#endif // APP_H
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user