Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Cancellation #21

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
932abc1
make npm scripts work under Windows
bergus Jun 20, 2016
3c3ead7
generalise actions
bergus Jun 20, 2016
508bbad
fixes and additions to test suite
bergus Jul 11, 2016
a419afa
enable ES6 syntax for profiling
bergus Jun 25, 2016
130aed7
better module structure
bergus Jul 1, 2016
f1993b8
first sketch of CancelToken
bergus Jun 21, 2016
8c93e14
add `token` parameters to all functions that need it
bergus Jun 22, 2016
a076df5
WIP: subscription on CancelTokens
bergus Jun 23, 2016
52b4d18
more CancelToken sketching
bergus Jun 23, 2016
36dc60d
cancel Indexed actions when their promise resolves prematurely
bergus Jun 23, 2016
6c47370
some optimisations for CancelToken implementation
bergus Jun 23, 2016
c7653bb
Add tests for CancelToken
bergus Jun 27, 2016
94a1f9d
simple token usage tests
bergus Jun 27, 2016
dcf9db0
Add tests for cancellation
bergus Jun 28, 2016
e61b095
cancellable coroutines
bergus Jun 29, 2016
d09a447
cancellation for assimilated thenables and promises
bergus Jul 3, 2016
aac0f8e
make .token public and immutable
bergus Jul 4, 2016
b70fe8f
sanity improvements for CancelTokenPool and CancelTokenReference
bergus Jul 5, 2016
c945325
sanity improvements for CancelToken::concat
bergus Jul 5, 2016
a8a4bf5
rewrite CancelToken::subscribe, add subscribeOrCall
bergus Jul 6, 2016
5500cf6
simplify unsubscribing Actions
bergus Jul 6, 2016
8663d25
add Promise::finally
bergus Jul 6, 2016
f34e858
add Promise::trifurcate
bergus Jul 7, 2016
eb0000b
more tests and problems
bergus Jul 7, 2016
47a022f
extra cancelled state as a special case of rejection
bergus Jul 8, 2016
b1ad384
Docs, docs, docs!
bergus Jul 12, 2016
65b8c69
update cancellation terminology
bergus Jul 22, 2016
911c1d0
Make token subscriptions asynchronous
bergus Jul 29, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ experiments/
node_modules/
build/
coverage/
perf/logs/
1 change: 1 addition & 0 deletions .jsinspectrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"identifiers": true,
"threshold": 35
}
195 changes: 150 additions & 45 deletions README.md

Large diffs are not rendered by default.

