[Pkg-javascript-commits] [pdf.js] 80/174: Adds thread abort capabilities.
David Prévot
taffit at moszumanska.debian.org
Thu Nov 19 18:45:17 UTC 2015
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to branch master
in repository pdf.js.
commit 58c3ea08202becf007c304512c44726719acb508
Author: Yury Delendik <ydelendik at mozilla.com>
Date: Tue Oct 20 20:50:32 2015 -0500
Adds thread abort capabilities.
---
src/core/annotation.js | 17 ++++++----
src/core/core.js | 11 +++---
src/core/evaluator.js | 63 +++++++++++++++++++----------------
src/core/worker.js | 81 +++++++++++++++++++++++++++++++++++++++++----
src/display/api.js | 15 +++++++--
test/unit/evaluator_spec.js | 52 +++++++++++++++++++++++++++--
6 files changed, 186 insertions(+), 53 deletions(-)
diff --git a/src/core/annotation.js b/src/core/annotation.js
index 5844f2e..f08d22d 100644
--- a/src/core/annotation.js
+++ b/src/core/annotation.js
@@ -281,7 +281,7 @@ var Annotation = (function AnnotationClosure() {
}.bind(this));
},
- getOperatorList: function Annotation_getOperatorList(evaluator) {
+ getOperatorList: function Annotation_getOperatorList(evaluator, task) {
if (!this.appearance) {
return Promise.resolve(new OperatorList());
@@ -308,7 +308,8 @@ var Annotation = (function AnnotationClosure() {
return resourcesPromise.then(function(resources) {
var opList = new OperatorList();
opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
- return evaluator.getOperatorList(self.appearance, resources, opList).
+ return evaluator.getOperatorList(self.appearance, task,
+ resources, opList).
then(function () {
opList.addOp(OPS.endAnnotation, []);
self.appearance.reset();
@@ -319,7 +320,7 @@ var Annotation = (function AnnotationClosure() {
};
Annotation.appendToOperatorList = function Annotation_appendToOperatorList(
- annotations, opList, pdfManager, partialEvaluator, intent) {
+ annotations, opList, pdfManager, partialEvaluator, task, intent) {
function reject(e) {
annotationsReadyCapability.reject(e);
@@ -332,7 +333,7 @@ var Annotation = (function AnnotationClosure() {
if (intent === 'display' && annotations[i].isViewable() ||
intent === 'print' && annotations[i].isPrintable()) {
annotationPromises.push(
- annotations[i].getOperatorList(partialEvaluator));
+ annotations[i].getOperatorList(partialEvaluator, task));
}
}
Promise.all(annotationPromises).then(function(datas) {
@@ -564,9 +565,10 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
}
Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {
- getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) {
+ getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator,
+ task) {
if (this.appearance) {
- return Annotation.prototype.getOperatorList.call(this, evaluator);
+ return Annotation.prototype.getOperatorList.call(this, evaluator, task);
}
var opList = new OperatorList();
@@ -579,7 +581,8 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
}
var stream = new Stream(stringToBytes(data.defaultAppearance));
- return evaluator.getOperatorList(stream, this.fieldResources, opList).
+ return evaluator.getOperatorList(stream, task,
+ this.fieldResources, opList).
then(function () {
return opList;
});
diff --git a/src/core/core.js b/src/core/core.js
index e188248..2342e31 100644
--- a/src/core/core.js
+++ b/src/core/core.js
@@ -161,7 +161,7 @@ var Page = (function PageClosure() {
}.bind(this));
},
- getOperatorList: function Page_getOperatorList(handler, intent) {
+ getOperatorList: function Page_getOperatorList(handler, task, intent) {
var self = this;
var pdfManager = this.pdfManager;
@@ -194,8 +194,8 @@ var Page = (function PageClosure() {
pageIndex: self.pageIndex,
intent: intent
});
- return partialEvaluator.getOperatorList(contentStream, self.resources,
- opList).then(function () {
+ return partialEvaluator.getOperatorList(contentStream, task,
+ self.resources, opList).then(function () {
return opList;
});
});
@@ -212,7 +212,7 @@ var Page = (function PageClosure() {
}
var annotationsReadyPromise = Annotation.appendToOperatorList(
- annotations, pageOpList, pdfManager, partialEvaluator, intent);
+ annotations, pageOpList, pdfManager, partialEvaluator, task, intent);
return annotationsReadyPromise.then(function () {
pageOpList.flush(true);
return pageOpList;
@@ -220,7 +220,7 @@ var Page = (function PageClosure() {
});
},
- extractTextContent: function Page_extractTextContent() {
+ extractTextContent: function Page_extractTextContent(task) {
var handler = {
on: function nullHandlerOn() {},
send: function nullHandlerSend() {}
@@ -249,6 +249,7 @@ var Page = (function PageClosure() {
self.fontCache);
return partialEvaluator.getTextContent(contentStream,
+ task,
self.resources);
});
},
diff --git a/src/core/evaluator.js b/src/core/evaluator.js
index 973f93e..41f6571 100644
--- a/src/core/evaluator.js
+++ b/src/core/evaluator.js
@@ -125,6 +125,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
buildFormXObject: function PartialEvaluator_buildFormXObject(resources,
xobj, smask,
operatorList,
+ task,
initialState) {
var matrix = xobj.dict.getArray('Matrix');
var bbox = xobj.dict.getArray('BBox');
@@ -157,7 +158,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]);
- return this.getOperatorList(xobj,
+ return this.getOperatorList(xobj, task,
(xobj.dict.get('Resources') || resources), operatorList, initialState).
then(function () {
operatorList.addOp(OPS.paintFormXObjectEnd, []);
@@ -269,7 +270,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
},
handleSMask: function PartialEvaluator_handleSmask(smask, resources,
- operatorList,
+ operatorList, task,
stateManager) {
var smaskContent = smask.get('G');
var smaskOptions = {
@@ -277,13 +278,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
backdrop: smask.get('BC')
};
return this.buildFormXObject(resources, smaskContent, smaskOptions,
- operatorList, stateManager.state.clone());
+ operatorList, task, stateManager.state.clone());
},
handleTilingType:
function PartialEvaluator_handleTilingType(fn, args, resources,
pattern, patternDict,
- operatorList) {
+ operatorList, task) {
// Create an IR of the pattern code.
var tilingOpList = new OperatorList();
// Merge the available resources, to prevent issues when the patternDict
@@ -291,8 +292,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var resourcesArray = [patternDict.get('Resources'), resources];
var patternResources = Dict.merge(this.xref, resourcesArray);
- return this.getOperatorList(pattern, patternResources, tilingOpList).then(
- function () {
+ return this.getOperatorList(pattern, task, patternResources,
+ tilingOpList).then(function () {
// Add the dependencies to the parent operator list so they are
// resolved before sub operator list is executed synchronously.
operatorList.addDependencies(tilingOpList.dependencies);
@@ -305,7 +306,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
handleSetFont:
function PartialEvaluator_handleSetFont(resources, fontArgs, fontRef,
- operatorList, state) {
+ operatorList, task, state) {
// TODO(mack): Not needed?
var fontName;
if (fontArgs) {
@@ -319,8 +320,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (!translated.font.isType3Font) {
return translated;
}
- return translated.loadType3Data(self, resources, operatorList).then(
- function () {
+ return translated.loadType3Data(self, resources, operatorList, task).
+ then(function () {
return translated;
});
}).then(function (translated) {
@@ -367,8 +368,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
},
setGState: function PartialEvaluator_setGState(resources, gState,
- operatorList, xref,
- stateManager) {
+ operatorList, task,
+ xref, stateManager) {
// This array holds the converted/processed state data.
var gStateObj = [];
var gStateMap = gState.map;
@@ -392,8 +393,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
break;
case 'Font':
promise = promise.then(function () {
- return self.handleSetFont(resources, null, value[0],
- operatorList, stateManager.state).
+ return self.handleSetFont(resources, null, value[0], operatorList,
+ task, stateManager.state).
then(function (loadedName) {
operatorList.addDependency(loadedName);
gStateObj.push([key, [loadedName, value[1]]]);
@@ -412,7 +413,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (isDict(dict)) {
promise = promise.then(function () {
return self.handleSMask(dict, resources, operatorList,
- stateManager);
+ task, stateManager);
});
gStateObj.push([key, true]);
} else {
@@ -593,7 +594,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
},
handleColorN: function PartialEvaluator_handleColorN(operatorList, fn, args,
- cs, patterns, resources, xref) {
+ cs, patterns, resources, task, xref) {
// compile tiling patterns
var patternName = args[args.length - 1];
// SCN/scn applies patterns along with normal colors
@@ -606,7 +607,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (typeNum === TILING_PATTERN) {
var color = cs.base ? cs.base.getRgb(args, 0) : null;
return this.handleTilingType(fn, color, resources, pattern,
- dict, operatorList);
+ dict, operatorList, task);
} else if (typeNum === SHADING_PATTERN) {
var shading = dict.get('Shading');
var matrix = dict.get('Matrix');
@@ -623,6 +624,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
},
getOperatorList: function PartialEvaluator_getOperatorList(stream,
+ task,
resources,
operatorList,
initialState) {
@@ -641,6 +643,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var timeSlotManager = new TimeSlotManager();
return new Promise(function next(resolve, reject) {
+ task.ensureNotTerminated();
timeSlotManager.reset();
var stop, operation = {}, i, ii, cs;
while (!(stop = timeSlotManager.check())) {
@@ -683,7 +686,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (type.name === 'Form') {
stateManager.save();
return self.buildFormXObject(resources, xobj, null,
- operatorList,
+ operatorList, task,
stateManager.state.clone()).
then(function () {
stateManager.restore();
@@ -707,8 +710,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
case OPS.setFont:
var fontSize = args[1];
// eagerly collect all fonts
- return self.handleSetFont(resources, args, null,
- operatorList, stateManager.state).
+ return self.handleSetFont(resources, args, null, operatorList,
+ task, stateManager.state).
then(function (loadedName) {
operatorList.addDependency(loadedName);
operatorList.addOp(OPS.setFont, [loadedName, fontSize]);
@@ -814,7 +817,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
cs = stateManager.state.fillColorSpace;
if (cs.name === 'Pattern') {
return self.handleColorN(operatorList, OPS.setFillColorN,
- args, cs, patterns, resources, xref).then(function() {
+ args, cs, patterns, resources, task, xref).then(function() {
next(resolve, reject);
}, reject);
}
@@ -825,7 +828,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
cs = stateManager.state.strokeColorSpace;
if (cs.name === 'Pattern') {
return self.handleColorN(operatorList, OPS.setStrokeColorN,
- args, cs, patterns, resources, xref).then(function() {
+ args, cs, patterns, resources, task, xref).then(function() {
next(resolve, reject);
}, reject);
}
@@ -859,8 +862,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
}
var gState = extGState.get(dictName.name);
- return self.setGState(resources, gState, operatorList, xref,
- stateManager).then(function() {
+ return self.setGState(resources, gState, operatorList, task,
+ xref, stateManager).then(function() {
next(resolve, reject);
}, reject);
case OPS.moveTo:
@@ -898,7 +901,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (stop) {
deferred.then(function () {
next(resolve, reject);
- });
+ }, reject);
return;
}
// Some PDFs don't close all restores inside object/form.
@@ -910,7 +913,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
});
},
- getTextContent: function PartialEvaluator_getTextContent(stream, resources,
+ getTextContent: function PartialEvaluator_getTextContent(stream, task,
+ resources,
stateManager) {
stateManager = (stateManager || new StateManager(new TextState()));
@@ -1088,6 +1092,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var timeSlotManager = new TimeSlotManager();
return new Promise(function next(resolve, reject) {
+ task.ensureNotTerminated();
timeSlotManager.reset();
var stop, operation = {}, args = [];
while (!(stop = timeSlotManager.check())) {
@@ -1243,7 +1248,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
stateManager.transform(matrix);
}
- return self.getTextContent(xobj,
+ return self.getTextContent(xobj, task,
xobj.dict.get('Resources') || resources, stateManager).
then(function (formTextContent) {
Util.appendToArray(bidiTexts, formTextContent.items);
@@ -1283,7 +1288,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (stop) {
deferred.then(function () {
next(resolve, reject);
- });
+ }, reject);
return;
}
resolve(textContent);
@@ -1848,7 +1853,7 @@ var TranslatedFont = (function TranslatedFontClosure() {
]);
this.sent = true;
},
- loadType3Data: function (evaluator, resources, parentOperatorList) {
+ loadType3Data: function (evaluator, resources, parentOperatorList, task) {
assert(this.font.isType3Font);
if (this.type3Loaded) {
@@ -1865,7 +1870,7 @@ var TranslatedFont = (function TranslatedFontClosure() {
loadCharProcsPromise = loadCharProcsPromise.then(function (key) {
var glyphStream = charProcs[key];
var operatorList = new OperatorList();
- return evaluator.getOperatorList(glyphStream, fontResources,
+ return evaluator.getOperatorList(glyphStream, task, fontResources,
operatorList).then(function () {
charProcOperatorList[key] = operatorList.getIR();
diff --git a/src/core/worker.js b/src/core/worker.js
index 9fc3ea4..7440534 100644
--- a/src/core/worker.js
+++ b/src/core/worker.js
@@ -22,11 +22,42 @@
'use strict';
+var WorkerTask = (function WorkerTaskClosure() {
+ function WorkerTask(name) {
+ this.name = name;
+ this.terminated = false;
+ this._capability = createPromiseCapability();
+ }
+
+ WorkerTask.prototype = {
+ get finished() {
+ return this._capability.promise;
+ },
+
+ finish: function () {
+ this._capability.resolve();
+ },
+
+ terminate: function () {
+ this.terminated = true;
+ },
+
+ ensureNotTerminated: function () {
+ if (this.terminated) {
+ throw new Error('Worker task was terminated');
+ }
+ }
+ };
+
+ return WorkerTask;
+})();
+
var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
setup: function wphSetup(handler) {
var pdfManager;
var terminated = false;
var cancelXHRs = null;
+ var WorkerTasks = [];
function ensureNotTerminated() {
if (terminated) {
@@ -34,6 +65,16 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
}
}
+ function startWorkerTask(task) {
+ WorkerTasks.push(task);
+ }
+
+ function finishWorkerTask(task) {
+ task.finish();
+ var i = WorkerTasks.indexOf(task);
+ WorkerTasks.splice(i, 1);
+ }
+
function loadDocument(recoveryMode) {
var loadDocumentCapability = createPromiseCapability();
@@ -413,17 +454,25 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
});
handler.on('RenderPageRequest', function wphSetupRenderPage(data) {
- pdfManager.getPage(data.pageIndex).then(function(page) {
+ var pageIndex = data.pageIndex;
+ pdfManager.getPage(pageIndex).then(function(page) {
+ var task = new WorkerTask('RenderPageRequest: page ' + pageIndex);
+ startWorkerTask(task);
- var pageNum = data.pageIndex + 1;
+ var pageNum = pageIndex + 1;
var start = Date.now();
// Pre compile the pdf page and fetch the fonts/images.
- page.getOperatorList(handler, data.intent).then(function(operatorList) {
+ page.getOperatorList(handler, task, data.intent).then(
+ function(operatorList) {
+ finishWorkerTask(task);
info('page=' + pageNum + ' - getOperatorList: time=' +
(Date.now() - start) + 'ms, len=' + operatorList.fnArray.length);
-
}, function(e) {
+ finishWorkerTask(task);
+ if (task.terminated) {
+ return; // ignoring errors from the terminated thread
+ }
var minimumStackMessage =
'worker.js: while trying to getPage() and getOperatorList()';
@@ -458,13 +507,23 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
}, this);
handler.on('GetTextContent', function wphExtractText(data) {
- return pdfManager.getPage(data.pageIndex).then(function(page) {
- var pageNum = data.pageIndex + 1;
+ var pageIndex = data.pageIndex;
+ return pdfManager.getPage(pageIndex).then(function(page) {
+ var task = new WorkerTask('GetTextContent: page ' + pageIndex);
+ startWorkerTask(task);
+ var pageNum = pageIndex + 1;
var start = Date.now();
- return page.extractTextContent().then(function(textContent) {
+ return page.extractTextContent(task).then(function(textContent) {
+ finishWorkerTask(task);
info('text indexing: page=' + pageNum + ' - time=' +
(Date.now() - start) + 'ms');
return textContent;
+ }, function (reason) {
+ finishWorkerTask(task);
+ if (task.terminated) {
+ return; // ignoring errors from the terminated thread
+ }
+ throw reason;
});
});
});
@@ -482,6 +541,14 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
if (cancelXHRs) {
cancelXHRs();
}
+
+ var waitOn = [];
+ WorkerTasks.forEach(function (task) {
+ waitOn.push(task.finished);
+ task.terminate();
+ });
+
+ return Promise.all(waitOn).then(function () {});
});
}
};
diff --git a/src/display/api.js b/src/display/api.js
index 08b02ca..59efee3 100644
--- a/src/display/api.js
+++ b/src/display/api.js
@@ -883,15 +883,20 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
this.destroyed = true;
this.transport.pageCache[this.pageIndex] = null;
+ var waitOn = [];
Object.keys(this.intentStates).forEach(function(intent) {
var intentState = this.intentStates[intent];
intentState.renderTasks.forEach(function(renderTask) {
+ var renderCompleted = renderTask.capability.promise.
+ catch(function () {}); // ignoring failures
+ waitOn.push(renderCompleted);
renderTask.cancel();
});
}, this);
this.objs.clear();
this.annotationsPromise = null;
this.pendingCleanup = false;
+ return Promise.all(waitOn);
},
/**
@@ -1054,15 +1059,21 @@ var WorkerTransport = (function WorkerTransportClosure() {
this.destroyed = true;
this.destroyCapability = createPromiseCapability();
+ var waitOn = [];
+ // We need to wait for all renderings to be completed, e.g.
+ // timeout/rAF can take a long time.
this.pageCache.forEach(function (page) {
if (page) {
- page._destroy();
+ waitOn.push(page._destroy());
}
});
this.pageCache = [];
this.pagePromises = [];
var self = this;
- this.messageHandler.sendWithPromise('Terminate', null).then(function () {
+ // We also need to wait for the worker to finish its long running tasks.
+ var terminated = this.messageHandler.sendWithPromise('Terminate', null);
+ waitOn.push(terminated);
+ Promise.all(waitOn).then(function () {
FontLoader.clear();
if (self.worker) {
self.worker.terminate();
diff --git a/test/unit/evaluator_spec.js b/test/unit/evaluator_spec.js
index 5bf007d..5e21122 100644
--- a/test/unit/evaluator_spec.js
+++ b/test/unit/evaluator_spec.js
@@ -1,13 +1,13 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* globals expect, it, describe, PartialEvaluator, StringStream, OPS,
- OperatorList, waitsFor, runs, Dict, Name, Stream */
+ OperatorList, waitsFor, runs, Dict, Name, Stream, WorkerTask */
'use strict';
describe('evaluator', function() {
function XrefMock(queue) {
- this.queue = queue;
+ this.queue = queue || [];
}
XrefMock.prototype = {
fetchIfRef: function() {
@@ -35,7 +35,9 @@ describe('evaluator', function() {
var done = false;
runs(function () {
var result = new OperatorList();
- evaluator.getOperatorList(stream, resources, result).then(function () {
+ var task = new WorkerTask('OperatorListCheck');
+ evaluator.getOperatorList(stream, task, resources, result).then(
+ function () {
check(result);
done = true;
});
@@ -259,4 +261,48 @@ describe('evaluator', function() {
});
});
});
+
+ describe('thread control', function() {
+ it('should abort operator list parsing', function () {
+ var evaluator = new PartialEvaluator(new PdfManagerMock(),
+ new XrefMock(), new HandlerMock(),
+ 'prefix');
+ var stream = new StringStream('qqQQ');
+ var resources = new ResourcesMock();
+ var done = false;
+ runs(function () {
+ var result = new OperatorList();
+ var task = new WorkerTask('OperatorListAbort');
+ task.terminate();
+ evaluator.getOperatorList(stream, task, resources, result).catch(
+ function () {
+ done = true;
+ expect(!!result.fnArray && !!result.argsArray).toEqual(true);
+ expect(result.fnArray.length).toEqual(0);
+ });
+ });
+ waitsFor(function () {
+ return done;
+ });
+ });
+ it('should abort text parsing parsing', function () {
+ var resources = new ResourcesMock();
+ var evaluator = new PartialEvaluator(new PdfManagerMock(),
+ new XrefMock(), new HandlerMock(),
+ 'prefix');
+ var stream = new StringStream('qqQQ');
+ var done = false;
+ runs(function () {
+ var task = new WorkerTask('TextContentAbort');
+ task.terminate();
+ evaluator.getTextContent(stream, task, resources).catch(
+ function () {
+ done = true;
+ });
+ });
+ waitsFor(function () {
+ return done;
+ });
+ });
+ });
});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/pdf.js.git
More information about the Pkg-javascript-commits
mailing list