quickjs 0.19.0 → 0.20.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,27 +34,66 @@
34
34
  #include <time.h>
35
35
  #include <dirent.h>
36
36
  #include <ftw.h>
37
+ #include <stdatomic.h>
38
+ #include <pthread.h>
39
+ #ifdef _WIN32
40
+ #include <windows.h>
41
+ #endif
37
42
 
38
43
  #include "cutils.h"
39
44
  #include "list.h"
40
45
  #include "quickjs-libc.h"
41
46
 
42
- /* enable test262 thread support to test SharedArrayBuffer and Atomics */
43
- #define CONFIG_AGENT
44
-
45
47
  #define CMD_NAME "run-test262"
46
48
 
47
49
  typedef struct namelist_t {
48
50
  char **array;
49
51
  int count;
50
52
  int size;
51
- unsigned int sorted : 1;
52
53
  } namelist_t;
53
54
 
55
+ /* per execution thread context */
56
+ typedef struct {
57
+ pthread_mutex_t agent_mutex;
58
+ pthread_cond_t agent_cond;
59
+ /* list of Test262Agent.link */
60
+ struct list_head agent_list;
61
+
62
+ pthread_mutex_t report_mutex;
63
+ /* list of AgentReport.link */
64
+ struct list_head report_list;
65
+
66
+ int async_done;
67
+ } ThreadLocalStorage;
68
+
69
+ typedef struct {
70
+ struct list_head link;
71
+ ThreadLocalStorage *tls;
72
+ pthread_t tid;
73
+ char *script;
74
+ JSValue broadcast_func;
75
+ BOOL broadcast_pending;
76
+ JSValue broadcast_sab; /* in the main context */
77
+ uint8_t *broadcast_sab_buf;
78
+ size_t broadcast_sab_size;
79
+ int32_t broadcast_val;
80
+ } Test262Agent;
81
+
82
+ typedef struct {
83
+ struct list_head link;
84
+ char *str;
85
+ } AgentReport;
86
+
54
87
  namelist_t test_list;
55
88
  namelist_t exclude_list;
56
89
  namelist_t exclude_dir_list;
57
90
 
91
+ int nthreads;
92
+ pthread_t progress_thread;
93
+ BOOL progress_exit_request;
94
+ pthread_cond_t progress_cond;
95
+ pthread_mutex_t progress_mutex;
96
+
58
97
  FILE *outfile;
59
98
  enum test_mode_t {
60
99
  TEST_DEFAULT_NOSTRICT, /* run tests as nostrict unless test is flagged as strictonly */
@@ -73,6 +112,7 @@ int stats_count;
73
112
  JSMemoryUsage stats_all, stats_avg, stats_min, stats_max;
74
113
  char *stats_min_filename;
75
114
  char *stats_max_filename;
115
+ pthread_mutex_t stats_mutex;
76
116
  int verbose;
77
117
  char *harness_dir;
78
118
  char *harness_exclude;
@@ -84,13 +124,115 @@ char *error_file;
84
124
  FILE *error_out;
85
125
  char *report_filename;
86
126
  int update_errors;
87
- int test_count, test_failed, test_index, test_skipped, test_excluded;
88
- int new_errors, changed_errors, fixed_errors;
89
- int async_done;
127
+ int slow_test_threshold;
128
+ int start_index, stop_index;
129
+ int test_excluded;
130
+ _Atomic int test_count, test_failed, test_skipped;
131
+ _Atomic int new_errors, changed_errors, fixed_errors;
90
132
 
91
133
  void warning(const char *, ...) __attribute__((__format__(__printf__, 1, 2)));
92
134
  void fatal(int, const char *, ...) __attribute__((__format__(__printf__, 2, 3)));
93
135
 
136
+ void atomic_inc(volatile _Atomic int *p)
137
+ {
138
+ atomic_fetch_add(p, 1);
139
+ }
140
+
141
+ #if defined(_WIN32)
142
+ static int cpu_count(void)
143
+ {
144
+ DWORD_PTR procmask, sysmask;
145
+ long count;
146
+ int i;
147
+
148
+ count = 0;
149
+ if (GetProcessAffinityMask(GetCurrentProcess(), &procmask, &sysmask))
150
+ for (i = 0; i < 8 * sizeof(procmask); i++)
151
+ count += 1 & (procmask >> i);
152
+ return count;
153
+ }
154
+ #elif defined(__linux__)
155
+ /* return the number of available physical cores or -1 if not available */
156
+ static int get_cpu_info_physical_cores(void)
157
+ {
158
+ FILE *f;
159
+ int nb_cores, physical_id;
160
+ char line[1024], *p;
161
+ char *field, *value;
162
+ int len;
163
+
164
+ f = fopen("/proc/cpuinfo", "rb");
165
+ if (!f)
166
+ return -1;
167
+ nb_cores = 0;
168
+ physical_id = -1;
169
+ for(;;) {
170
+ if (fgets(line, sizeof(line), f) == NULL)
171
+ break;
172
+ len = strlen(line);
173
+ while (len > 0 && isspace(line[len - 1]))
174
+ len--;
175
+ line[len] = '\0';
176
+ field = line;
177
+ p = line;
178
+ if (*p == '#')
179
+ continue;
180
+ while (*p != ':' && *p != '\0')
181
+ p++;
182
+ if (*p == '\0')
183
+ continue;
184
+ *p = '\0';
185
+ p++;
186
+ while (isspace(*p))
187
+ p++;
188
+ value = p;
189
+
190
+ len = strlen(field);
191
+ while (len > 0 && isspace(field[len - 1]))
192
+ len--;
193
+ field[len] = '\0';
194
+
195
+ // printf("'%s' '%s'\n", field, value);
196
+ if (!strcmp(field, "cpu cores")) {
197
+ if (nb_cores == 0) {
198
+ nb_cores = strtol(value, NULL, 0);
199
+ }
200
+ } else if (!strcmp(field, "physical id")) {
201
+ physical_id = max_int(physical_id, strtol(value, NULL, 0));
202
+ }
203
+ }
204
+ fclose(f);
205
+ // printf("nb_cores=%d physical_id=%d\n", nb_cores, physical_id);
206
+ if (nb_cores <= 0 || physical_id < 0)
207
+ return -1;
208
+ return nb_cores * (physical_id + 1);
209
+ }
210
+
211
+ static int cpu_count(void)
212
+ {
213
+ int n = get_cpu_info_physical_cores();
214
+ if (n <= 0)
215
+ n = 1;
216
+ return n;
217
+ }
218
+ #else /* __linux__ */
219
+ static int cpu_count(void)
220
+ {
221
+ return sysconf(_SC_NPROCESSORS_ONLN);
222
+ }
223
+ #endif /* !__linux__ */
224
+
225
+ static void init_thread_local_storage(ThreadLocalStorage *tls)
226
+ {
227
+ memset(tls, 0, sizeof(*tls));
228
+ pthread_mutex_init(&tls->agent_mutex, NULL);
229
+ pthread_cond_init(&tls->agent_cond, NULL);
230
+ init_list_head(&tls->agent_list);
231
+
232
+ pthread_mutex_init(&tls->report_mutex, NULL);
233
+ init_list_head(&tls->report_list);
234
+ }
235
+
94
236
  void warning(const char *fmt, ...)
95
237
  {
96
238
  va_list ap;
@@ -263,16 +405,13 @@ void namelist_sort(namelist_t *lp)
263
405
  }
264
406
  lp->count = count;
265
407
  }
