"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.startHTTPThreads = startHTTPThreads;
exports.updateWorkerIdleness = updateWorkerIdleness;
const manageThreads_js_1 = require("./manageThreads.js");
const hdbTerms = __importStar(require("../../utility/hdbTerms.js"));
const harperLogger = __importStar(require("../../utility/logging/harper_logger.js"));
const write_ts_1 = require("../../resources/analytics/write.js");
const worker_threads_1 = require("worker_threads");
const path_1 = require("path");
const workers = [];
const workersReady = [];
if (worker_threads_1.isMainThread) {
    process.on('uncaughtException', (error) => {
        // TODO: Maybe we should try to log the first of each type of error
        if (error.code === 'ECONNRESET')
            return; // that's what network connections do
        if (error.code === 'EIO') {
            // that means the terminal is closed
            harperLogger.disableStdio();
            return;
        }
        console.error('uncaughtException', error);
    });
}
async function startHTTPThreads(threadCount = 2, dynamicThreads) {
    (0, write_ts_1.recordHostname)().catch((err) => harperLogger.error?.('Error recording hostname for analytics:', err));
    try {
        if (dynamicThreads) {
            startHTTPWorker(0, 1);
        }
        else {
            const { loadRootComponents } = require('../loadRootComponents.js');
            if (threadCount === 0) {
                (0, manageThreads_js_1.setMainIsWorker)(true);
                await require('./threadServer.js').startServers();
                return Promise.resolve([]);
            }
            await loadRootComponents();
            const { listenOnPorts } = require('./threadServer.js');
            await listenOnPorts();
            // Windows does not support SO_REUSEPORT, so only a single HTTP worker is supported.
            if (process.platform === 'win32')
                threadCount = 1;
        }
        for (let i = 0; i < threadCount; i++) {
            startHTTPWorker(i, threadCount);
        }
        await Promise.all(workersReady);
    }
    finally {
        (0, manageThreads_js_1.threadsHaveStarted)(undefined);
    }
}
function startHTTPWorker(index, threadCount = 1) {
    (0, manageThreads_js_1.startWorker)((0, path_1.join)(__dirname, './threadServer.js'), {
        name: hdbTerms.THREAD_TYPES.HTTP,
        workerIndex: index,
        threadCount,
        async onStarted(worker) {
            // note that this can be called multiple times, once when started, and again when threads are restarted
            const ready = new Promise((resolve, reject) => {
                function onMessage(message) {
                    if (message.type === 'child_started') {
                        worker.removeListener('message', onMessage);
                        resolve(worker);
                    }
                }
                worker.on('message', onMessage);
                worker.on('error', reject);
            });
            workersReady.push(ready);
            await ready;
            workers.push(worker);
            worker.on('exit', removeWorker);
            worker.on('shutdown', removeWorker);
            function removeWorker() {
                const index = workers.indexOf(worker);
                if (index > -1)
                    workers.splice(index, 1);
            }
        },
    });
}
// basically, the amount of additional idleness to expect based on previous idleness (some work will continue, some
// won't)
const EXPECTED_IDLE_DECAY = 1000;
/**
 * Updates the idleness statistics for each worker
 */
function updateWorkerIdleness() {
    for (const worker of workers) {
        worker.expectedIdle = worker.recentELU.idle + EXPECTED_IDLE_DECAY;
        worker.requests = 1;
    }
    workers.sort((a, b) => (a.expectedIdle > b.expectedIdle ? -1 : 1));
}
(0, manageThreads_js_1.setMonitorListener)(updateWorkerIdleness);
//# sourceMappingURL=socketRouter.js.map