Compare commits

...

3 Commits

Author SHA1 Message Date
Victor Woeltjen
369b991991 [Search] Don't re-index from second window
...if another window has already completed indexing
of domain objects for in-memory search.
2015-11-16 15:48:45 -08:00
Victor Woeltjen
63430b7f51 [Search] Used shared worker
nasa/openmctweb#308
2015-11-16 15:30:11 -08:00
Victor Woeltjen
8363302caf [Workers] Allow web workers to be shared
Support an additional flag in the  extension category
such that SharedWorkers may be used. nasa/openmctweb#308.
2015-11-16 15:16:44 -08:00
6 changed files with 94 additions and 25 deletions

View File

@@ -1254,6 +1254,21 @@ object, or the current view proxy.
* `all()`: Get an array of all objects in the selection state. Will include
either or both of the view proxy and selected object.
## Workers Category
The `workers` extension category allows scripts to be run as web workers
using the `workerService`.
An extension of this category has no implementation. The following properties
are supported:
* `key`: A symbolic string used to identify this worker.
* `workerUrl`: The path, relative to this bundle's `src` folder, where
this worker's source code resides.
* `shared`: Optional; a boolean flag which, if true, indicates that this
worker should be instantiated as a
[`SharedWorker`](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker/SharedWorker).
# Directives
Open MCT Web defines several Angular directives that are intended for use both
@@ -1849,6 +1864,14 @@ the TelemetrySeries itself, in that order.
* `getSeries(domainObject)`: Get the latest `TelemetrySeries` (as resulted from
a previous `request(...)` call) available for this domain object.
### Worker Service
The `workerService` may be used to run web workers defined via the
`workers` extension category. It has the following method:
* `run(key)`: Run the worker identified by the provided `key`. Returns
a `Worker` (or `SharedWorker`, if the specified worker is defined
as a shared worker); if the `key` is unknown, returns `undefined`.
# Models
Domain object models in Open MCT Web are JavaScript objects describing the

View File

@@ -38,7 +38,8 @@ define(
* @constructor
*/
function WorkerService($window, workers) {
var workerUrls = {};
var workerUrls = {},
sharedWorkers = {};
function addWorker(worker) {
var key = worker.key;
@@ -48,12 +49,15 @@ define(
worker.bundle.sources,
worker.scriptUrl
].join("/");
sharedWorkers[key] = worker.shared;
}
}
(workers || []).forEach(addWorker);
this.workerUrls = workerUrls;
this.sharedWorkers = sharedWorkers;
this.Worker = $window.Worker;
this.SharedWorker = $window.SharedWorker;
}
/**
@@ -66,7 +70,8 @@ define(
*/
WorkerService.prototype.run = function (key) {
var scriptUrl = this.workerUrls[key],
Worker = this.Worker;
Worker = this.sharedWorkers[key] ?
this.SharedWorker : this.Worker;
return scriptUrl && Worker && new Worker(scriptUrl);
};

View File

@@ -30,10 +30,14 @@ define(
var mockWindow,
testWorkers,
mockWorker,
mockSharedWorker,
service;
beforeEach(function () {
mockWindow = jasmine.createSpyObj('$window', ['Worker']);
mockWindow = jasmine.createSpyObj(
'$window',
['Worker', 'SharedWorker']
);
testWorkers = [
{
key: 'abc',
@@ -49,11 +53,19 @@ define(
key: 'xyz',
scriptUrl: 'bad.js',
bundle: { path: 'bad', sources: 'bad' }
},
{
key: 'a-shared-worker',
shared: true,
scriptUrl: 'c.js',
bundle: { path: 'a', sources: 'b' }
}
];
mockWorker = {};
mockSharedWorker = {};
mockWindow.Worker.andReturn(mockWorker);
mockWindow.SharedWorker.andReturn(mockSharedWorker);
service = new WorkerService(mockWindow, testWorkers);
});
@@ -68,6 +80,12 @@ define(
expect(mockWindow.Worker).toHaveBeenCalledWith('x/y/z.js');
});
it("allows workers to be shared", function () {
expect(service.run('a-shared-worker')).toBe(mockSharedWorker);
expect(mockWindow.SharedWorker)
.toHaveBeenCalledWith('a/b/c.js');
});
it("returns undefined for unknown workers", function () {
expect(service.run('def')).toBeUndefined();
});

View File

@@ -66,7 +66,8 @@
"workers": [
{
"key": "genericSearchWorker",
"scriptUrl": "services/GenericSearchWorker.js"
"scriptUrl": "services/GenericSearchWorker.js",
"shared": true
}
]
}