266
- lp->sorted = 1;
267
408
  }
268
409
 
269
- int namelist_find(namelist_t *lp, const char *name)
410
+ /* the list must be sorted */
411
+ int namelist_find(const namelist_t *lp, const char *name)
270
412
  {
271
413
  int a, b, m, cmp;
272
414
 
273
- if (!lp->sorted) {
274
- namelist_sort(lp);
275
- }
276
415
  for (a = 0, b = lp->count; a < b;) {
277
416
  m = a + (b - a) / 2;
278
417
  cmp = namelist_cmp(lp->array[m], name);
@@ -382,33 +521,37 @@ static void js_print_value_write(void *opaque, const char *buf, size_t len)
382
521
  static JSValue js_print(JSContext *ctx, JSValueConst this_val,
383
522
  int argc, JSValueConst *argv)
384
523
  {
524
+ ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
385
525
  int i;
386
526
  JSValueConst v;
387
527
 
388
- if (outfile) {
389
- for (i = 0; i < argc; i++) {
390
- if (i != 0)
391
- fputc(' ', outfile);
392
- v = argv[i];
393
- if (JS_IsString(v)) {
394
- const char *str;
395
- size_t len;
396
- str = JS_ToCStringLen(ctx, &len, v);
397
- if (!str)
398
- return JS_EXCEPTION;
399
- if (!strcmp(str, "Test262:AsyncTestComplete")) {
400
- async_done++;
401
- } else if (strstart(str, "Test262:AsyncTestFailure", NULL)) {
402
- async_done = 2; /* force an error */
403
- }
528
+ for (i = 0; i < argc; i++) {
529
+ if (i != 0 && outfile)
530
+ fputc(' ', outfile);
531
+ v = argv[i];
532
+ if (JS_IsString(v)) {
533
+ const char *str;
534
+ size_t len;
535
+ str = JS_ToCStringLen(ctx, &len, v);
536
+ if (!str)
537
+ return JS_EXCEPTION;
538
+ if (!strcmp(str, "Test262:AsyncTestComplete")) {
539
+ tls->async_done++;
540
+ } else if (strstart(str, "Test262:AsyncTestFailure", NULL)) {
541
+ tls->async_done = 2; /* force an error */
542
+ }
543
+ if (outfile) {
404
544
  fwrite(str, 1, len, outfile);
405
- JS_FreeCString(ctx, str);
406
- } else {
545
+ }
546
+ JS_FreeCString(ctx, str);
547
+ } else {
548
+ if (outfile) {
407
549
  JS_PrintValue(ctx, js_print_value_write, outfile, v, NULL);
408
550
  }
409
551
  }
410
- fputc('\n', outfile);
411
552
  }
553
+ if (outfile)
554
+ fputc('\n', outfile);
412
555
  return JS_UNDEFINED;
413
556
  }
414
557
 
@@ -433,42 +576,13 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val,
433
576
  return ret;
434
577
  }
435
578
 
436
- #ifdef CONFIG_AGENT
437
-
438
- #include <pthread.h>
439
-
440
- typedef struct {
441
- struct list_head link;
442
- pthread_t tid;
443
- char *script;
444
- JSValue broadcast_func;
445
- BOOL broadcast_pending;
446
- JSValue broadcast_sab; /* in the main context */
447
- uint8_t *broadcast_sab_buf;
448
- size_t broadcast_sab_size;
449
- int32_t broadcast_val;
450
- } Test262Agent;
451
-
452
- typedef struct {
453
- struct list_head link;
454
- char *str;
455
- } AgentReport;
456
-
457
579
  static JSValue add_helpers1(JSContext *ctx);
458
580
  static void add_helpers(JSContext *ctx);
459
581
 
460
- static pthread_mutex_t agent_mutex = PTHREAD_MUTEX_INITIALIZER;
461
- static pthread_cond_t agent_cond = PTHREAD_COND_INITIALIZER;
462
- /* list of Test262Agent.link */
463
- static struct list_head agent_list = LIST_HEAD_INIT(agent_list);
464
-
465
- static pthread_mutex_t report_mutex = PTHREAD_MUTEX_INITIALIZER;
466
- /* list of AgentReport.link */
467
- static struct list_head report_list = LIST_HEAD_INIT(report_list);
468
-
469
582
  static void *agent_start(void *arg)
470
583
  {
471
584
  Test262Agent *agent = arg;
585
+ ThreadLocalStorage *tls = agent->tls;
472
586
  JSRuntime *rt;
473
587
  JSContext *ctx;
474
588
  JSValue ret_val;
@@ -478,6 +592,7 @@ static void *agent_start(void *arg)
478
592
  if (rt == NULL) {
479
593
  fatal(1, "JS_NewRuntime failure");
480
594
  }
595
+ JS_SetRuntimeOpaque(rt, tls);
481
596
  ctx = JS_NewContext(rt);
482
597
  if (ctx == NULL) {
483
598
  JS_FreeRuntime(rt);
@@ -507,15 +622,15 @@ static void *agent_start(void *arg)
507
622
  } else {
508
623
  JSValue args[2];
509
624
 
510
- pthread_mutex_lock(&agent_mutex);
625
+ pthread_mutex_lock(&tls->agent_mutex);
511
626
  while (!agent->broadcast_pending) {
512
- pthread_cond_wait(&agent_cond, &agent_mutex);
627
+ pthread_cond_wait(&tls->agent_cond, &tls->agent_mutex);
513
628
  }
514
629
 
515
630
  agent->broadcast_pending = FALSE;
516
- pthread_cond_signal(&agent_cond);
631
+ pthread_cond_signal(&tls->agent_cond);
517
632
 
518
- pthread_mutex_unlock(&agent_mutex);
633
+ pthread_mutex_unlock(&tls->agent_mutex);
519
634
 
520
635
  args[0] = JS_NewArrayBuffer(ctx, agent->broadcast_sab_buf,
521
636
  agent->broadcast_sab_size,
@@ -543,6 +658,7 @@ static void *agent_start(void *arg)
543
658
  static JSValue js_agent_start(JSContext *ctx, JSValue this_val,
544
659
  int argc, JSValue *argv)
545
660
  {
661
+ ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
546
662
  const char *script;
547
663
  Test262Agent *agent;
548
664
  pthread_attr_t attr;
@@ -555,11 +671,12 @@ static JSValue js_agent_start(JSContext *ctx, JSValue this_val,
555
671
  return JS_EXCEPTION;
556
672
  agent = malloc(sizeof(*agent));
557
673
  memset(agent, 0, sizeof(*agent));
674
+ agent->tls = tls;
558
675
  agent->broadcast_func = JS_UNDEFINED;
559
676
  agent->broadcast_sab = JS_UNDEFINED;
560
677
  agent->script = strdup(script);
561
678
  JS_FreeCString(ctx, script);
562
- list_add_tail(&agent->link, &agent_list);
679
+ list_add_tail(&agent->link, &tls->agent_list);
563
680
  pthread_attr_init(&attr);
564
681
  // musl libc gives threads 80 kb stacks, much smaller than
565
682
  // JS_DEFAULT_STACK_SIZE (256 kb)
@@ -571,10 +688,11 @@ static JSValue js_agent_start(JSContext *ctx, JSValue this_val,
571
688
 
572
689
  static void js_agent_free(JSContext *ctx)
573
690
  {
691
+ ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
574
692
  struct list_head *el, *el1;
575
693
  Test262Agent *agent;
576
694
 
577
- list_for_each_safe(el, el1, &agent_list) {
695
+ list_for_each_safe(el, el1, &tls->agent_list) {
578
696
  agent = list_entry(el, Test262Agent, link);
579
697
  pthread_join(agent->tid, NULL);
580
698
  JS_FreeValue(ctx, agent->broadcast_sab);
@@ -593,11 +711,11 @@ static JSValue js_agent_leaving(JSContext *ctx, JSValue this_val,
593
711
  return JS_UNDEFINED;
594
712
  }
595
713
 
596
- static BOOL is_broadcast_pending(void)
714
+ static BOOL is_broadcast_pending(ThreadLocalStorage *tls)
597
715
  {
598
716
  struct list_head *el;
599
717
  Test262Agent *agent;
600
- list_for_each(el, &agent_list) {
718
+ list_for_each(el, &tls->agent_list) {
601
719
  agent = list_entry(el, Test262Agent, link);
602
720
  if (agent->broadcast_pending)
603
721
  return TRUE;
@@ -608,6 +726,7 @@ static BOOL is_broadcast_pending(void)
608
726
  static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val,
609
727
  int argc, JSValue *argv)
610
728
  {
729
+ ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
611
730
  JSValueConst sab = argv[0];
612
731
  struct list_head *el;
613
732
  Test262Agent *agent;
@@ -626,8 +745,8 @@ static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val,
626
745
 
627
746
  /* broadcast the values and wait until all agents have started
628
747
  calling their callbacks */
629
- pthread_mutex_lock(&agent_mutex);
630
- list_for_each(el, &agent_list) {
748
+ pthread_mutex_lock(&tls->agent_mutex);
749
+ list_for_each(el, &tls->agent_list) {
631
750
  agent = list_entry(el, Test262Agent, link);
632
751
  agent->broadcast_pending = TRUE;
633
752
  /* the shared array buffer is used by the thread, so increment
@@ -637,12 +756,12 @@ static JSValue js_agent_broadcast(JSContext *ctx, JSValue this_val,
637
756
  agent->broadcast_sab_size = buf_size;
638
757
  agent->broadcast_val = val;
639
758
  }
640
- pthread_cond_broadcast(&agent_cond);
759
+ pthread_cond_broadcast(&tls->agent_cond);
641
760
 
642
- while (is_broadcast_pending()) {
643
- pthread_cond_wait(&agent_cond, &agent_mutex);
761
+ while (is_broadcast_pending(tls)) {
762
+ pthread_cond_wait(&tls->agent_cond, &tls->agent_mutex);
644
763
  }
645
- pthread_mutex_unlock(&agent_mutex);
764
+ pthread_mutex_unlock(&tls->agent_mutex);
646
765
  return JS_UNDEFINED;
647
766
  }
648
767
 
@@ -685,17 +804,18 @@ static JSValue js_agent_monotonicNow(JSContext *ctx, JSValue this_val,
685
804
  static JSValue js_agent_getReport(JSContext *ctx, JSValue this_val,
686
805
  int argc, JSValue *argv)
687
806
  {
807
+ ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
688
808
  AgentReport *rep;
689
809
  JSValue ret;
690
810
 
691
- pthread_mutex_lock(&report_mutex);
692
- if (list_empty(&report_list)) {
811
+ pthread_mutex_lock(&tls->report_mutex);
812
+ if (list_empty(&tls->report_list)) {
693
813
  rep = NULL;
694
814
  } else {
695
- rep = list_entry(report_list.next, AgentReport, link);
815
+ rep = list_entry(tls->report_list.next, AgentReport, link);
696
816
  list_del(&rep->link);
697
817
  }
698
- pthread_mutex_unlock(&report_mutex);
818
+ pthread_mutex_unlock(&tls->report_mutex);
699
819
  if (rep) {
700
820
  ret = JS_NewString(ctx, rep->str);
701
821
  free(rep->str);
@@ -709,6 +829,7 @@ static JSValue js_agent_getReport(JSContext *ctx, JSValue this_val,
709
829
  static JSValue js_agent_report(JSContext *ctx, JSValue this_val,
710
830
  int argc, JSValue *argv)
711
831
  {
832
+ ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
712
833
  const char *str;
713
834
  AgentReport *rep;
714
835
 
@@ -719,9 +840,9 @@ static JSValue js_agent_report(JSContext *ctx, JSValue this_val,
719
840
  rep->str = strdup(str);
720
841
  JS_FreeCString(ctx, str);
721
842
 
722
- pthread_mutex_lock(&report_mutex);
723
- list_add_tail(&rep->link, &report_list);
724
- pthread_mutex_unlock(&report_mutex);
843
+ pthread_mutex_lock(&tls->report_mutex);
844
+ list_add_tail(&rep->link, &tls->report_list);
845
+ pthread_mutex_unlock(&tls->report_mutex);
725
846
  return JS_UNDEFINED;
726
847
  }
727
848
 
@@ -747,7 +868,6 @@ static JSValue js_new_agent(JSContext *ctx)
747
868
  countof(js_agent_funcs));
748
869
  return agent;
749
870
  }
750
- #endif
751
871
 
752
872
  static JSValue js_createRealm(JSContext *ctx, JSValue this_val,
753
873
  int argc, JSValue *argv)
@@ -798,9 +918,7 @@ static JSValue add_helpers1(JSContext *ctx)
798
918
  JS_SetPropertyStr(ctx, obj262, "codePointRange",
799
919
  JS_NewCFunction(ctx, js_string_codePointRange,
800
920
  "codePointRange", 2));
801
- #ifdef CONFIG_AGENT
802
921
  JS_SetPropertyStr(ctx, obj262, "agent", js_new_agent(ctx));
803
- #endif
804
922
 
805
923
  JS_SetPropertyStr(ctx, obj262, "global",
806
924
  JS_DupValue(ctx, global_obj));
@@ -947,7 +1065,7 @@ void update_exclude_dirs(void)
947
1065
  char *name;
948
1066
  int i, j, count;
949
1067
 
950
- /* split directpries from exclude_list */
1068
+ /* split directories from exclude_list */
951
1069
  for (count = i = 0; i < ep->count; i++) {
952
1070
  name = ep->array[i];
953
1071
  if (has_suffix(name, "/")) {
@@ -1245,6 +1363,7 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
1245
1363
  const char *error_type, FILE *outfile, int eval_flags,
1246
1364
  int is_async)
1247
1365
  {
1366
+ ThreadLocalStorage *tls = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
1248
1367
  JSValue res_val, exception_val;
1249
1368
  int ret, error_line, pos, pos_line;
1250
1369
  BOOL is_error, has_error_line, ret_promise;
@@ -1258,7 +1377,7 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
1258
1377
 
1259
1378
  /* a module evaluation returns a promise */
1260
1379
  ret_promise = ((eval_flags & JS_EVAL_TYPE_MODULE) != 0);
1261
- async_done = 0; /* counter of "Test262:AsyncTestComplete" messages */
1380
+ tls->async_done = 0; /* counter of "Test262:AsyncTestComplete" messages */
1262
1381
 
1263
1382
  res_val = JS_Eval(ctx, buf, buf_len, filename, eval_flags);
1264
1383
 
@@ -1277,7 +1396,7 @@ static int eval_buf(JSContext *ctx, const char *buf, size_t buf_len,
1277
1396
  } else if (ret == 0) {
1278
1397
  if (is_async) {
1279
1398
  /* test if the test called $DONE() once */
1280
- if (async_done != 1) {
1399
+ if (tls->async_done != 1) {
1281
1400
  res_val = JS_ThrowTypeError(ctx, "$DONE() not called");
1282
1401
  } else {
1283
1402
  res_val = JS_UNDEFINED;
@@ -1545,6 +1664,8 @@ static char *get_option(char **pp, int *state)
1545
1664
  void update_stats(JSRuntime *rt, const char *filename) {
1546
1665
  JSMemoryUsage stats;
1547
1666
  JS_ComputeMemoryUsage(rt, &stats);
1667
+
1668
+ pthread_mutex_lock(&stats_mutex);
1548
1669
  if (stats_count++ == 0) {
1549
1670
  stats_avg = stats_all = stats_min = stats_max = stats;
1550
1671
  stats_min_filename = strdup(filename);
@@ -1587,9 +1708,11 @@ void update_stats(JSRuntime *rt, const char *filename) {
1587
1708
  update(fast_array_elements);
1588
1709
  }
1589
1710
  #undef update
1711
+ pthread_mutex_unlock(&stats_mutex);
1590
1712
  }
1591
1713
 
1592
- int run_test_buf(const char *filename, const char *harness, namelist_t *ip,
1714
+ int run_test_buf(ThreadLocalStorage *tls,
1715
+ const char *filename, const char *harness, namelist_t *ip,
1593
1716
  char *buf, size_t buf_len, const char* error_type,
1594
1717
  int eval_flags, BOOL is_negative, BOOL is_async,
1595
1718
  BOOL can_block)
@@ -1602,6 +1725,7 @@ int run_test_buf(const char *filename, const char *harness, namelist_t *ip,
1602
1725
  if (rt == NULL) {
1603
1726
  fatal(1, "JS_NewRuntime failure");
1604
1727
  }
1728
+ JS_SetRuntimeOpaque(rt, tls);
1605
1729
  ctx = JS_NewContext(rt);
1606
1730
  if (ctx == NULL) {
1607
1731
  JS_FreeRuntime(rt);
@@ -1630,15 +1754,13 @@ int run_test_buf(const char *filename, const char *harness, namelist_t *ip,
1630
1754
  if (dump_memory) {
1631
1755
  update_stats(rt, filename);
1632
1756
  }
1633
- #ifdef CONFIG_AGENT
1634
1757
  js_agent_free(ctx);
1635
- #endif
1636
1758
  JS_FreeContext(ctx);
1637
1759
  JS_FreeRuntime(rt);
1638
1760
 
1639
- test_count++;
1761
+ atomic_inc(&test_count);
1640
1762
  if (ret) {
1641
- test_failed++;
1763
+ atomic_inc(&test_failed);
1642
1764
  if (outfile) {
1643
1765
  /* do not output a failure number to minimize diff */
1644
1766
  fprintf(outfile, " FAILED\n");
@@ -1647,7 +1769,7 @@ int run_test_buf(const char *filename, const char *harness, namelist_t *ip,
1647
1769
  return ret;
1648
1770
  }
1649
1771
 
1650
- int run_test(const char *filename, int index)
1772
+ int run_test(ThreadLocalStorage *tls, const char *filename, int index)
1651
1773
  {
1652
1774
  char harnessbuf[1024];
1653
1775
  char *harness;
@@ -1853,7 +1975,7 @@ int run_test(const char *filename, int index)
1853
1975
  }
1854
1976
 
1855
1977
  if (skip || use_strict + use_nostrict == 0) {
1856
- test_skipped++;
1978
+ atomic_inc(&test_skipped);
1857
1979
  ret = -2;
1858
1980
  } else {
1859
1981
  clock_t clocks;
@@ -1866,12 +1988,12 @@ int run_test(const char *filename, int index)
1866
1988
  clocks = clock();
1867
1989
  ret = 0;
1868
1990
  if (use_nostrict) {
1869
- ret = run_test_buf(filename, harness, ip, buf, buf_len,
1991
+ ret = run_test_buf(tls, filename, harness, ip, buf, buf_len,
1870
1992
  error_type, eval_flags, is_negative, is_async,
1871
1993
  can_block);
1872
1994
  }
1873
1995
  if (use_strict) {
1874
- ret |= run_test_buf(filename, harness, ip, buf, buf_len,
1996
+ ret |= run_test_buf(tls, filename, harness, ip, buf, buf_len,
1875
1997
  error_type, eval_flags | JS_EVAL_FLAG_STRICT,
1876
1998
  is_negative, is_async, can_block);
1877
1999
  }
@@ -1889,7 +2011,8 @@ int run_test(const char *filename, int index)
1889
2011
  }
1890
2012
 
1891
2013
  /* run a test when called by test262-harness+eshost */
1892
- int run_test262_harness_test(const char *filename, BOOL is_module)
2014
+ int run_test262_harness_test(ThreadLocalStorage *tls,
2015
+ const char *filename, BOOL is_module, BOOL can_block)
1893
2016
  {
1894
2017
  JSRuntime *rt;
1895
2018
  JSContext *ctx;
@@ -1897,7 +2020,6 @@ int run_test262_harness_test(const char *filename, BOOL is_module)
1897
2020
  size_t buf_len;
1898
2021
  int eval_flags, ret_code, ret;
1899
2022
  JSValue res_val;
1900
- BOOL can_block;
1901
2023
 
1902
2024
  outfile = stdout; /* for js_print */
1903
2025
 
@@ -1905,6 +2027,7 @@ int run_test262_harness_test(const char *filename, BOOL is_module)
1905
2027
  if (rt == NULL) {
1906
2028
  fatal(1, "JS_NewRuntime failure");
1907
2029
  }
2030
+ JS_SetRuntimeOpaque(rt, tls);
1908
2031
  ctx = JS_NewContext(rt);
1909
2032
  if (ctx == NULL) {
1910
2033
  JS_FreeRuntime(rt);
@@ -1912,7 +2035,6 @@ int run_test262_harness_test(const char *filename, BOOL is_module)
1912
2035
  }
1913
2036
  JS_SetRuntimeInfo(rt, filename);
1914
2037
 
1915
- can_block = TRUE;
1916
2038
  JS_SetCanBlock(rt, can_block);
1917
2039
 
1918
2040
  /* loader for ES6 modules */
@@ -1960,78 +2082,115 @@ int run_test262_harness_test(const char *filename, BOOL is_module)
1960
2082
  JS_FreeValue(ctx, promise);
1961
2083
  }
1962
2084
  free(buf);
1963
- #ifdef CONFIG_AGENT
1964
2085
  js_agent_free(ctx);
1965
- #endif
1966
2086
  JS_FreeContext(ctx);
1967
2087
  JS_FreeRuntime(rt);
1968
2088
  return ret_code;
1969
2089
  }
1970
2090
 
1971
- clock_t last_clock;
2091
+ static int pthread_cond_timedwait2(pthread_cond_t *cond, pthread_mutex_t *mutex, int timeout)
2092
+ {
2093
+ struct timespec ts;
2094
+
2095
+ clock_gettime(CLOCK_REALTIME, &ts);
2096
+ ts.tv_sec += timeout / 1000;
2097
+ ts.tv_nsec += (timeout % 1000) * 1000000;
2098
+ if (ts.tv_nsec >= 1000000000) {
2099
+ ts.tv_nsec -= 1000000000;
2100
+ ts.tv_sec++;
2101
+ }
2102
+ return pthread_cond_timedwait(cond, mutex, &ts);
2103
+ }
2104
+
2105
+ void *show_progress(void *opaque)
2106
+ {
2107
+ int test_skipped1, test_failed1, test_count1;
2108
+
2109
+ pthread_mutex_lock(&progress_mutex);
2110
+ for(;;) {
2111
+ pthread_cond_timedwait2(&progress_cond, &progress_mutex, 50);
2112
+
2113
+ test_failed1 = atomic_load(&test_failed);
2114
+ test_count1 = atomic_load(&test_count);
2115
+ test_skipped1 = atomic_load(&test_skipped);
1972
2116
 
1973
- void show_progress(int force) {
1974
- clock_t t = clock();
1975
- if (force || !last_clock || (t - last_clock) > CLOCKS_PER_SEC / 20) {
1976
- last_clock = t;
1977
2117
  if (compact) {
1978
2118
  static int last_test_skipped;
1979
2119
  static int last_test_failed;
1980
2120
  static int dots;
1981
2121
  char c = '.';
1982
- if (test_skipped > last_test_skipped)
2122
+
2123
+ if (test_skipped1 > last_test_skipped)
1983
2124
  c = '-';
1984
- if (test_failed > last_test_failed)
2125
+ if (test_failed1 > last_test_failed)
1985
2126
  c = '!';
1986
- last_test_skipped = test_skipped;
1987
- last_test_failed = test_failed;
2127
+ last_test_skipped = test_skipped1;
2128
+ last_test_failed = test_failed1;
2129
+
1988
2130
  fputc(c, stderr);
1989
- if (force || ++dots % 60 == 0) {
2131
+ if (progress_exit_request || ++dots % 60 == 0) {
1990
2132
  fprintf(stderr, " %d/%d/%d\n",
1991
- test_failed, test_count, test_skipped);
2133
+ test_failed1, test_count1, test_skipped1);
1992
2134
  }
1993
2135
  } else {
1994
2136
  /* output progress indicator: erase end of line and return to col 0 */
1995
2137
  fprintf(stderr, "%d/%d/%d\033[K\r",
1996
- test_failed, test_count, test_skipped);
2138
+ test_failed1, test_count1, test_skipped1);
1997
2139
  }
1998
2140
  fflush(stderr);
2141
+ if (progress_exit_request)
2142
+ break;
1999
2143
  }
2144
+ pthread_mutex_unlock(&progress_mutex);
2145
+ return NULL;
2000
2146
  }
2001
2147
 
2002
- static int slow_test_threshold;
2148
+ enum { INCLUDE, EXCLUDE, SKIP };
2003
2149
 
2004
- void run_test_dir_list(namelist_t *lp, int start_index, int stop_index)
2150
+ int include_exclude_or_skip(int i) // naming is hard...
2005
2151
  {
2006
- int i;
2152
+ if (namelist_find(&exclude_list, test_list.array[i]) >= 0)
2153
+ return EXCLUDE;
2154
+ if (i < start_index)
2155
+ return SKIP;
2156
+ if (stop_index >= 0 && i > stop_index)
2157
+ return SKIP;
2158
+ return INCLUDE;
2159
+ }
2007
2160
 
2008
- namelist_sort(lp);
2009
- for (i = 0; i < lp->count; i++) {
2161
+ typedef struct {
2162
+ pthread_t tid;
2163
+ int thread_index;
2164
+ } RunTestDirThread;
2165
+
2166
+ void *run_test_dir_list(void *opaque)
2167
+ {
2168
+ RunTestDirThread *th = opaque;
2169
+ ThreadLocalStorage tls_s, *tls = &tls_s;
2170
+ namelist_t *lp = &test_list;
2171
+ int i;
2172
+
2173
+ init_thread_local_storage(tls);
2174
+
2175
+ for (i = th->thread_index; i < lp->count; i += nthreads) {
2010
2176
  const char *p = lp->array[i];
2011
- if (namelist_find(&exclude_list, p) >= 0) {
2012
- test_excluded++;
2013
- } else if (test_index < start_index) {
2014
- test_skipped++;
2015
- } else if (stop_index >= 0 && test_index > stop_index) {
2016
- test_skipped++;
2177
+ int ti;
2178
+ if (INCLUDE != include_exclude_or_skip(i))
2179
+ continue;
2180
+
2181
+ if (slow_test_threshold != 0) {
2182
+ ti = get_clock_ms();
2017
2183
  } else {
2018
- int ti;
2019
- if (slow_test_threshold != 0) {
2020
- ti = get_clock_ms();
2021
- } else {
2022
- ti = 0;
2023
- }
2024
- run_test(p, test_index);
2025
- if (slow_test_threshold != 0) {
2026
- ti = get_clock_ms() - ti;
2027
- if (ti >= slow_test_threshold)
2028
- fprintf(stderr, "\n%s (%d ms)\n", p, ti);
2029
- }
2030
- show_progress(FALSE);
2184
+ ti = 0;
2185
+ }
2186
+ run_test(tls, p, i);
2187
+ if (slow_test_threshold != 0) {
2188
+ ti = get_clock_ms() - ti;
2189
+ if (ti >= slow_test_threshold)
2190
+ fprintf(stderr, "\n%s (%d ms)\n", p, ti);
2031
2191
  }
2032
- test_index++;
2033
2192
  }
2034
- show_progress(TRUE);
2193
+ return NULL;
2035
2194
  }
2036
2195
 
2037
2196
  void help(void)
@@ -2049,13 +2208,15 @@ void help(void)
2049
2208
  "-t show timings\n"
2050
2209
  "-u update error file\n"
2051
2210
  "-v verbose: output error messages\n"
2052
- "-T duration display tests taking more than 'duration' ms\n"
2211
+ "-D duration display tests taking more than 'duration' ms\n"
2212
+ "-T threads number of parallel threads\n"
2053
2213
  "-c file read configuration from 'file'\n"
2054
2214
  "-d dir run all test files in directory tree 'dir'\n"
2055
2215
  "-e file load the known errors from 'file'\n"
2056
2216
  "-f file execute single test from 'file'\n"
2057
2217
  "-r file set the report file name (default=none)\n"
2058
- "-x file exclude tests listed in 'file'\n");
2218
+ "-x file exclude tests listed in 'file'\n"
2219
+ "--no-can-block set [[CanBlock]] to false (Atomics.wait will throw)\n");
2059
2220
  exit(1);
2060
2221
  }
2061
2222
 
@@ -2069,15 +2230,20 @@ char *get_opt_arg(const char *option, char *arg)
2069
2230
 
2070
2231
  int main(int argc, char **argv)
2071
2232
  {
2072
- int optind, start_index, stop_index;
2233
+ ThreadLocalStorage tls_s, *tls = &tls_s;
2234
+ int optind;
2073
2235
  BOOL is_dir_list;
2074
2236
  BOOL only_check_errors = FALSE;
2075
2237
  const char *filename;
2076
2238
  const char *ignore = "";
2077
2239
  BOOL is_test262_harness = FALSE;
2078
2240
  BOOL is_module = FALSE;
2241
+ BOOL can_block = TRUE;
2079
2242
  BOOL count_skipped_features = FALSE;
2080
2243
  clock_t clocks;
2244
+
2245
+ init_thread_local_storage(tls);
2246
+ pthread_mutex_init(&stats_mutex, NULL);
2081
2247
 
2082
2248
  #if !defined(_WIN32)
2083
2249
  compact = !isatty(STDERR_FILENO);
@@ -2091,7 +2257,7 @@ int main(int argc, char **argv)
2091
2257
  if (*arg != '-')
2092
2258
  break;
2093
2259
  optind++;
2094
- if (strstr("-c -d -e -x -f -r -E -T", arg))
2260
+ if (strstr("-c -d -e -x -f -r -E -D -T", arg))
2095
2261
  optind++;
2096
2262
  if (strstr("-d -f", arg))
2097
2263
  ignore = "testdir"; // run only the tests from -d or -f
@@ -2138,12 +2304,16 @@ int main(int argc, char **argv)
2138
2304
  report_filename = get_opt_arg(arg, argv[optind++]);
2139
2305
  } else if (str_equal(arg, "-E")) {
2140
2306
  only_check_errors = TRUE;
2141
- } else if (str_equal(arg, "-T")) {
2307
+ } else if (str_equal(arg, "-D")) {
2142
2308
  slow_test_threshold = atoi(get_opt_arg(arg, argv[optind++]));
2309
+ } else if (str_equal(arg, "-T")) {
2310
+ nthreads = atoi(get_opt_arg(arg, argv[optind++]));
2143
2311
  } else if (str_equal(arg, "-N")) {
2144
2312
  is_test262_harness = TRUE;
2145
2313
  } else if (str_equal(arg, "--module")) {
2146
2314
  is_module = TRUE;
2315
+ } else if (str_equal(arg, "--no-can-block")) {
2316
+ can_block = FALSE;
2147
2317
  } else if (str_equal(arg, "--count_skipped_features")) {
2148
2318
  count_skipped_features = TRUE;
2149
2319
  } else {
@@ -2156,9 +2326,18 @@ int main(int argc, char **argv)
2156
2326
  help();
2157
2327
 
2158
2328
  if (is_test262_harness) {
2159
- return run_test262_harness_test(argv[optind], is_module);
2329
+ return run_test262_harness_test(tls, argv[optind], is_module, can_block);
2160
2330
  }
2161
2331
 
2332
+ if (nthreads == 0) {
2333
+ nthreads = cpu_count();
2334
+ if (nthreads >= 8) {
2335
+ // minus one to not (over)commit the system completely
2336
+ nthreads--;
2337
+ }
2338
+ }
2339
+ nthreads = max_int(nthreads, 1);
2340
+
2162
2341
  error_out = stdout;
2163
2342
  if (error_filename) {
2164
2343
  error_file = load_file(error_filename, NULL);
@@ -2189,10 +2368,14 @@ int main(int argc, char **argv)
2189
2368
  }
2190
2369
 
2191
2370
  if (is_dir_list) {
2371
+ RunTestDirThread *threads;
2372
+ int i;
2373
+
2192
2374
  if (optind < argc && !isdigit((unsigned char)argv[optind][0])) {
2193
2375
  filename = argv[optind++];
2194
2376
  namelist_load(&test_list, filename);
2195
2377
  }
2378
+
2196
2379
  start_index = 0;
2197
2380
  stop_index = -1;
2198
2381
  if (optind < argc) {
@@ -2201,7 +2384,8 @@ int main(int argc, char **argv)
2201
2384
  stop_index = atoi(argv[optind++]);
2202
2385
  }
2203
2386
  }
2204
- if (!report_filename || str_equal(report_filename, "none")) {
2387
+ /* XXX: could reorder the report and the errors when nthreads > 1 */
2388
+ if (!report_filename || str_equal(report_filename, "none") || nthreads > 1) {
2205
2389
  outfile = NULL;
2206
2390
  } else if (str_equal(report_filename, "-")) {
2207
2391
  outfile = stdout;
@@ -2211,7 +2395,50 @@ int main(int argc, char **argv)
2211
2395
  perror_exit(1, report_filename);
2212
2396
  }
2213
2397
  }
2214
- run_test_dir_list(&test_list, start_index, stop_index);
2398
+
2399
+ // exclude_dir_list has already been sorted by update_exclude_dirs()
2400
+ namelist_sort(&test_list);
2401
+ namelist_sort(&exclude_list);
2402
+
2403
+ for (i = 0; i < test_list.count; i++) {
2404
+ switch (include_exclude_or_skip(i)) {
2405
+ case EXCLUDE:
2406
+ test_excluded++;
2407
+ break;
2408
+ case SKIP:
2409
+ test_skipped++;
2410
+ break;
2411
+ }
2412
+ }
2413
+
2414
+ pthread_cond_init(&progress_cond, NULL);
2415
+ pthread_mutex_init(&progress_mutex, NULL);
2416
+ pthread_create(&progress_thread, NULL, show_progress, NULL);
2417
+
2418
+ threads = malloc(sizeof(threads[0]) * nthreads);
2419
+ for (i = 0; i < nthreads; i++) {
2420
+ RunTestDirThread *th = &threads[i];
2421
+ pthread_attr_t attr;
2422
+
2423
+ th->thread_index = i;
2424
+
2425
+ pthread_attr_init(&attr);
2426
+ pthread_attr_setstacksize(&attr, 2 << 20); // 2 MB, glibc default
2427
+ pthread_create(&th->tid, &attr, run_test_dir_list, th);
2428
+ pthread_attr_destroy(&attr);
2429
+ }
2430
+ for (i = 0; i < nthreads; i++)
2431
+ pthread_join(threads[i].tid, NULL);
2432
+ free(threads);
2433
+
2434
+ pthread_mutex_lock(&progress_mutex);
2435
+ progress_exit_request = TRUE;
2436
+ pthread_cond_signal(&progress_cond);
2437
+ pthread_mutex_unlock(&progress_mutex);
2438
+ pthread_join(progress_thread, NULL);
2439
+
2440
+ pthread_mutex_destroy(&progress_mutex);
2441
+ pthread_cond_destroy(&progress_cond);
2215
2442
 
2216
2443
  if (outfile && outfile != stdout) {
2217
2444
  fclose(outfile);
@@ -2220,7 +2447,7 @@ int main(int argc, char **argv)
2220
2447
  } else {
2221
2448
  outfile = stdout;
2222
2449
  while (optind < argc) {
2223
- run_test(argv[optind++], -1);
2450
+ run_test(tls, argv[optind++], -1);
2224
2451
  }
2225
2452
  }
2226
2453
 
@@ -2279,7 +2506,7 @@ int main(int argc, char **argv)
2279
2506
  }
2280
2507
  fprintf(stderr, "\n");
2281
2508
  if (show_timings)
2282
- fprintf(stderr, "Total time: %.3fs\n", (double)clocks / CLOCKS_PER_SEC);
2509
+ fprintf(stderr, "Total user time: %.3fs (nthreads=%d)\n", (double)clocks / CLOCKS_PER_SEC, nthreads);
2283
2510
  }
2284
2511
 
2285
2512
  if (error_out && error_out != stdout) {