Don't use _Thread_local in run-test262.c

Allows building with tcc and old gcc versions again.
This commit is contained in:
Ben Noordhuis 2024-11-09 21:04:59 +01:00
parent f641d4ff18
commit b29332697b
3 changed files with 47 additions and 43 deletions

View file

@ -140,7 +140,7 @@ jobs:
make cxxtest make cxxtest
- name: test - name: test
if: ${{ matrix.config.configType != 'examples' && matrix.config.configType != 'tcc' }} if: ${{ matrix.config.configType != 'examples' }}
run: | run: |
make test make test

View file

@ -289,14 +289,7 @@ endif()
# Test262 runner # Test262 runner
# #
if(EMSCRIPTEN if(NOT EMSCRIPTEN)
OR CMAKE_C_COMPILER_ID STREQUAL "TinyCC"
OR (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 5))
# Empty. run-test262 uses pthreads, sorry Windows users.
# tcc and gcc 4.8 don't understand _Thread_local, whereas I
# don't understand why people still use 4.8 in this day and age
# but hey, here we are.
else()
add_executable(run-test262 add_executable(run-test262
run-test262.c run-test262.c
) )

View file

@ -50,6 +50,17 @@ typedef pthread_t js_thread_t;
#define CMD_NAME "run-test262" #define CMD_NAME "run-test262"
typedef struct {
js_mutex_t agent_mutex;
js_cond_t agent_cond;
/* list of Test262Agent.link */
struct list_head agent_list;
js_mutex_t report_mutex;
/* list of AgentReport.link */
struct list_head report_list;
int async_done;
} ThreadLocalStorage;
typedef struct namelist_t { typedef struct namelist_t {
char **array; char **array;
int count; int count;
@ -97,7 +108,6 @@ int start_index, stop_index;
int test_excluded; int test_excluded;
_Atomic int test_count, test_failed, test_skipped; _Atomic int test_count, test_failed, test_skipped;
_Atomic int new_errors, changed_errors, fixed_errors; _Atomic int new_errors, changed_errors, fixed_errors;
_Thread_local int async_done;
void warning(const char *, ...) __attribute__((__format__(__printf__, 1, 2))); void warning(const char *, ...) __attribute__((__format__(__printf__, 1, 2)));
void fatal(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); void fatal(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3)));
@ -443,6 +453,7 @@ static void enumerate_tests(const char *path)
static JSValue js_print_262(JSContext *ctx, JSValue this_val, static JSValue js_print_262(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv) int argc, JSValue *argv)
{ {
ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
int i; int i;
const char *str; const char *str;
@ -451,9 +462,9 @@ static JSValue js_print_262(JSContext *ctx, JSValue this_val,
if (!str) if (!str)
return JS_EXCEPTION; return JS_EXCEPTION;
if (!strcmp(str, "Test262:AsyncTestComplete")) { if (!strcmp(str, "Test262:AsyncTestComplete")) {
async_done++; tls->async_done++;
} else if (strstart(str, "Test262:AsyncTestFailure", NULL)) { } else if (strstart(str, "Test262:AsyncTestFailure", NULL)) {
async_done = 2; /* force an error */ tls->async_done = 2; /* force an error */
} }
if (outfile) { if (outfile) {
if (i != 0) if (i != 0)
@ -552,16 +563,6 @@ static long cpu_count(void)
#endif #endif
} }
typedef struct {
js_mutex_t agent_mutex;
js_cond_t agent_cond;
/* list of Test262Agent.link */
struct list_head agent_list;
js_mutex_t report_mutex;
/* list of AgentReport.link */
struct list_head report_list;
} ThreadLocalStorage;
static void init_thread_local_storage(ThreadLocalStorage *p) static void init_thread_local_storage(ThreadLocalStorage *p)
{ {
js_mutex_init(&p->agent_mutex); js_mutex_init(&p->agent_mutex);
@ -569,11 +570,9 @@ static void init_thread_local_storage(ThreadLocalStorage *p)
init_list_head(&p->agent_list); init_list_head(&p->agent_list);
js_mutex_init(&p->report_mutex); js_mutex_init(&p->report_mutex);
init_list_head(&p->report_list); init_list_head(&p->report_list);
p->async_done = 0;
} }
// points to parent thread's TLS in agent threads
static _Thread_local ThreadLocalStorage *tls;
typedef struct { typedef struct {
struct list_head link; struct list_head link;
js_thread_t tid; js_thread_t tid;
@ -597,17 +596,20 @@ static void add_helpers(JSContext *ctx);
static void *agent_start(void *arg) static void *agent_start(void *arg)
{ {
Test262Agent *agent = arg; ThreadLocalStorage *tls;
Test262Agent *agent;
JSRuntime *rt; JSRuntime *rt;
JSContext *ctx; JSContext *ctx;
JSValue ret_val; JSValue ret_val;
int ret; int ret;
agent = arg;
tls = agent->tls; // shares thread-local storage with parent thread tls = agent->tls; // shares thread-local storage with parent thread
rt = JS_NewRuntime(); rt = JS_NewRuntime();
if (rt == NULL) { if (rt == NULL) {
fatal(1, "JS_NewRuntime failure"); fatal(1, "JS_NewRuntime failure");
} }
JS_SetRuntimeOpaque(rt, tls);
ctx = JS_NewContext(rt); ctx = JS_NewContext(rt);
if (ctx == NULL) { if (ctx == NULL) {
JS_FreeRuntime(rt); JS_FreeRuntime(rt);
@ -674,6 +676,7 @@ static void *agent_start(void *arg)
static JSValue js_agent_start(JSContext *ctx, JSValue this_val, static JSValue js_agent_start(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv) int argc, JSValue *argv)
{ {
ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
const char *script; const char *script;
Test262Agent *agent; Test262Agent *agent;
@ -697,6 +700,7 @@ static JSValue js_agent_start(JSContext *ctx, JSValue this_val,
static void js_agent_free(JSContext *ctx) static void js_agent_free(JSContext *ctx)
{ {
ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
struct list_head *el, *el1; struct list_head *el, *el1;
Test262Agent *agent; Test262Agent *agent;
@ -719,7 +723,7 @@ static JSValue js_agent_leaving(JSContext *ctx, JSValue this_val,
return JS_UNDEFINED; return JS_UNDEFINED;
} }
static BOOL is_broadcast_pending(void) static BOOL is_broadcast_pending(ThreadLocalStorage *tls)
{ {
struct list_head *el; struct list_head *el;
Test262Agent *agent; Test262Agent *agent;
@ -734,6 +738,7 @@ static BOOL is_broadcast_pending(void)
static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val, static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv) int argc, JSValue *argv)
{ {
ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
JSValue sab = argv[0]; JSValue sab = argv[0];
struct list_head *el; struct list_head *el;
Test262Agent *agent; Test262Agent *agent;
@ -765,7 +770,7 @@ static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val,
} }
js_cond_broadcast(&tls->agent_cond); js_cond_broadcast(&tls->agent_cond);
while (is_broadcast_pending()) { while (is_broadcast_pending(tls)) {
js_cond_wait(&tls->agent_cond, &tls->agent_mutex); js_cond_wait(&tls->agent_cond, &tls->agent_mutex);
} }
js_mutex_unlock(&tls->agent_mutex); js_mutex_unlock(&tls->agent_mutex);
@ -819,6 +824,7 @@ static JSValue js_agent_monotonicNow(JSContext *ctx, JSValue this_val,
static JSValue js_agent_getReport(JSContext *ctx, JSValue this_val, static JSValue js_agent_getReport(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv) int argc, JSValue *argv)
{ {
ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
AgentReport *rep; AgentReport *rep;
JSValue ret; JSValue ret;
@ -843,6 +849,7 @@ static JSValue js_agent_getReport(JSContext *ctx, JSValue this_val,
static JSValue js_agent_report(JSContext *ctx, JSValue this_val, static JSValue js_agent_report(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv) int argc, JSValue *argv)
{ {
ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
const char *str; const char *str;
AgentReport *rep; AgentReport *rep;
@ -1338,6 +1345,7 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
const char *error_type, int eval_flags, int is_async, const char *error_type, int eval_flags, int is_async,
int *msec) int *msec)
{ {
ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
JSValue res_val, exception_val; JSValue res_val, exception_val;
int ret, error_line, pos, pos_line; int ret, error_line, pos, pos_line;
BOOL is_error, has_error_line, ret_promise; BOOL is_error, has_error_line, ret_promise;
@ -1352,7 +1360,7 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
/* a module evaluation returns a promise */ /* a module evaluation returns a promise */
ret_promise = ((eval_flags & JS_EVAL_TYPE_MODULE) != 0); ret_promise = ((eval_flags & JS_EVAL_TYPE_MODULE) != 0);
async_done = 0; /* counter of "Test262:AsyncTestComplete" messages */ tls->async_done = 0; /* counter of "Test262:AsyncTestComplete" messages */
start = get_clock_ms(); start = get_clock_ms();
res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags); res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
@ -1373,7 +1381,7 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
} else if (ret == 0) { } else if (ret == 0) {
if (is_async) { if (is_async) {
/* test if the test called $DONE() once */ /* test if the test called $DONE() once */
if (async_done != 1) { if (tls->async_done != 1) {
res_val = JS_ThrowTypeError(ctx, "$DONE() not called"); res_val = JS_ThrowTypeError(ctx, "$DONE() not called");
} else { } else {
res_val = JS_UNDEFINED; res_val = JS_UNDEFINED;
@ -1721,10 +1729,10 @@ JSContext *JS_NewCustomContext(JSRuntime *rt)
return ctx; return ctx;
} }
int run_test_buf(const char *filename, char *harness, namelist_t *ip, int run_test_buf(ThreadLocalStorage *tls, const char *filename, char *harness,
char *buf, size_t buf_len, const char* error_type, namelist_t *ip, char *buf, size_t buf_len,
int eval_flags, BOOL is_negative, BOOL is_async, const char* error_type, int eval_flags, BOOL is_negative,
BOOL can_block, int *msec) BOOL is_async, BOOL can_block, int *msec)
{ {
JSRuntime *rt; JSRuntime *rt;
JSContext *ctx; JSContext *ctx;
@ -1734,6 +1742,7 @@ int run_test_buf(const char *filename, char *harness, namelist_t *ip,
if (rt == NULL) { if (rt == NULL) {
fatal(1, "JS_NewRuntime failure"); fatal(1, "JS_NewRuntime failure");
} }
JS_SetRuntimeOpaque(rt, tls);
js_std_init_handlers(rt); js_std_init_handlers(rt);
ctx = JS_NewCustomContext(rt); ctx = JS_NewCustomContext(rt);
if (ctx == NULL) { if (ctx == NULL) {
@ -1782,7 +1791,7 @@ int run_test_buf(const char *filename, char *harness, namelist_t *ip,
return ret; return ret;
} }
int run_test(const char *filename, int *msec) int run_test(ThreadLocalStorage *tls, const char *filename, int *msec)
{ {
char harnessbuf[1024]; char harnessbuf[1024];
char *harness; char *harness;
@ -1943,12 +1952,12 @@ int run_test(const char *filename, int *msec)
} }
ret = 0; ret = 0;
if (use_nostrict) { if (use_nostrict) {
ret = run_test_buf(filename, harness, ip, buf, buf_len, ret = run_test_buf(tls, filename, harness, ip, buf, buf_len,
error_type, eval_flags, is_negative, is_async, error_type, eval_flags, is_negative, is_async,
can_block, msec); can_block, msec);
} }
if (use_strict) { if (use_strict) {
ret |= run_test_buf(filename, harness, ip, buf, buf_len, ret |= run_test_buf(tls, filename, harness, ip, buf, buf_len,
error_type, eval_flags | JS_EVAL_FLAG_STRICT, error_type, eval_flags | JS_EVAL_FLAG_STRICT,
is_negative, is_async, can_block, msec); is_negative, is_async, can_block, msec);
} }
@ -1961,7 +1970,8 @@ int run_test(const char *filename, int *msec)
} }
/* run a test when called by test262-harness+eshost */ /* run a test when called by test262-harness+eshost */
int run_test262_harness_test(const char *filename, BOOL is_module) int run_test262_harness_test(ThreadLocalStorage *tls, const char *filename,
BOOL is_module)
{ {
JSRuntime *rt; JSRuntime *rt;
JSContext *ctx; JSContext *ctx;
@ -1977,6 +1987,7 @@ int run_test262_harness_test(const char *filename, BOOL is_module)
if (rt == NULL) { if (rt == NULL) {
fatal(1, "JS_NewRuntime failure"); fatal(1, "JS_NewRuntime failure");
} }
JS_SetRuntimeOpaque(rt, tls);
ctx = JS_NewContext(rt); ctx = JS_NewContext(rt);
if (ctx == NULL) { if (ctx == NULL) {
JS_FreeRuntime(rt); JS_FreeRuntime(rt);
@ -2072,10 +2083,10 @@ int include_exclude_or_skip(int i) // naming is hard...
void *run_test_dir_list(void *arg) void *run_test_dir_list(void *arg)
{ {
ThreadLocalStorage tls_s, *tls = &tls_s;
const char *p; const char *p;
int i, msec; int i, msec;
tls = &(ThreadLocalStorage){};
init_thread_local_storage(tls); init_thread_local_storage(tls);
for (i = (uintptr_t)arg; i < test_list.count; i += nthreads) { for (i = (uintptr_t)arg; i < test_list.count; i += nthreads) {
@ -2083,7 +2094,7 @@ void *run_test_dir_list(void *arg)
continue; continue;
p = test_list.array[i]; p = test_list.array[i];
msec = 0; msec = 0;
run_test(p, &msec); run_test(tls, p, &msec);
if (verbose > 1 || (slow_test_threshold && msec >= slow_test_threshold)) if (verbose > 1 || (slow_test_threshold && msec >= slow_test_threshold))
fprintf(stderr, "%s (%d ms)\n", p, msec); fprintf(stderr, "%s (%d ms)\n", p, msec);
} }
@ -2124,6 +2135,7 @@ char *get_opt_arg(const char *option, char *arg)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
ThreadLocalStorage tls_s, *tls = &tls_s;
int i, optind; int i, optind;
BOOL is_dir_list; BOOL is_dir_list;
BOOL only_check_errors = FALSE; BOOL only_check_errors = FALSE;
@ -2134,7 +2146,6 @@ int main(int argc, char **argv)
js_std_set_worker_new_context_func(JS_NewCustomContext); js_std_set_worker_new_context_func(JS_NewCustomContext);
tls = &(ThreadLocalStorage){};
init_thread_local_storage(tls); init_thread_local_storage(tls);
js_mutex_init(&stats_mutex); js_mutex_init(&stats_mutex);
@ -2209,7 +2220,7 @@ int main(int argc, char **argv)
help(); help();
if (is_test262_harness) { if (is_test262_harness) {
return run_test262_harness_test(argv[optind], is_module); return run_test262_harness_test(tls, argv[optind], is_module);
} }
nthreads = max_int(nthreads, 1); nthreads = max_int(nthreads, 1);
@ -2276,7 +2287,7 @@ int main(int argc, char **argv)
} else { } else {
while (optind < argc) { while (optind < argc) {
int msec = 0; int msec = 0;
run_test(argv[optind++], &msec); run_test(tls, argv[optind++], &msec);
} }
} }