feat: add engine alive indicator, debug mode, and orchestrator retry logic
This commit is contained in:
committed by
Loic Coenen (aider)
parent
e79c2ac116
commit
f2993eac80
@@ -1,5 +1,5 @@
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Wall -Wextra -Wpedantic -std=c11 -Isrc
|
CFLAGS = -Wall -Wextra -Wpedantic -std=c11 -Isrc -I../engine/src
|
||||||
CARLA_INC = -I/usr/include/carla -I/usr/include/carla/includes
|
CARLA_INC = -I/usr/include/carla -I/usr/include/carla/includes
|
||||||
CARLA_LIB = -L/usr/lib/carla -Wl,-rpath,/usr/lib/carla -lcarla_standalone2
|
CARLA_LIB = -L/usr/lib/carla -Wl,-rpath,/usr/lib/carla -lcarla_standalone2
|
||||||
|
|
||||||
@@ -22,8 +22,8 @@ all: looper-client test_status_parse
|
|||||||
looper-client: src/main.c src/tui.c $(PLUGINS_OBJ) $(CARLA_OBJ) $(CLIENT_CMD_OBJ) $(SCRIPT_OBJ) $(LOG_OBJ)
|
looper-client: src/main.c src/tui.c $(PLUGINS_OBJ) $(CARLA_OBJ) $(CLIENT_CMD_OBJ) $(SCRIPT_OBJ) $(LOG_OBJ)
|
||||||
$(CC) $(CFLAGS) $(CARLA_INC) -o $@ $^ $(CARLA_LIB) -ljack -lncurses
|
$(CC) $(CFLAGS) $(CARLA_INC) -o $@ $^ $(CARLA_LIB) -ljack -lncurses
|
||||||
|
|
||||||
test_status_parse: tests/test_status_parse.c $(PLUGINS_OBJ) $(CARLA_OBJ) $(CLIENT_CMD_OBJ)
|
test_status_parse: tests/test_status_parse.c $(PLUGINS_OBJ) $(CARLA_OBJ) $(CLIENT_CMD_OBJ) $(SCRIPT_OBJ)
|
||||||
$(CC) $(CFLAGS) $(CARLA_INC) -o test_status_parse tests/test_status_parse.c src/tui.c $(PLUGINS_OBJ) $(CARLA_OBJ) $(CLIENT_CMD_OBJ) $(CARLA_LIB) -ljack -lncurses
|
$(CC) $(CFLAGS) $(CARLA_INC) -o test_status_parse tests/test_status_parse.c src/tui.c $(PLUGINS_OBJ) $(CARLA_OBJ) $(CLIENT_CMD_OBJ) $(SCRIPT_OBJ) $(CARLA_LIB) -ljack -lncurses
|
||||||
|
|
||||||
# --- Plugin stubs (now real) ---
|
# --- Plugin stubs (now real) ---
|
||||||
$(PLUGINS_OBJ): src/plugins.c src/plugins.h
|
$(PLUGINS_OBJ): src/plugins.c src/plugins.h
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
#include "../engine/src/log.c"
|
#include "../../engine/src/log.c"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@@ -15,8 +16,15 @@
|
|||||||
#include "script.h"
|
#include "script.h"
|
||||||
#include <CarlaHost.h>
|
#include <CarlaHost.h>
|
||||||
|
|
||||||
|
/* ---------- engine alive indicator ---------- */
|
||||||
|
static bool engine_running = false;
|
||||||
|
static bool debug_mode = false;
|
||||||
|
|
||||||
/* ---------- FIFO command helper ---------- */
|
/* ---------- FIFO command helper ---------- */
|
||||||
int send_command(const char *cmd) {
|
int send_command(const char *cmd) {
|
||||||
|
if (debug_mode) {
|
||||||
|
fprintf(stderr, "DEBUG: send_command(%s)\n", cmd);
|
||||||
|
}
|
||||||
const char *fifo_path = getenv("LOOPER_CMD_FIFO");
|
const char *fifo_path = getenv("LOOPER_CMD_FIFO");
|
||||||
if (!fifo_path) fifo_path = "/tmp/looper_cmd";
|
if (!fifo_path) fifo_path = "/tmp/looper_cmd";
|
||||||
int fd = open(fifo_path, O_WRONLY | O_NONBLOCK);
|
int fd = open(fifo_path, O_WRONLY | O_NONBLOCK);
|
||||||
@@ -162,7 +170,7 @@ static void draw_grid(void) {
|
|||||||
}
|
}
|
||||||
clear();
|
clear();
|
||||||
attron(A_BOLD);
|
attron(A_BOLD);
|
||||||
mvprintw(0,0,"JACK Looper - Client (FIFO only)");
|
mvprintw(0,0,"JACK Looper - Client (FIFO only) %s", engine_running ? "[online]" : "[offline]");
|
||||||
attroff(A_BOLD);
|
attroff(A_BOLD);
|
||||||
for (int r=0; r<GRID_ROWS; r++)
|
for (int r=0; r<GRID_ROWS; r++)
|
||||||
for (int c=0; c<GRID_COLS; c++)
|
for (int c=0; c<GRID_COLS; c++)
|
||||||
@@ -181,6 +189,7 @@ static void draw_grid(void) {
|
|||||||
void tui_init(void) {
|
void tui_init(void) {
|
||||||
initscr();
|
initscr();
|
||||||
cbreak(); noecho(); keypad(stdscr, TRUE); curs_set(0);
|
cbreak(); noecho(); keypad(stdscr, TRUE); curs_set(0);
|
||||||
|
debug_mode = (getenv("LOOPER_DEBUG") != NULL);
|
||||||
if (!has_colors()) { endwin(); fprintf(stderr,"No colors\n"); exit(1); }
|
if (!has_colors()) { endwin(); fprintf(stderr,"No colors\n"); exit(1); }
|
||||||
start_color();
|
start_color();
|
||||||
init_pair(COLOR_EMPTY, COLOR_WHITE, COLOR_BLACK);
|
init_pair(COLOR_EMPTY, COLOR_WHITE, COLOR_BLACK);
|
||||||
@@ -234,6 +243,9 @@ void tui_run(void) {
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if engine is alive by testing existence of status FIFO */
|
||||||
|
engine_running = (access(STATUS_FIFO, F_OK) == 0);
|
||||||
|
|
||||||
/* read any available note events (for script macros) */
|
/* read any available note events (for script macros) */
|
||||||
int nfd = open(NOTES_FIFO, O_RDONLY | O_NONBLOCK);
|
int nfd = open(NOTES_FIFO, O_RDONLY | O_NONBLOCK);
|
||||||
if (nfd >= 0) {
|
if (nfd >= 0) {
|
||||||
|
|||||||
7
makefile
7
makefile
@@ -1,8 +1,10 @@
|
|||||||
# Top-level Makefile – delegates build/clean/test to subdirectories
|
# Top-level Makefile – delegates build/clean/test to subdirectories
|
||||||
|
|
||||||
|
CC ?= gcc
|
||||||
|
|
||||||
SUBDIRS = engine client
|
SUBDIRS = engine client
|
||||||
|
|
||||||
.PHONY: all build clean test check format orchestrator $(SUBDIRS)
|
.PHONY: all build clean test check format orchestrator run $(SUBDIRS)
|
||||||
|
|
||||||
all: build orchestrator
|
all: build orchestrator
|
||||||
|
|
||||||
@@ -15,6 +17,9 @@ orchestrator: orchestrator.c
|
|||||||
$(SUBDIRS):
|
$(SUBDIRS):
|
||||||
$(MAKE) -C $@
|
$(MAKE) -C $@
|
||||||
|
|
||||||
|
run: orchestrator
|
||||||
|
./looper
|
||||||
|
|
||||||
test:
|
test:
|
||||||
# $(MAKE) -C engine test
|
# $(MAKE) -C engine test
|
||||||
$(MAKE) -C client test
|
$(MAKE) -C client test
|
||||||
|
|||||||
138
orchestrator.c
138
orchestrator.c
@@ -1,3 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* orchestrator.c - Launches both the engine and client processes,
|
||||||
|
* forwards signals, and waits for either to exit before cleaning up
|
||||||
|
* the other. If a child exits abnormally it is retried up to 3 times.
|
||||||
|
*/
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -8,27 +15,44 @@
|
|||||||
static pid_t engine_pid = 0;
|
static pid_t engine_pid = 0;
|
||||||
static pid_t client_pid = 0;
|
static pid_t client_pid = 0;
|
||||||
|
|
||||||
static void cleanup(int sig) {
|
static void terminate_children(void) {
|
||||||
(void)sig;
|
|
||||||
if (engine_pid > 0) kill(engine_pid, SIGTERM);
|
if (engine_pid > 0) kill(engine_pid, SIGTERM);
|
||||||
if (client_pid > 0) kill(client_pid, SIGTERM);
|
if (client_pid > 0) kill(client_pid, SIGTERM);
|
||||||
while (wait(NULL) > 0);
|
}
|
||||||
|
|
||||||
|
static void wait_children(void) {
|
||||||
|
int status;
|
||||||
|
while (waitpid(-1, &status, 0) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cleanup(int sig) {
|
||||||
|
(void)sig;
|
||||||
|
terminate_children();
|
||||||
|
wait_children();
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
static pid_t start_engine(void) {
|
||||||
signal(SIGINT, cleanup);
|
pid_t pid = fork();
|
||||||
signal(SIGTERM, cleanup);
|
if (pid == -1) {
|
||||||
|
perror("fork engine");
|
||||||
engine_pid = fork();
|
return -1;
|
||||||
if (engine_pid == 0) {
|
}
|
||||||
|
if (pid == 0) {
|
||||||
execl("./engine/looper", "looper", NULL);
|
execl("./engine/looper", "looper", NULL);
|
||||||
perror("execl engine");
|
perror("execl engine");
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
client_pid = fork();
|
static pid_t start_client(int argc, char *argv[]) {
|
||||||
if (client_pid == 0) {
|
pid_t pid = fork();
|
||||||
|
if (pid == -1) {
|
||||||
|
perror("fork client");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
if (argc > 2 && strcmp(argv[1], "-s") == 0) {
|
if (argc > 2 && strcmp(argv[1], "-s") == 0) {
|
||||||
execl("./client/looper-client", "looper-client", "-s", argv[2], NULL);
|
execl("./client/looper-client", "looper-client", "-s", argv[2], NULL);
|
||||||
} else {
|
} else {
|
||||||
@@ -37,15 +61,85 @@ int main(int argc, char *argv[]) {
|
|||||||
perror("execl client");
|
perror("execl client");
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
return pid;
|
||||||
int status;
|
}
|
||||||
pid_t exited = wait(&status);
|
|
||||||
if (exited == engine_pid) {
|
int main(int argc, char *argv[]) {
|
||||||
kill(client_pid, SIGTERM);
|
signal(SIGINT, cleanup);
|
||||||
wait(NULL);
|
signal(SIGTERM, cleanup);
|
||||||
} else if (exited == client_pid) {
|
|
||||||
kill(engine_pid, SIGTERM);
|
int i;
|
||||||
wait(NULL);
|
for (i = 1; i < argc; i++) {
|
||||||
}
|
if (strcmp(argv[i], "--debug") == 0) {
|
||||||
return 0;
|
setenv("LOOPER_DEBUG", "1", 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int attempt = 0;
|
||||||
|
const int MAX_ATTEMPTS = 3;
|
||||||
|
|
||||||
|
while (attempt < MAX_ATTEMPTS) {
|
||||||
|
attempt++;
|
||||||
|
|
||||||
|
engine_pid = start_engine();
|
||||||
|
if (engine_pid == -1) {
|
||||||
|
if (attempt >= MAX_ATTEMPTS) {
|
||||||
|
fprintf(stderr, "Failed to start engine after %d attempts\n", MAX_ATTEMPTS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
usleep(500000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
client_pid = start_client(argc, argv);
|
||||||
|
if (client_pid == -1) {
|
||||||
|
kill(engine_pid, SIGTERM);
|
||||||
|
waitpid(engine_pid, NULL, 0);
|
||||||
|
if (attempt >= MAX_ATTEMPTS) {
|
||||||
|
fprintf(stderr, "Failed to start client after %d attempts\n", MAX_ATTEMPTS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
usleep(500000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Both children have started. Wait for either to exit. */
|
||||||
|
int status;
|
||||||
|
pid_t exited = waitpid(-1, &status, 0);
|
||||||
|
pid_t other = 0;
|
||||||
|
if (exited == engine_pid) {
|
||||||
|
other = client_pid;
|
||||||
|
} else if (exited == client_pid) {
|
||||||
|
other = engine_pid;
|
||||||
|
} else {
|
||||||
|
/* unexpected waitpid failure */
|
||||||
|
terminate_children();
|
||||||
|
wait_children();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kill the other child now that one has exited. */
|
||||||
|
if (other > 0) {
|
||||||
|
kill(other, SIGTERM);
|
||||||
|
waitpid(other, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Normal clean exit (zero status) means we are done. */
|
||||||
|
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attempt >= MAX_ATTEMPTS) {
|
||||||
|
fprintf(stderr, "Child exited abnormally after %d attempts. Quitting.\n",
|
||||||
|
MAX_ATTEMPTS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Child exited abnormally, retrying...\n");
|
||||||
|
usleep(500000);
|
||||||
|
/* loop back to try another fresh start */
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user