diff --git a/src/common/compute/backends/local/Client.js b/src/common/compute/backends/local/Client.js index b6f05e527..58bbc0b69 100644 --- a/src/common/compute/backends/local/Client.js +++ b/src/common/compute/backends/local/Client.js @@ -1,6 +1,7 @@ /*globals define*/ // TODO: Show an error if not running on the server... define([ + '../../../utils', 'common/util/assert', '../ComputeClient', '../JobResults', @@ -12,6 +13,7 @@ define([ 'os', 'path', ], function( + utils, assert, ComputeClient, JobResults, @@ -56,12 +58,15 @@ define([ this.canceled = false; } - cancelJob (jobInfo) { + async cancelJob (jobInfo) { const {hash} = jobInfo; if (this.currentJob === hash) { this.canceled = true; this.subprocess.kill(); + await utils.waitUntil( + async () => this.CANCELED === await this.getStatus(jobInfo) + ); } else if (this.jobQueue.includes(hash)) { const i = this.jobQueue.indexOf(hash); this.jobQueue.splice(i, 1); diff --git a/src/common/utils.js b/src/common/utils.js index 0c3ad64ba..00d4d6c32 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -135,6 +135,18 @@ return obj1; }; + async function sleep(duration) { + const deferred = defer(); + setTimeout(deferred.resolve, duration); + return deferred.promise; + } + + async function waitUntil(fn, interval=50) { + while (!await fn()) { + await sleep(interval); + } + } + return { deepExtend, splitObj, @@ -142,5 +154,7 @@ abbr, withTimeout, defer, + sleep, + waitUntil, }; })); diff --git a/test/unit/common/compute/backends/local/Client.spec.js b/test/unit/common/compute/backends/local/Client.spec.js new file mode 100644 index 000000000..380a3c740 --- /dev/null +++ b/test/unit/common/compute/backends/local/Client.spec.js @@ -0,0 +1,45 @@ +describe('local compute', function() { + const assert = require('assert'); + const testFixture = require('../../../../../globals'); + const GeneratedFiles = testFixture.requirejs('deepforge/plugin/GeneratedFiles'); + const Compute = testFixture.requirejs('deepforge/compute/index'); + const BlobClient = require('webgme-engine/src/server/middleware/blob/BlobClientWithFSBackend'); + const gmeConfig = testFixture.getGmeConfig(); + const blobClient = new BlobClient(gmeConfig, testFixture.logger); + const backend = Compute.getBackend('local'); + const client = backend.getClient(testFixture.logger, blobClient); + const utils = testFixture.requirejs('deepforge/utils'); + + describe('cancelJob', function() { + let jobInfo; + + beforeEach(async () => { + const jobHash = await getJobHash('sleep', '10'); + const deferred = utils.defer(); + client.on('update', (hash, status) => { + if (hash === jobHash && status === client.RUNNING) { + deferred.resolve(); + } + }); + + jobInfo = await client.createJob(jobHash); + await deferred.promise; + }); + + it('should block until canceled', async function() { + await client.cancelJob(jobInfo); + const status = await client.getStatus(jobInfo); + assert.equal(status, client.CANCELED); + }); + }); + + async function getJobHash(cmd) { + const config = { + cmd, args: Array.prototype.slice.call(arguments, 1), + }; + const files = new GeneratedFiles(blobClient); + files.addFile('executor_config.json', JSON.stringify(config)); + return await files.save(); + } +}); +