1 | import os from 'os';
|
2 |
|
3 | import { StrykerOptions } from '@stryker-mutator/api/core';
|
4 | import { ReplaySubject, Observable, range } from 'rxjs';
|
5 | import { Disposable, tokens } from 'typed-inject';
|
6 | import { commonTokens } from '@stryker-mutator/api/plugin';
|
7 | import { Logger } from '@stryker-mutator/api/logging';
|
8 |
|
9 | export class ConcurrencyTokenProvider implements Disposable {
|
10 | private readonly concurrencyCheckers: number;
|
11 | private readonly concurrencyTestRunners: number;
|
12 | private readonly testRunnerTokenSubject = new ReplaySubject<number>();
|
13 |
|
14 | public get testRunnerToken$(): Observable<number> {
|
15 | return this.testRunnerTokenSubject;
|
16 | }
|
17 | public readonly checkerToken$: Observable<number>;
|
18 | public static readonly inject = tokens(commonTokens.options, commonTokens.logger);
|
19 |
|
20 | constructor(options: Pick<StrykerOptions, 'checkers' | 'concurrency'>, private readonly log: Logger) {
|
21 | const cpuCount = os.cpus().length;
|
22 | const concurrency = options.concurrency ?? (cpuCount > 4 ? cpuCount - 1 : cpuCount);
|
23 | if (options.checkers.length > 0) {
|
24 | this.concurrencyCheckers = Math.max(Math.ceil(concurrency / 2), 1);
|
25 | this.checkerToken$ = range(this.concurrencyCheckers);
|
26 | this.concurrencyTestRunners = Math.max(Math.floor(concurrency / 2), 1);
|
27 | log.info('Creating %s checker process(es) and %s test runner process(es).', this.concurrencyCheckers, this.concurrencyTestRunners);
|
28 | } else {
|
29 | this.concurrencyCheckers = 0;
|
30 | this.checkerToken$ = range(1);
|
31 | this.concurrencyTestRunners = concurrency;
|
32 | log.info('Creating %s test runner process(es).', this.concurrencyTestRunners);
|
33 | }
|
34 | Array.from({ length: this.concurrencyTestRunners }).forEach(() => this.testRunnerTokenSubject.next(this.tick()));
|
35 | }
|
36 |
|
37 | public freeCheckers(): void {
|
38 | if (this.concurrencyCheckers > 0) {
|
39 | this.log.debug('Checking done, creating %s additional test runner process(es)', this.concurrencyCheckers);
|
40 | for (let i = 0; i < this.concurrencyCheckers; i++) {
|
41 | this.testRunnerTokenSubject.next(this.tick());
|
42 | }
|
43 | this.testRunnerTokenSubject.complete();
|
44 | }
|
45 | }
|
46 |
|
47 | private count = 0;
|
48 | private tick() {
|
49 | return this.count++;
|
50 | }
|
51 |
|
52 | public dispose(): void {
|
53 | this.testRunnerTokenSubject.complete();
|
54 | }
|
55 | }
|