parallel.es
Version:
Simple parallelization for EcmaScript
120 lines (102 loc) • 4.26 kB
text/typescript
import {FunctionCallDeserializer} from "../../common/function/function-call-deserializer";
import {ITaskDefinition} from "../../common/task/task-definition";
import {IFunctionDefinition} from "../../common/function/function-defintion";
import {
functionExecutionError, isFunctionResponse, isInitializeMessage, isScheduleTask, requestFunctionMessage,
workerResultMessage } from "../../common/worker/worker-messages";
import {BrowserWorkerSlave} from "./browser-worker-slave";
/**
* State of the browser worker slave.
*/
export abstract class BrowserWorkerSlaveState {
constructor(public name: string, protected slave: BrowserWorkerSlave) {}
/**
* Executed when the slave changes its state to this state.
*/
public enter(): void {
// intentionally empty
}
/**
* Executed whenever the slave receives a message from the ui-thread while being in this state
* @param event the received message
* @returns {boolean} true if the state has handled the message, false otherwise
*/
public onMessage(event: MessageEvent): boolean { return false; }
}
/**
* Initial state of a slave. The slave is waiting for the initialization message.
*/
export class DefaultBrowserWorkerSlaveState extends BrowserWorkerSlaveState {
constructor(slave: BrowserWorkerSlave) {
super("Default", slave);
}
public onMessage(event: MessageEvent): boolean {
if (isInitializeMessage(event.data)) {
this.slave.id = event.data.workerId;
this.slave.changeState(new IdleBrowserWorkerSlaveState(this.slave));
return true;
}
return false;
}
}
/**
* The slave is waiting for work from the ui-thread.
*/
export class IdleBrowserWorkerSlaveState extends BrowserWorkerSlaveState {
constructor(slave: BrowserWorkerSlave) {
super("Idle", slave);
}
public onMessage(event: MessageEvent): boolean {
if (!isScheduleTask(event.data)) {
return false;
}
const task: ITaskDefinition = event.data.task;
const missingFunctions = task.usedFunctionIds.filter(id => !this.slave.functionCache.has(id));
if (missingFunctions.length === 0) {
this.slave.changeState(new ExecuteFunctionBrowserWorkerSlaveState(this.slave, task));
} else {
const [ head, ...tail ] = missingFunctions;
this.slave.postMessage(requestFunctionMessage(head, ...tail));
this.slave.changeState(new WaitingForFunctionDefinitionBrowserWorkerSlaveState(this.slave, task));
}
return true;
}
}
/**
* The slave is waiting for the definition of the requested function that is needed to execute the assigned task.
*/
export class WaitingForFunctionDefinitionBrowserWorkerSlaveState extends BrowserWorkerSlaveState {
constructor(slave: BrowserWorkerSlave, private task: ITaskDefinition) {
super("WaitingForFunctionDefinition", slave);
}
public onMessage(event: MessageEvent): boolean {
const message = event.data;
if (isFunctionResponse(message)) {
for (const definition of message.functions as IFunctionDefinition[]) {
this.slave.functionCache.registerFunction(definition);
}
this.slave.changeState(new ExecuteFunctionBrowserWorkerSlaveState(this.slave, this.task));
return true;
}
return false;
}
}
/**
* The slave is executing the function
*/
export class ExecuteFunctionBrowserWorkerSlaveState extends BrowserWorkerSlaveState {
constructor(slave: BrowserWorkerSlave, private task: ITaskDefinition) {
super("Executing", slave);
}
public enter(): void {
const functionDeserializer = new FunctionCallDeserializer(this.slave.functionCache);
try {
const main = functionDeserializer.deserializeFunctionCall(this.task.main);
const result = main({functionCallDeserializer: functionDeserializer});
this.slave.postMessage(workerResultMessage(result));
} catch (error) {
this.slave.postMessage(functionExecutionError(error));
}
this.slave.changeState(new IdleBrowserWorkerSlaveState(this.slave));
}
}