Skip to content

Commit

Permalink
Build and run run-test262 on Windows
Browse files Browse the repository at this point in the history
The ulterior motive here is not that I want to increase CI times
further but that I want to repurpose run-test262 for running our
own tests.
  • Loading branch information
bnoordhuis committed Oct 10, 2024
1 parent 364453b commit 3c3c487
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 22 deletions.
3 changes: 1 addition & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,7 @@ endif()
# Test262 runner
#

if(WIN32
OR EMSCRIPTEN
if(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.
Expand Down
4 changes: 2 additions & 2 deletions cutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -1129,7 +1129,7 @@ void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)

/*---- Portable time functions ----*/

#if defined(_MSC_VER)
#ifdef _WIN32
// From: https://stackoverflow.com/a/26085827
static int gettimeofday_msvc(struct timeval *tp, struct timezone *tzp)
{
Expand Down Expand Up @@ -1184,7 +1184,7 @@ uint64_t js__hrtime_ns(void) {

int64_t js__gettimeofday_us(void) {
struct timeval tv;
#if defined(_MSC_VER)
#ifdef _WIN32
gettimeofday_msvc(&tv, NULL);
#else
gettimeofday(&tv, NULL);
Expand Down
138 changes: 120 additions & 18 deletions run-test262.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,19 @@
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>

#ifdef _WIN32
#include <windows.h>
#include <process.h>
typedef HANDLE js_thread_t;
#else
#include <dirent.h>
#include <ftw.h>
#include <pthread.h>
#include <unistd.h>
typedef pthread_t js_thread_t;
#endif

#include "cutils.h"
#include "list.h"
Expand All @@ -50,8 +57,8 @@ typedef struct namelist_t {
} namelist_t;

long nthreads; // invariant: 0 < nthreads < countof(threads)
pthread_t threads[32];
pthread_t progress_thread;
js_thread_t threads[32];
js_thread_t progress_thread;
js_cond_t progress_cond;
js_mutex_t progress_mutex;

Expand Down Expand Up @@ -328,7 +335,7 @@ void namelist_load(namelist_t *lp, const char *filename)
char *base_name;
FILE *f;

f = fopen(filename, "rb");
f = fopen(filename, "r");
if (!f) {
perror_exit(1, filename);
}
Expand Down Expand Up @@ -369,20 +376,66 @@ void namelist_free(namelist_t *lp)
lp->size = 0;
}

static int add_test_file(const char *filename, const struct stat *ptr, int flag)
static int add_test_file(const char *filename)
{
namelist_t *lp = &test_list;
if (has_suffix(filename, ".js") && !has_suffix(filename, "_FIXTURE.js"))
namelist_add(lp, NULL, filename);
return 0;
}

static void find_test_files(const char *path);

static void consider_test_file(const char *path, const char *name, int is_dir)
{
char s[1024];

if (str_equal(name, ".") || str_equal(name, ".."))
return;
snprintf(s, sizeof(s), "%s/%s", path, name);
if (is_dir)
find_test_files(s);
else
add_test_file(s);
}

static void find_test_files(const char *path)
{
#ifdef _WIN32
WIN32_FIND_DATAA d;
HANDLE h;
char s[1024];

snprintf(s, sizeof(s), "%s/*", path);
h = FindFirstFileA(s, &d);
if (h != INVALID_HANDLE_VALUE) {
do {
consider_test_file(path,
d.cFileName,
d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
} while (FindNextFileA(h, &d));
FindClose(h);
}
#else
struct dirent *d, **ds = NULL;
int i, n;

n = scandir(path, &ds, NULL, alphasort);
for (i = 0; i < n; i++) {
d = ds[i];
consider_test_file(path, d->d_name, d->d_type == DT_DIR);
free(d);
}
free(ds);
#endif
}

/* find js files from the directory tree and sort the list */
static void enumerate_tests(const char *path)
{
namelist_t *lp = &test_list;
int start = lp->count;
ftw(path, add_test_file, 100);
find_test_files(path);
qsort(lp->array + start, lp->count - start, sizeof(*lp->array),
namelist_cmp_indirect);
}
Expand Down Expand Up @@ -435,25 +488,64 @@ static JSValue js_evalScript_262(JSContext *ctx, JSValue this_val,
return ret;
}

static void start_thread(pthread_t *thrd, void *(*start)(void *), void *arg)
static void start_thread(js_thread_t *thrd, void *(*start)(void *), void *arg)
{
// musl libc gives threads 80 kb stacks, much smaller than
// JS_DEFAULT_STACK_SIZE (256 kb)
static const unsigned stacksize = 2 << 20; // 2 MB, glibc default
#ifdef _WIN32
HANDLE h, cp;

cp = GetCurrentProcess();
h = (HANDLE)_beginthread((void (*)(void *))(void *)start, stacksize, arg);
if (!h)
fatal(1, "_beginthread error");
// _endthread() automatically closes the handle but we want to wait on
// it so make a copy. Race-y for very short-lived threads. Can be solved
// by switching to _beginthreadex(CREATE_SUSPENDED) but means changing
// |start| from __cdecl to __stdcall.
if (!DuplicateHandle(cp, h, cp, thrd, 0, FALSE, DUPLICATE_SAME_ACCESS))
fatal(1, "DuplicateHandle error");
#else
pthread_attr_t attr;

if (pthread_attr_init(&attr))
fatal(1, "pthread_attr_init");
// musl libc gives threads 80 kb stacks, much smaller than
// JS_DEFAULT_STACK_SIZE (256 kb)
if (pthread_attr_setstacksize(&attr, 2 << 20)) // 2 MB, glibc default
if (pthread_attr_setstacksize(&attr, stacksize))
fatal(1, "pthread_attr_setstacksize");
if (pthread_create(thrd, &attr, start, arg))
fatal(1, "pthread_create error");
pthread_attr_destroy(&attr);
#endif
}

static void join_thread(pthread_t thrd)
static void join_thread(js_thread_t thrd)
{
#ifdef _WIN32
if (WaitForSingleObject(thrd, INFINITE))
fatal(1, "WaitForSingleObject error");
CloseHandle(thrd);
#else
if (pthread_join(thrd, NULL))
fatal(1, "pthread_join error");
#endif
}

static long cpu_count(void)
{
#ifdef _WIN32
DWORD_PTR procmask, sysmask;
long count;
int i;

count = 0;
if (GetProcessAffinityMask(GetCurrentProcess(), &procmask, &sysmask))
for (i = 0; i < 8 * sizeof(procmask); i++)
count += 1 & (procmask >> i);
return count;
#else
return sysconf(_SC_NPROCESSORS_ONLN);
#endif
}

typedef struct {
Expand All @@ -480,7 +572,7 @@ static _Thread_local ThreadLocalStorage *tls;

typedef struct {
struct list_head link;
pthread_t tid;
js_thread_t tid;
char *script;
JSValue broadcast_func;
BOOL broadcast_pending;
Expand Down Expand Up @@ -606,7 +698,7 @@ static void js_agent_free(JSContext *ctx)

list_for_each_safe(el, el1, &tls->agent_list) {
agent = list_entry(el, Test262Agent, link);
pthread_join(agent->tid, NULL);
join_thread(agent->tid);
JS_FreeValue(ctx, agent->broadcast_sab);
list_del(&agent->link);
free(agent);
Expand Down Expand Up @@ -695,15 +787,23 @@ static JSValue js_agent_sleep(JSContext *ctx, JSValue this_val,
uint32_t duration;
if (JS_ToUint32(ctx, &duration, argv[0]))
return JS_EXCEPTION;
#ifdef _WIN32
Sleep(duration);
#else
usleep(duration * 1000);
#endif
return JS_UNDEFINED;
}

static int64_t get_clock_ms(void)
{
#ifdef _WIN32
return GetTickCount64();
#else
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000);
#endif
}

static JSValue js_agent_monotonicNow(JSContext *ctx, JSValue this_val,
Expand Down Expand Up @@ -985,7 +1085,7 @@ void load_config(const char *filename, const char *ignore)
} section = SECTION_NONE;
int lineno = 0;

f = fopen(filename, "rb");
f = fopen(filename, "r");
if (!f) {
perror_exit(1, filename);
}
Expand Down Expand Up @@ -1936,7 +2036,7 @@ void *show_progress(void *unused) {
js_mutex_lock(&progress_mutex);
while (js_cond_timedwait(&progress_cond, &progress_mutex, interval)) {
/* output progress indicator: erase end of line and return to col 0 */
fprintf(stderr, "%d/%d/%d\033[K\r",
fprintf(stderr, "%d/%d/%d \r",
atomic_load(&test_failed),
atomic_load(&test_count),
atomic_load(&test_skipped));
Expand Down Expand Up @@ -2026,12 +2126,14 @@ int main(int argc, char **argv)
init_thread_local_storage(tls);
js_mutex_init(&stats_mutex);

#if !defined(__MINGW32__)
#ifndef _WIN32
/* Date tests assume California local time */
setenv("TZ", "America/Los_Angeles", 1);
nthreads = sysconf(_SC_NPROCESSORS_ONLN) - 1;
#endif

// minus one to not (over)commit the system completely
nthreads = cpu_count() - 1;

optind = 1;
while (optind < argc) {
char *arg = argv[optind];
Expand Down

0 comments on commit 3c3c487

Please sign in to comment.