"use strict";
/*
 * Copyright 2018, collaboration Factory AG. All rights reserved.
 */
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExecutorService = void 0;
const generic_pool_1 = require("generic-pool");
const path = require("path");
const child_process_1 = require("child_process");
const interfaces_1 = require("../compiler/interfaces");
const utils_1 = require("../utils");
const COMPILER_ENTRY_POINT = path.resolve(path.dirname(__filename), '../compiler/index.js');
const Processfactory = {
    create: () => __awaiter(void 0, void 0, void 0, function* () {
        return child_process_1.fork(COMPILER_ENTRY_POINT);
    }),
    // @ts-ignore
    destroy: (_process) => __awaiter(void 0, void 0, void 0, function* () {
        return _process.kill();
    }),
};
class ExecutorService {
    constructor(maxParallelism) {
        this.maxParallelism = maxParallelism;
        this.running = 0;
        this.childProcesses = new Map();
        utils_1.debug(`(ExecutorService) got maxParallelism: ${maxParallelism}`);
        this.pool = generic_pool_1.createPool(Processfactory, {
            max: maxParallelism,
        });
    }
    hasCapacity() {
        return this.running < this.maxParallelism;
    }
    run(compileRequest, watchFiles) {
        this.running++;
        return new Promise((resolve, reject) => {
            this.pool.acquire().then((process) => {
                if (!watchFiles) {
                    if (process.pid != null) {
                        this.childProcesses.set(process.pid, process);
                        utils_1.debug(`⟲ Added child process with pid ${process.pid}`);
                    }
                }
                let resolved = false;
                const exit = (code) => {
                    if (!resolved) {
                        this.pool.release(process);
                        this.running--;
                        reject(code);
                    }
                };
                process.send(compileRequest);
                process.once('error', () => {
                    this.killAllProcesses();
                });
                process.once('message', (message) => {
                    process.removeListener('exit', exit);
                    this.pool.release(process);
                    resolved = true;
                    if (message.state === interfaces_1.ProcessState.DONE) {
                        this.running--;
                        resolve(message.result);
                    }
                    else {
                        this.running--;
                        if (watchFiles) {
                            reject(2);
                        }
                        else {
                            this.killAllProcesses();
                        }
                    }
                });
                process.once('exit', exit);
            }, (e) => {
                this.running--;
                throw Error(`failed to acquire process: ${e}`);
            });
        });
    }
    /**
     * Kills all running processes
     * @private
     */
    killAllProcesses() {
        this.childProcesses.forEach((childProcess, pid) => {
            console.log(`⟲ Killing child process with pid ${pid}`);
            childProcess.kill();
        });
        console.log(`⟲ Shutting down the main process with pid ${process.pid}!`);
        process.exit(1);
    }
    destroy() {
        let drainPromise = this.pool.drain();
        drainPromise.then(() => this.pool.clear(), () => this.pool.clear());
        return drainPromise;
    }
}
exports.ExecutorService = ExecutorService;
//# sourceMappingURL=ExecutorService.js.map