From e8b9303b4cec783c42cbdef6bbd6fefb4abafae1 Mon Sep 17 00:00:00 2001 From: 1computer1 Date: Fri, 17 May 2019 01:55:24 -0400 Subject: [PATCH] Add concurrent setting --- README.md | 3 ++- config.example.json | 3 ++- src/struct/LanguageHandler.js | 41 ++++++++++++++++++++--------------- src/struct/Queue.js | 38 ++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 19 deletions(-) create mode 100644 src/struct/Queue.js diff --git a/README.md b/README.md index 5d3bbb6..4dc4b88 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,7 @@ The container is locked down, so there is no networking, limited memory and CPU - `memory` Max memory usage of a container. - `cpu` Max CPU usage of a container. - `timeout` Time limit for code in milliseconds. - - `prepare` Whether to run containers on setup. + - `prepare` Whether to start containers on setup. Setting to true will speed up the first eval, but that language might not be used. + - `concurrent` Number of code evaluations per language than can run at a time. 0. Run `node .` diff --git a/config.example.json b/config.example.json index 45b3d70..925eb55 100644 --- a/config.example.json +++ b/config.example.json @@ -11,5 +11,6 @@ "memory": "256m", "cpus": "0.25", "timeout": 10000, - "prepare": false + "prepare": false, + "concurrent": 10 } diff --git a/src/struct/LanguageHandler.js b/src/struct/LanguageHandler.js index 809e181..547be15 100644 --- a/src/struct/LanguageHandler.js +++ b/src/struct/LanguageHandler.js @@ -1,6 +1,7 @@ const { AkairoHandler } = require('discord-akairo'); const { Collection } = require('discord.js'); const Language = require('./Language'); +const Queue = require('./Queue'); const childProcess = require('child_process'); const util = require('util'); const path = require('path'); @@ -24,6 +25,7 @@ class LanguageHandler extends AkairoHandler { this.aliases = new Collection(); this.containers = new Collection(); + this.queues = new Collection(); } register(language, filepath) { @@ -56,6 +58,7 @@ class LanguageHandler extends AkairoHandler { return Promise.all(loads.map(async name => { const folder = path.join(__dirname, '../../docker', name); await util.promisify(childProcess.exec)(`docker build -t "1computer1/comp_iler:${name}" ${folder}`); + this.queues.set(id, new Queue(10)); if (this.client.config.prepare) { await this.setupContainer(id); } @@ -89,25 +92,29 @@ class LanguageHandler extends AkairoHandler { this.containers.get(id).count += 1; } - async evalCode({ language, code, options }) { + evalCode({ language, code, options }) { const { id = language.id, env = {} } = language.runWith(options); - const { name, count } = await this.setupContainer(id); - this.incrementCount(id); - const proc = childProcess.spawn('docker', [ - 'exec', - `-eCOUNT=${count}`, - ...Object.entries(env).map(([k, v]) => `-e${k}=${v}`), - name, '/bin/sh', '/var/run/run.sh', code - ]); + const queue = this.queues.get(id); + return queue.enqueue(async () => { + const { name, count } = await this.setupContainer(id); + this.incrementCount(id); - try { - const result = await this.handleSpawn(proc); - return result; - } catch (err) { - this.containers.delete(id); - await this.kill(name); - throw err; - } + const proc = childProcess.spawn('docker', [ + 'exec', + `-eCOUNT=${count}`, + ...Object.entries(env).map(([k, v]) => `-e${k}=${v}`), + name, '/bin/sh', '/var/run/run.sh', code + ]); + + try { + const result = await this.handleSpawn(proc); + return result; + } catch (err) { + this.containers.delete(id); + await this.kill(name); + throw err; + } + }); } handleSpawn(proc) { diff --git a/src/struct/Queue.js b/src/struct/Queue.js new file mode 100644 index 0000000..4c89d5f --- /dev/null +++ b/src/struct/Queue.js @@ -0,0 +1,38 @@ +class Queue { + constructor(limit) { + this.limit = limit; + this.tasks = []; + this.ongoing = 0; + } + + get length() { + return this.tasks.length; + } + + enqueue(task) { + return new Promise((resolve, reject) => { + this.tasks.push({ task, resolve, reject }); + if (this.ongoing <= this.limit) { + this.process(); + } + }); + } + + async process() { + this.ongoing++; + const { task, resolve, reject } = this.tasks.shift(); + try { + const x = await task(); + resolve(x); + } catch (e) { + reject(e); + } + + this.ongoing--; + while (this.ongoing <= this.limit && this.tasks.length > 0) { + this.process(); + } + } +} + +module.exports = Queue;