359 changes: 359 additions & 0 deletions cancellation.md

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"jsnext:main": "dist/creed.es.js",
"files": [
"dist/creed.js",
"dist/creed.es.js"
"dist/creed.es.js",
"dist/creed.node.js"
],
"repository": {
"type": "git",
Expand All @@ -28,13 +29,13 @@
"scripts": {
"compile": "npm run compile-src",
"compile-src": "mkdirp build/src && buble -m -i src -o build/src --no modules",
"build-dist": "npm run compile && mkdirp dist && rollup -c",
"build": "npm run build-dist && uglifyjs -c 'warnings=false' -m -o dist/creed.min.js -- dist/creed.js",
"build-dist": "npm run compile && mkdirp dist && rollup -c && rollup -f cjs -o dist/creed.node.js src/main.js",
"build": "npm run build-dist && uglifyjs -c \"warnings=false\" -m -o dist/creed.min.js -- dist/creed.js",
"preversion": "npm run build",
"check-coverage": "istanbul check-coverage --statements 100 --branches 100 --lines 100 --functions 100 coverage/coverage*.json",
"lint": "jsinspect src && eslint src",
"pretest": "npm run lint",
"test": "istanbul cover _mocha",
"test": "istanbul cover node_modules/mocha/bin/_mocha",
"posttest": "npm run test-aplus",
"test-aplus": "promises-aplus-tests test/aplus.js --reporter dot"
},
Expand Down
2 changes: 1 addition & 1 deletion perf/doxbee-sequential-errors/promises-creed-algebraic.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ global.useCreed = true;
global.useQ = false;
global.useBluebird = false;

var creed = require('../../dist/creed');
var creed = require('../..');

require('../lib/fakesP');

Expand Down
2 changes: 1 addition & 1 deletion perf/doxbee-sequential-errors/promises-creed-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ global.useCreed = true;
global.useQ = false;
global.useBluebird = false;

var creed = require('../../dist/creed');
var creed = require('../..');

require('../lib/fakesP');

Expand Down
2 changes: 1 addition & 1 deletion perf/doxbee-sequential-errors/promises-creed.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ global.useCreed = true;
global.useQ = false;
global.useBluebird = false;

var creed = require('../../dist/creed');
var creed = require('../..');

require('../lib/fakesP');

Expand Down
4 changes: 2 additions & 2 deletions perf/doxbee-sequential/promises-creed-algebraic.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ global.useCreed = true;
global.useQ = false;
global.useBluebird = false;

var creed = require('../../dist/creed');
var creed = require('../..');

require('../lib/fakesP');

Expand Down Expand Up @@ -52,7 +52,7 @@ module.exports = function upload(stream, idOrPath, tag, done) {
}).chain(function() {
return File.whereUpdate({id: fileId}, {version: version.id})
.execWithin(tx);
}).map(function() {
}).then(function() {
tx.commit();
return done();
}, function(err) {
Expand Down
2 changes: 1 addition & 1 deletion perf/doxbee-sequential/promises-creed-generator.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
global.useBluebird = false;
global.useQ = false;
global.useCreed = true;
var creed = require('../../dist/creed');
var creed = require('../..');
require('../lib/fakesP');

module.exports = creed.coroutine(function* upload(stream, idOrPath, tag, done) {
Expand Down
2 changes: 1 addition & 1 deletion perf/doxbee-sequential/promises-creed.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ global.useCreed = true;
global.useQ = false;
global.useBluebird = false;

var creed = require('../../dist/creed');
var creed = require('../..');

require('../lib/fakesP');

Expand Down
2 changes: 1 addition & 1 deletion perf/lib/fakesP.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ else if (global.useNative) {
};
}
else if (global.useCreed) {
var lifter = require('../../dist/creed').fromNode;
var lifter = require('../..').fromNode;
}
else {
var lifter = require('when/node').lift;
Expand Down
2 changes: 1 addition & 1 deletion perf/madeup-parallel/promises-creed-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ global.useQ = false;
global.useWhen = false;

global.useCreed = true;
var creed = require('../../dist/creed');
var creed = require('../..');
require('../lib/fakesP');

module.exports = creed.coroutine(function* upload(stream, idOrPath, tag, done) {
Expand Down
2 changes: 1 addition & 1 deletion perf/madeup-parallel/promises-creed.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ global.useWhen = false;

global.useCreed = true;

var creed = require('../../dist/creed');
var creed = require('../..');

require('../lib/fakesP');

Expand Down
3 changes: 1 addition & 2 deletions perf/performance.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

var args = require('optimist').argv;

var path = require('path');

global.LIKELIHOOD_OF_REJECTION = args.e || 0.1;
Expand Down Expand Up @@ -138,7 +137,7 @@ function measure(files, requests, time, parg, callback) {
async.mapSeries(files, function(f, done) {
console.log("benchmarking", f);
var logFile = path.basename(f) + ".log";
var profileFlags = ["--prof", "--logfile=C:/etc/v8/" + logFile];
var profileFlags = ["--prof", "--logfile=logs/" + logFile];

var argsFork = [__filename,
'--n', requests,
Expand Down
106 changes: 106 additions & 0 deletions src/Action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { noop } from './util'
import { reject } from './Promise'

export default class Action {
constructor (promise) {
// the Future which this Action tries to resolve
// when null, the action is cancelled and won't be executed
this.promise = promise
}

destroy () {
this.promise = null
}

cancel (p) {
/* istanbul ignore else */
if (this.promise._isResolved()) { // promise checks for cancellation itself
this.destroy()
}
}

// default onFulfilled action
fulfilled (p) {
this.put(p)
}

// default onRejected action
rejected (p) {
this.put(p)
return false
}

// default onCancelled action
cancelled (p) {
reject(p.near().value)._runAction(this)
}

// when this.promise is to be settled (possible having awaited the result)
put (p) {
// assert: isSettled(p) || p.token === this.promise.token
// asssert: this.promise != null
this.end().__become(p)
}

end () {
const promise = this.promise
const token = promise.token
this.promise = null
if (token != null) token._unsubscribe(this)
return promise
}
}

const sentinel = noop // Symbol('currently executing')

export class CancellableAction extends Action {
constructor (f, promise) {
super(promise)
// the function that produces the resolution result for the promise
// when null, the function has been executed but the promise might still get cancelled
this.f = f
}

destroy () {
this.promise = null
this.f = null
}

cancel (results) {
if (this.promise._isResolved()) { // promise checks for cancellation itself
if (this.f !== sentinel) { // not currently running
this.destroy()
}
// otherwise keep the cancelled .promise so that it stays usable in handle()
// and ignores whatever is done with the f() result
return true
}
return false
}

fulfilled (p) {
if (this.f) {
this.tryCall(this.f, p.value)
} else {
this.put(p)
}
}

tryCall (f, x) {
this.f = sentinel
let result
try {
result = f(x)
} catch (e) {
this.f = null
this.end()._reject(e)
return
}
this.f = null
return this.handle(result)
}

handle (p) {
this.promise._resolve(p, this)
}
}
5 changes: 4 additions & 1 deletion src/Any.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { silenceError } from './inspect.js'
import { silenceError } from './Promise' // deferred
import CancelReason from './CancelReason'

export default class Any {
constructor () {
Expand All @@ -10,7 +11,9 @@ export default class Any {
}

fulfillAt (p, i, promise) {
const token = promise.token
promise._become(p)
token._cancel(new CancelReason('result is already fulfilled'))
}

rejectAt (p, i, promise) {
Expand Down
2 changes: 2 additions & 0 deletions src/CancelReason.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export default class CancelReason extends Error {
}
Loading