View File

@@ -59,11 +59,9 @@ define([
this.worker = this.startWorker(workerService);
this.indexOnMutation(topic);
ROOTS.forEach(function indexRoot(rootId) {
provider.scheduleForIndexing(rootId);
});
this.rootsToIndex = ROOTS;
this.worker.port.postMessage({ request: 'status' });
}
/**
@@ -102,7 +100,8 @@ define([
var worker = workerService.run('genericSearchWorker'),
provider = this;
worker.addEventListener('message', function (messageEvent) {
worker.port.start();
worker.port.addEventListener('message', function (messageEvent) {
provider.onWorkerMessage(messageEvent);
});
@@ -150,9 +149,12 @@ define([
* @private
*/
GenericSearchProvider.prototype.keepIndexing = function () {
if (this.pendingRequests === 0 && this.idsToIndex.length === 0) {
this.worker.port.postMessage({ request: 'finish' });
}
while (this.pendingRequests < this.MAX_CONCURRENT_REQUESTS &&
this.idsToIndex.length
) {
this.idsToIndex.length) {
this.beginIndexRequest();
}
};
@@ -168,7 +170,7 @@ define([
GenericSearchProvider.prototype.index = function (id, model) {
var provider = this;
this.worker.postMessage({
this.worker.port.postMessage({
request: 'index',
model: model,
id: id
@@ -220,14 +222,25 @@ define([
* @private
*/
GenericSearchProvider.prototype.onWorkerMessage = function (event) {
var provider = this,
pendingQuery,
modelResults;
if (event.data.request === 'status' && !event.data.finished) {
this.rootsToIndex.forEach(function indexRoot(rootId) {
provider.scheduleForIndexing(rootId);
});
return;
}
if (event.data.request !== 'search') {
return;
}
var pendingQuery = this.pendingQueries[event.data.queryId],
modelResults = {
total: event.data.total
};
pendingQuery = this.pendingQueries[event.data.queryId];
modelResults = {
total: event.data.total
};
modelResults.hits = event.data.results.map(function (hit) {
return {
@@ -265,7 +278,7 @@ define([
) {
var queryId = this.makeQueryId();
this.worker.postMessage({
this.worker.port.postMessage({
request: 'search',
input: searchInput,
maxResults: maxResults,

View File

@@ -30,7 +30,8 @@
// An array of objects composed of domain object IDs and models
// {id: domainObject's ID, model: domainObject's model}
var indexedItems = [],
TERM_SPLITTER = /[ _\*]/;
TERM_SPLITTER = /[ _\*]/,
status = { request: 'status', finished: false };
function indexItem(id, model) {
var vector = {
@@ -118,7 +119,7 @@
match.matchCount += 100;
} else if (match.item.vector.lowerCaseName ===
query.inputLowerCase) {
match.matchCount += 50;
match.matchCount += 50;
}
return match;
})
@@ -147,11 +148,19 @@
return message;
}
self.onmessage = function (event) {
if (event.data.request === 'index') {
indexItem(event.data.id, event.data.model);
} else if (event.data.request === 'search') {
self.postMessage(search(event.data));
}
self.onconnect = function (connectEvent) {
var port = connectEvent.ports[0];
port.onmessage = function (event) {
if (event.data.request === 'index') {
indexItem(event.data.id, event.data.model);
} else if (event.data.request === 'search') {
port.postMessage(search(event.data));
} else if (event.data.request === 'status') {
port.postMessage(status);
} else if (event.data.request === 'finish') {
status.finished = true;
}
};
};
}());