Skip to content

Commit

Permalink
feat: split longest running task in half (#137)
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdbradley authored Mar 26, 2022
1 parent 40645f3 commit ab532e6
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 52 deletions.
11 changes: 7 additions & 4 deletions src/lib/atomics/sync-create-messenger-atomics.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { onMessageFromWebWorker } from '../sandbox/on-messenge-from-worker';
import { readMainPlatform } from '../sandbox/read-main-platform';
import {
MainAccessRequest,
MessageFromWorkerToSandbox,
Messenger,
PartytownWebWorker,
WorkerMessageType,
} from '../types';
import { onMessageFromWebWorker } from '../sandbox/on-messenge-from-worker';
import { readMainInterfaces, readMainPlatform } from '../sandbox/read-main-platform';

const createMessengerAtomics: Messenger = async (receiveMessage) => {
const size = 1024 * 1024 * 1024;
Expand All @@ -18,12 +18,15 @@ const createMessengerAtomics: Messenger = async (receiveMessage) => {
const accessReq = msg[1] as MainAccessRequest;

if (msgType === WorkerMessageType.MainDataRequestFromWorker) {
// web worker has requested data from the main thread
// collect up all the info about the main thread interfaces
// web worker has requested the initial data from the main thread
// collect up the info about the main thread interfaces
// send the main thread interface data to the web worker
const initData = readMainPlatform();
initData.$sharedDataBuffer$ = sharedDataBuffer;
worker.postMessage([WorkerMessageType.MainDataResponseToWorker, initData]);
} else if (msg[0] === WorkerMessageType.MainInterfacesRequestFromWorker) {
// web worker has requested the rest of the html/svg interfaces
worker.postMessage([WorkerMessageType.MainInterfacesResponseToWorker, readMainInterfaces()]);
} else if (msgType === WorkerMessageType.ForwardWorkerAccessRequest) {
receiveMessage(accessReq, (accessRsp) => {
const stringifiedData = JSON.stringify(accessRsp);
Expand Down
2 changes: 1 addition & 1 deletion src/lib/sandbox/main-globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { MainWindow, PartytownConfig } from '../types';
import { debug } from '../utils';

export const mainWindow: MainWindow = window.parent;
export const doc = document;
export const docImpl = document.implementation.createHTMLDocument();

export const config: PartytownConfig = mainWindow.partytown || {};
export const libPath = (config.lib || '/~partytown/') + (debug ? 'debug/' : '');
85 changes: 46 additions & 39 deletions src/lib/sandbox/read-main-platform.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
import {
createElementFromConstructor,
debug,
getConstructorName,
getNodeName,
isValidMemberName,
len,
noop,
} from '../utils';
import { config, docImpl, libPath, mainWindow } from './main-globals';
import {
InterfaceType,
InterfaceInfo,
InterfaceMember,
InitWebWorkerData,
StorageItem,
} from '../types';
import { logMain } from '../log';
import { config, doc, libPath, mainWindow } from './main-globals';

export const readMainPlatform = () => {
const startTime = debug ? performance.now() : 0;

const docImpl = doc.implementation.createHTMLDocument();
const elm = docImpl.createElement('i');
const textNode = docImpl.createTextNode('');
const comment = docImpl.createComment('');
Expand All @@ -32,13 +27,6 @@ export const readMainPlatform = () => {
const perf = mainWindow.performance;
const screen = mainWindow.screen;

// get all HTML*Element constructors on window
// and create each element to get their implementation
const elms = Object.getOwnPropertyNames(mainWindow)
.map((interfaceName) => createElementFromConstructor(docImpl, interfaceName))
.filter((elm) => elm)
.map((elm) => [elm]);

const impls: any[] = [
// window implementations
[mainWindow.history],
Expand Down Expand Up @@ -66,18 +54,9 @@ export const readMainPlatform = () => {
[elm.style],
[docImpl],
[docImpl.doctype!],
...elms,
]
.filter((implData) => implData[0])
.map((implData) => {
const impl = implData[0];
const interfaceType: InterfaceType = implData[1] as any;
const cstrName = getConstructorName(impl);
const CstrPrototype = (mainWindow as any)[cstrName].prototype;
return [cstrName, CstrPrototype, impl, interfaceType];
});
];

const $interfaces$: InterfaceInfo[] = [
const initialInterfaces: InterfaceInfo[] = [
readImplementation('Window', mainWindow),
readImplementation('Node', textNode),
];
Expand All @@ -94,26 +73,51 @@ export const readMainPlatform = () => {

const initWebWorkerData: InitWebWorkerData = {
$config$,
$interfaces$: readImplementations(impls, initialInterfaces),
$libPath$: new URL(libPath, mainWindow.location as any) + '',
$interfaces$,
$origin$: origin,
$localStorage$: readStorage('localStorage'),
$sessionStorage$: readStorage('sessionStorage'),
};

impls.map(([cstrName, CstrPrototype, impl, intefaceType]) =>
readOwnImplementation($interfaces$, cstrName, CstrPrototype, impl, intefaceType)
addGlobalConstructorUsingPrototype(
initWebWorkerData.$interfaces$,
mainWindow,
'IntersectionObserverEntry'
);

addGlobalConstructorUsingPrototype($interfaces$, mainWindow, 'IntersectionObserverEntry');
return initWebWorkerData;
};

if (debug) {
logMain(
`Read ${$interfaces$.length} interfaces in ${(performance.now() - startTime).toFixed(1)}ms`
);
}
export const readMainInterfaces = () => {
// get all HTML*Element constructors on window
// and create each element to get their implementation
const elms = Object.getOwnPropertyNames(mainWindow)
.map((interfaceName) => createElementFromConstructor(docImpl, interfaceName))
.filter((elm) => elm)
.map((elm) => [elm]);

return initWebWorkerData;
return readImplementations(elms, []);
};

const cstrs = new Set(['Object']);

const readImplementations = (impls: any[], interfaces: InterfaceInfo[]) => {
const cstrImpls = impls
.filter((implData) => implData[0])
.map((implData) => {
const impl = implData[0];
const interfaceType: InterfaceType = implData[1] as any;
const cstrName = getConstructorName(impl);
const CstrPrototype = (mainWindow as any)[cstrName].prototype;
return [cstrName, CstrPrototype, impl, interfaceType];
});

cstrImpls.map(([cstrName, CstrPrototype, impl, intefaceType]) =>
readOwnImplementation(cstrs, interfaces, cstrName, CstrPrototype, impl, intefaceType)
);

return interfaces;
};

const readImplementation = (cstrName: string, impl: any, memberName?: string) => {
Expand All @@ -126,22 +130,25 @@ const readImplementation = (cstrName: string, impl: any, memberName?: string) =>
};

const readOwnImplementation = (
cstrs: Set<string>,
interfaces: InterfaceInfo[],
cstrName: string,
CstrPrototype: any,
impl: any,
interfaceType: InterfaceType
) => {
if (cstrName !== 'Object' && !interfaces.some((i) => i[0] === cstrName)) {
if (!cstrs.has(cstrName)) {
cstrs.add(cstrName);
const SuperCstr = Object.getPrototypeOf(CstrPrototype);
const superCstrName = getConstructorName(SuperCstr);
const interfaceMembers: InterfaceMember[] = [];
const propDescriptors = Object.getOwnPropertyDescriptors(CstrPrototype);

readOwnImplementation(interfaces, superCstrName, SuperCstr, impl, interfaceType);
readOwnImplementation(cstrs, interfaces, superCstrName, SuperCstr, impl, interfaceType);

Object.keys(Object.getOwnPropertyDescriptors(CstrPrototype)).map((memberName) =>
readImplementationMember(interfaceMembers, impl, memberName)
);
for (const memberName in propDescriptors) {
readImplementationMember(interfaceMembers, impl, memberName);
}

interfaces.push([cstrName, superCstrName, interfaceMembers, interfaceType, getNodeName(impl)]);
}
Expand Down
12 changes: 9 additions & 3 deletions src/lib/service-worker/sync-create-messenger-sw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
WorkerMessageType,
} from '../types';
import { onMessageFromWebWorker } from '../sandbox/on-messenge-from-worker';
import { readMainPlatform } from '../sandbox/read-main-platform';
import { readMainInterfaces, readMainPlatform } from '../sandbox/read-main-platform';

const createMessengerServiceWorker: Messenger = (receiveMessage) => {
const swContainer = window.navigator.serviceWorker;
Expand All @@ -20,10 +20,16 @@ const createMessengerServiceWorker: Messenger = (receiveMessage) => {

return (worker: PartytownWebWorker, msg: MessageFromWorkerToSandbox) => {
if (msg[0] === WorkerMessageType.MainDataRequestFromWorker) {
// web worker has requested data from the main thread
// collect up all the info about the main thread interfaces
// web worker has requested the initial data from the main thread
// collect up info about the main thread interfaces
// send the main thread interface data to the web worker
worker.postMessage([WorkerMessageType.MainDataResponseToWorker, readMainPlatform()]);
} else if (msg[0] === WorkerMessageType.MainInterfacesRequestFromWorker) {
// web worker has requested the rest of the html/svg interfaces
worker.postMessage([
WorkerMessageType.MainInterfacesResponseToWorker,
readMainInterfaces(),
]);
} else {
onMessageFromWebWorker(worker, msg);
}
Expand Down
4 changes: 4 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export type AssignInstanceId = InstanceId | AssignWinInstanceId;

export type MessageFromWorkerToSandbox =
| [type: WorkerMessageType.MainDataRequestFromWorker]
| [type: WorkerMessageType.MainInterfacesRequestFromWorker]
| [type: WorkerMessageType.InitializedWebWorker]
| [
type: WorkerMessageType.InitializedEnvironmentScript,
Expand All @@ -43,6 +44,7 @@ export type MessageFromWorkerToSandbox =

export type MessageFromSandboxToWorker =
| [type: WorkerMessageType.MainDataResponseToWorker, initWebWorkerData: InitWebWorkerData]
| [type: WorkerMessageType.MainInterfacesResponseToWorker, interfaces: InterfaceInfo[]]
| [type: WorkerMessageType.InitializeEnvironment, initEnvData: InitializeEnvironmentData]
| [type: WorkerMessageType.InitializeNextScript, initScriptData: InitializeScriptData]
| [type: WorkerMessageType.InitializedScripts, winId: WinId]
Expand All @@ -61,6 +63,8 @@ export type MessageFromSandboxToWorker =
export const enum WorkerMessageType {
MainDataRequestFromWorker,
MainDataResponseToWorker,
MainInterfacesRequestFromWorker,
MainInterfacesResponseToWorker,
InitializedWebWorker,
InitializeEnvironment,
InitializedEnvironmentScript,
Expand Down
9 changes: 9 additions & 0 deletions src/lib/web-worker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ const receiveMessageFromSandboxToWorker = (ev: MessageEvent<MessageFromSandboxTo
// initialize the web worker with the received the main data
initWebWorker(msgValue);

// request from main that the worker needs the interfaces
webWorkerCtx.$postMessage$([WorkerMessageType.MainInterfacesRequestFromWorker]);
} else if (msgType === WorkerMessageType.MainInterfacesResponseToWorker) {
// received more main thread interfaces, append them to the array
webWorkerCtx.$interfaces$ = [...webWorkerCtx.$interfaces$, ...msgValue];
webWorkerCtx.$isInitialized$ = 1;

logWorker(`Initialized web worker`);

// send to the main thread that the web worker has been initialized
webWorkerCtx.$postMessage$([WorkerMessageType.InitializedWebWorker]);

Expand Down
5 changes: 0 additions & 5 deletions src/lib/web-worker/init-web-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
webWorkerSessionStorage,
} from './worker-constants';
import type { InitWebWorkerData } from '../types';
import { logWorker } from '../log';
import type { PartytownConfig } from '@builder.io/partytown/integration';

export const initWebWorker = (initWebWorkerData: InitWebWorkerData) => {
Expand All @@ -30,8 +29,4 @@ export const initWebWorker = (initWebWorkerData: InitWebWorkerData) => {
config[configName] = new Function('return ' + config[configName])();
}
});

webWorkerCtx.$isInitialized$ = 1;

logWorker(`Initialized web worker`);
};

1 comment on commit ab532e6

@vercel
Copy link

@vercel vercel bot commented on ab532e6 Mar 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.