Skip to content

Commit 082ab22

Browse files
v2.5.1 (#331)
* v2.4.0 (#327) * feat(query): support live inserts into parted tables Add immutable live-tail growth with explicit partition keys, shared FILE-domain symbol handling, atomic symbol rebinding, adversarial coverage, documentation, and a runnable rollover example. * fix(core): restore total-core -c semantics * fix(parse) Fix nonstring if not defined --------- Co-authored-by: Karim <k.nassar@lynxtrading.com>
1 parent 4a664ab commit 082ab22

7 files changed

Lines changed: 116 additions & 19 deletions

File tree

docs/docs/namespaces/sys.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Signature: `(.sys.args)`. Returns the process's command-line arguments as a dict
3131
|---|---|---|---|
3232
| `file` | str | `-f` / positional | Script path; empty if none. |
3333
| `port` | i64 | `-p` | IPC listen port; `0` if unset. |
34-
| `cores` | i64 | `-c` | Worker-pool size; `0` = auto. |
34+
| `cores` | i64 | `-c` | Total execution cores, including the main thread; `0` = auto. |
3535
| `timeit` | bool | `-t` | Profiler enabled at startup. |
3636
| `querylog` | bool | `-Q` | Query-statistics logging enabled at startup. |
3737
| `interactive` | bool | `-i` | Force the REPL after a script. |

src/app/main.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ int main(int argc, char** argv) {
8181
int interactive = 0;
8282
const char* file = NULL;
8383
uint16_t port = 0;
84-
int n_cores = -1; /* -1 = leave pool at lazy ncpu-1 default */
84+
int n_cores = -1; /* total execution cores; -1 = leave pool lazy */
8585
int timeit_init = 0; /* -t N: enable profiler at startup */
8686
int qlog_init = 0; /* -Q N: enable query-statistics logging at startup */
8787
const char* auth_pw = NULL;
@@ -92,7 +92,7 @@ int main(int argc, char** argv) {
9292
/* Parse args. Supported flags:
9393
* -f FILE run script file
9494
* -p PORT IPC listen port
95-
* -c N worker-pool size (0 = auto: ncpu - 1)
95+
* -c N total execution cores, main included (0 = auto)
9696
* -t N enable timeit at startup (N != 0 turns on)
9797
* -Q N enable query-statistics logging (N != 0 turns on)
9898
* -i interactive
@@ -152,7 +152,7 @@ int main(int argc, char** argv) {
152152
" [-u PW | -U PW] [-l BASE | -L BASE] [-m SIZE] [file.rfl] [-- app args]\n"
153153
" -f, --file FILE run script file (or pass as a positional arg)\n"
154154
" -p, --port PORT listen for IPC clients on PORT\n"
155-
" -c, --cores N worker-pool size (0 = auto: ncpu - 1, default)\n"
155+
" -c, --cores N total execution cores, main included (0 = auto)\n"
156156
" -t, --timeit N enable profiler at startup (N != 0)\n"
157157
" -i, --interactive start the REPL even after running a file\n"
158158
" -u PW set plain auth password\n"
@@ -191,9 +191,9 @@ int main(int argc, char** argv) {
191191
* (file load, REPL eval, builtins). If -c wasn't given, leave the
192192
* pool to its lazy default on first use. */
193193
if (n_cores >= 0) {
194-
ray_err_t err = ray_pool_init((uint32_t)n_cores);
194+
ray_err_t err = ray_pool_init_total((uint32_t)n_cores);
195195
if (err != RAY_OK)
196-
fprintf(stderr, "warning: ray_pool_init(%d) failed (%d)\n",
196+
fprintf(stderr, "warning: ray_pool_init_total(%d) failed (%d)\n",
197197
n_cores, (int)err);
198198
}
199199

src/core/pool.c

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ static void worker_loop(void* arg) {
120120
* ray_pool_create
121121
* -------------------------------------------------------------------------- */
122122

123-
ray_err_t ray_pool_create(ray_pool_t* pool, uint32_t n_workers) {
123+
static ray_err_t ray_pool_create_impl(ray_pool_t* pool, uint32_t n_workers,
124+
bool auto_size) {
124125
/* conc-L7: memset zeroes all fields including the `cancelled` atomic,
125126
* which resets any cancellation state from a prior pool instance. */
126127
memset(pool, 0, sizeof(*pool));
@@ -133,14 +134,14 @@ ray_err_t ray_pool_create(ray_pool_t* pool, uint32_t n_workers) {
133134
atomic_init(&pool->pending, 0);
134135
atomic_init(&pool->cancelled, 0);
135136

136-
if (n_workers == 0) {
137+
if (auto_size) {
137138
/* Auto-size to ncpu-1. The RAYFORCE_CORES env var overrides this
138139
* default worker count — the test harness sets it (see the Makefile
139140
* `test` target) so neither the in-process runtime nor the server
140141
* children it spawns via .sys.exec each create ncpu-1 threads on a
141-
* many-core box. An explicit -c (which passes n_workers > 0 through
142-
* ray_pool_init) bypasses this entirely, and a non-test run with the
143-
* var unset keeps the historical ncpu-1 default. */
142+
* many-core box. An explicit positive -c uses ray_pool_init_total()
143+
* with exact sizing and bypasses this entirely; a non-test run with
144+
* the var unset keeps the historical ncpu-1 default. */
144145
const char* env = getenv("RAYFORCE_CORES");
145146
if (env && *env) {
146147
long v = strtol(env, NULL, 10);
@@ -221,6 +222,10 @@ ray_err_t ray_pool_create(ray_pool_t* pool, uint32_t n_workers) {
221222
return RAY_OK;
222223
}
223224

225+
ray_err_t ray_pool_create(ray_pool_t* pool, uint32_t n_workers) {
226+
return ray_pool_create_impl(pool, n_workers, n_workers == 0);
227+
}
228+
224229
/* --------------------------------------------------------------------------
225230
* ray_pool_free
226231
* -------------------------------------------------------------------------- */
@@ -506,11 +511,11 @@ ray_pool_t* ray_pool_get(void) {
506511
* Public API wrappers (declared in rayforce.h)
507512
* -------------------------------------------------------------------------- */
508513

509-
/* conc-L4: If ray_pool_init() is called when the pool is already initialized
510-
* (state==2), the n_workers parameter is silently ignored and the existing
511-
* pool configuration is preserved. This is by design — the pool is a
512-
* singleton and reconfiguration requires ray_pool_destroy() + ray_pool_init(). */
513-
ray_err_t ray_pool_init(uint32_t n_workers) {
514+
/* conc-L4: If an initializer is called when the pool is already initialized
515+
* (state==2), its requested size is silently ignored and the existing pool
516+
* configuration is preserved. This is by design — the pool is a singleton
517+
* and reconfiguration requires ray_pool_destroy() followed by initialization. */
518+
static ray_err_t ray_pool_init_impl(uint32_t n_workers, bool auto_size) {
514519
uint32_t expected = 0;
515520
if (!atomic_compare_exchange_strong_explicit(&g_pool_init_state, &expected, 1,
516521
memory_order_acq_rel,
@@ -527,7 +532,7 @@ ray_err_t ray_pool_init(uint32_t n_workers) {
527532
}
528533
return RAY_OK; /* already initialized or completed during our spin */
529534
}
530-
ray_err_t err = ray_pool_create(&g_pool, n_workers);
535+
ray_err_t err = ray_pool_create_impl(&g_pool, n_workers, auto_size);
531536
if (err == RAY_OK) {
532537
atomic_store_explicit(&g_pool_init_state, 2, memory_order_release);
533538
} else {
@@ -536,6 +541,16 @@ ray_err_t ray_pool_init(uint32_t n_workers) {
536541
return err;
537542
}
538543

544+
ray_err_t ray_pool_init(uint32_t n_workers) {
545+
return ray_pool_init_impl(n_workers, n_workers == 0);
546+
}
547+
548+
ray_err_t ray_pool_init_total(uint32_t total_workers) {
549+
if (total_workers == 0)
550+
return ray_pool_init_impl(0, true);
551+
return ray_pool_init_impl(total_workers - 1, false);
552+
}
553+
539554
void ray_pool_destroy(void) {
540555
uint32_t expected = 2;
541556
if (!atomic_compare_exchange_strong_explicit(&g_pool_init_state, &expected, 3,

src/core/pool.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ ray_pool_t* ray_pool_get(void);
107107

108108
/* Public pool init/destroy (moved from rayforce.h) */
109109
ray_err_t ray_pool_init(uint32_t n_workers);
110+
/* Initialize by total execution participants, including the main thread.
111+
* Pass 0 to auto-detect. Used by the CLI, where -c is a total-core count. */
112+
ray_err_t ray_pool_init_total(uint32_t total_workers);
110113
void ray_pool_destroy(void);
111114

112115
#endif /* RAY_POOL_H */

src/lang/parse.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,13 @@
5353
#define PA_MINUS 14
5454
#define PA_SEMI 15 /* ; comment */
5555

56-
static const char _PA[128] =
56+
#if defined(__has_attribute) && __has_attribute(nonstring)
57+
#define RAY_NONSTRING __attribute__((nonstring))
58+
#else
59+
#define RAY_NONSTRING
60+
#endif
61+
62+
static const char _PA[128] RAY_NONSTRING =
5763
/* NUL \t \n */
5864
"\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x0c\x00\x00\x0c\x00\x00"
5965
/* */

test/test_pool.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,36 @@ static test_result_t test_pool_total_workers(void) {
661661
PASS();
662662
}
663663

664+
/* --------------------------------------------------------------------------
665+
* Test: total-worker initialization used by the CLI.
666+
*
667+
* The pool API normally accepts a background-worker count and reserves worker
668+
* 0 for the calling thread. The CLI's -c contract is different: it counts all
669+
* participants. In particular, total=1 must create an exact zero-background
670+
* pool rather than taking ray_pool_init(0)'s auto-size path.
671+
* -------------------------------------------------------------------------- */
672+
673+
static test_result_t test_pool_init_total(void) {
674+
ray_pool_destroy();
675+
TEST_ASSERT_EQ_I(ray_pool_init_total(1), RAY_OK);
676+
ray_pool_t* pool = ray_pool_get();
677+
TEST_ASSERT_NOT_NULL(pool);
678+
TEST_ASSERT_EQ_U(pool->n_workers, 0u);
679+
TEST_ASSERT_EQ_U(ray_pool_total_workers(pool), 1u);
680+
681+
ray_pool_destroy();
682+
TEST_ASSERT_EQ_I(ray_pool_init_total(4), RAY_OK);
683+
pool = ray_pool_get();
684+
TEST_ASSERT_NOT_NULL(pool);
685+
TEST_ASSERT_EQ_U(pool->n_workers, 3u);
686+
TEST_ASSERT_EQ_U(ray_pool_total_workers(pool), 4u);
687+
688+
/* Restore the lazy/default configuration for later tests. */
689+
ray_pool_destroy();
690+
TEST_ASSERT_EQ_I(ray_pool_init(0), RAY_OK);
691+
PASS();
692+
}
693+
664694
/* --------------------------------------------------------------------------
665695
* Test: ray_pool_free(NULL) is a no-op (covers the early-return guard).
666696
* -------------------------------------------------------------------------- */
@@ -1217,6 +1247,7 @@ const test_entry_t pool_entries[] = {
12171247
{ "pool/dispatch_cancelled", test_dispatch_cancelled, NULL, NULL },
12181248
{ "pool/zero_workers", test_pool_zero_workers, NULL, NULL },
12191249
{ "pool/total_workers", test_pool_total_workers, NULL, NULL },
1250+
{ "pool/init_total", test_pool_init_total, NULL, NULL },
12201251
{ "pool/free_null", test_pool_free_null, NULL, NULL },
12211252
{ "pool/init_idempotent", test_pool_init_idempotent, NULL, NULL },
12221253
{ "pool/destroy_reinit", test_pool_destroy_and_reinit, NULL, NULL },
@@ -1235,4 +1266,3 @@ const test_entry_t pool_entries[] = {
12351266
{ NULL, NULL, NULL, NULL },
12361267
};
12371268

1238-

test/test_repl.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2905,9 +2905,52 @@ static test_result_t test_repl_pty_ctrl_c_during_lazy_materialize(void) {
29052905
PASS();
29062906
}
29072907

2908+
#if defined(__linux__)
2909+
/* Run the real launcher in piped REPL mode and count its live task threads.
2910+
* /proc/self/task observes the exact process-wide total, so this catches both
2911+
* the ordinary off-by-one and the -c 1 collision with the pool's auto mode. */
2912+
static int run_cli_task_count(unsigned cores, long* out_count) {
2913+
char command[256];
2914+
snprintf(command, sizeof(command),
2915+
"printf '(count (.fs.list \"/proc/self/task\"))\\n'"
2916+
" | ./rayforce -c %u -i", cores);
2917+
2918+
FILE* pipe = popen(command, "r");
2919+
if (!pipe) return -1;
2920+
2921+
char line[128];
2922+
bool have_line = fgets(line, sizeof(line), pipe) != NULL;
2923+
int status = pclose(pipe);
2924+
if (!have_line || status == -1 || !WIFEXITED(status) ||
2925+
WEXITSTATUS(status) != 0)
2926+
return -2;
2927+
2928+
char* end = NULL;
2929+
long count = strtol(line, &end, 10);
2930+
if (end == line) return -3;
2931+
*out_count = count;
2932+
return 0;
2933+
}
2934+
2935+
static test_result_t test_repl_cli_cores_are_total(void) {
2936+
long count = 0;
2937+
int rc = run_cli_task_count(1, &count);
2938+
TEST_ASSERT_FMT(rc == 0, "-c 1 launcher probe failed: %d", rc);
2939+
TEST_ASSERT_FMT(count == 1, "-c 1 created %ld total threads", count);
2940+
2941+
rc = run_cli_task_count(3, &count);
2942+
TEST_ASSERT_FMT(rc == 0, "-c 3 launcher probe failed: %d", rc);
2943+
TEST_ASSERT_FMT(count == 3, "-c 3 created %ld total threads", count);
2944+
PASS();
2945+
}
2946+
#endif
2947+
29082948
/* ─── Suite definition ───────────────────────────────────────────── */
29092949

29102950
const test_entry_t repl_entries[] = {
2951+
#if defined(__linux__)
2952+
{ "repl/cli/cores_are_total", test_repl_cli_cores_are_total, NULL, NULL },
2953+
#endif
29112954
/* file-batch entrypoint — ray_repl_run_file */
29122955
{ "repl/run_file/happy", test_repl_run_file_happy, repl_setup, repl_teardown },
29132956
{ "repl/run_file/multi_form", test_repl_run_file_multi_form, repl_setup, repl_teardown },

0 commit comments

Comments
 (0)