dcp-client 5.4.0 → 5.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/all +1 -0
- package/build/generate-sandbox-definitions-json +15 -4
- package/build/generate-worktimes-json +41 -0
- package/dist/dcp-client-bundle.js +1 -1
- package/dist/dcp-client-bundle.js.map +1 -1
- package/generated/sandbox-definitions.json +1 -1
- package/generated/worktimes.json +1 -0
- package/lib/get-worktime-info.js +44 -0
- package/libexec/sandbox/bootstrap.js +6 -2
- package/libexec/sandbox/bravojs-env.js +17 -382
- package/libexec/sandbox/calculate-capabilities.js +1 -1
- package/libexec/sandbox/map-basic-worktime.js +53 -0
- package/libexec/sandbox/pyodide-worktime.js +197 -0
- package/libexec/sandbox/worktimes.js +277 -49
- package/npm-hooks/prepublish +22 -0
- package/package.json +19 -19
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file pyodide-worktime.js
|
|
3
|
+
* This file sets up the hooks for the Pyodide worktime. It registers the processSlice
|
|
4
|
+
* initSandbox hooks, which run slices and setup the sandbox respectively.
|
|
5
|
+
*
|
|
6
|
+
* @author Wes Garland <wes@distributive.network>
|
|
7
|
+
* @author Severn Lortie <severn@distributive.network>
|
|
8
|
+
* @author Will Pringle <will@distributive.network>
|
|
9
|
+
* @author Hamada Gasmallah <hamada@distributive.network>
|
|
10
|
+
*
|
|
11
|
+
* @date February, 2024
|
|
12
|
+
* @copyright Copyright (c) 2018-2024, Distributive, Ltd. All Rights Reserved.
|
|
13
|
+
*/
|
|
14
|
+
'use-strict';
|
|
15
|
+
/* global self, bravojs, addEventListener, postMessage */
|
|
16
|
+
// @ts-nocheck
|
|
17
|
+
|
|
18
|
+
self.wrapScriptLoading({ scriptName: 'pyodide-worktime'}, function pyodideWorktime$$fn(protectedStorage, ring1PostMessage, wrapPostMessage)
|
|
19
|
+
{
|
|
20
|
+
let wrappedPythonSliceHandler;
|
|
21
|
+
/**
|
|
22
|
+
* Registers the worktime callbacks that allow the worktime controller (worktimes.js) to setup our environment and run the worktime
|
|
23
|
+
*/
|
|
24
|
+
function registerWorktime()
|
|
25
|
+
{
|
|
26
|
+
protectedStorage.worktimes.registerWorktime({
|
|
27
|
+
name: 'pyodide',
|
|
28
|
+
version: '0.28.0',
|
|
29
|
+
processSlice,
|
|
30
|
+
initSandbox,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Function which generates a "map-basic"-like workFunction
|
|
36
|
+
* out of a Pyodide Worktime job (Python code, files, env variables).
|
|
37
|
+
*
|
|
38
|
+
* It takes any "images" passed in the workFunction "arguments" and
|
|
39
|
+
* writes them to the in memory filesystem provided by Emscripten.
|
|
40
|
+
* It adds any environment variables specified in the workFunction
|
|
41
|
+
* "arguments" to the pseudo-"process" for use.
|
|
42
|
+
* It globally imports a dcp module with function "set_slice_handler"
|
|
43
|
+
* which takes a python function as input. The python function passed
|
|
44
|
+
* to that slice handler is invoked by the function which this
|
|
45
|
+
* function creates.
|
|
46
|
+
*
|
|
47
|
+
* @param {Object} job The job being assigned to the sandbox
|
|
48
|
+
*/
|
|
49
|
+
async function initSandbox(job)
|
|
50
|
+
{
|
|
51
|
+
var pythonSliceHandler;
|
|
52
|
+
|
|
53
|
+
const pyodide = await pyodideInit();
|
|
54
|
+
const sys = pyodide.pyimport('sys');
|
|
55
|
+
|
|
56
|
+
const findImports = pyodide.runPython('import pyodide; pyodide.code.find_imports');
|
|
57
|
+
const findPythonModuleLoader = pyodide.runPython('import importlib; importlib.util.find_spec');
|
|
58
|
+
|
|
59
|
+
const parsedArguments = parsePyodideArguments(job.arguments);
|
|
60
|
+
|
|
61
|
+
// write images to file and set environment variables
|
|
62
|
+
const prepPyodide = pyodide.runPython(`
|
|
63
|
+
import tarfile, io
|
|
64
|
+
import os, sys
|
|
65
|
+
|
|
66
|
+
def prepPyodide(args):
|
|
67
|
+
for image in args['images']:
|
|
68
|
+
image = bytes(image)
|
|
69
|
+
imageFile = io.BytesIO(image)
|
|
70
|
+
tar = tarfile.open(mode='r', fileobj=imageFile)
|
|
71
|
+
|
|
72
|
+
# Don't overwrite directories which corrupts Pyodide's in memory filesystem
|
|
73
|
+
def safe_extract(tar, path="/"):
|
|
74
|
+
for member in tar.getmembers():
|
|
75
|
+
if member.isdir():
|
|
76
|
+
dir_path = os.path.join(path, member.name)
|
|
77
|
+
if not os.path.exists(dir_path):
|
|
78
|
+
os.makedirs(dir_path)
|
|
79
|
+
else:
|
|
80
|
+
tar.extract(member, path)
|
|
81
|
+
safe_extract(tar)
|
|
82
|
+
|
|
83
|
+
for item, value in args['environmentVariables'].items():
|
|
84
|
+
os.environ[item] = value
|
|
85
|
+
|
|
86
|
+
sys.argv.extend(args['sysArgv'])
|
|
87
|
+
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
prepPyodide`);
|
|
91
|
+
|
|
92
|
+
prepPyodide(pyodide.toPy(parsedArguments));
|
|
93
|
+
|
|
94
|
+
// register the dcp Python module
|
|
95
|
+
if (!sys.modules.get('dcp'))
|
|
96
|
+
{
|
|
97
|
+
const create_proxy = pyodide.runPython('import pyodide;pyodide.ffi.create_proxy');
|
|
98
|
+
|
|
99
|
+
pyodide.registerJsModule('dcp', {
|
|
100
|
+
set_slice_handler: function pyodide$$dcp$$setSliceHandler(func) {
|
|
101
|
+
pythonSliceHandler = create_proxy(func);
|
|
102
|
+
},
|
|
103
|
+
progress,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
pyodide.runPython( 'import dcp' );
|
|
107
|
+
|
|
108
|
+
// attempt to import packages from the package manager (if they're not already loaded)
|
|
109
|
+
const workFunctionPythonImports = findImports(job.workFunction).toJs();
|
|
110
|
+
const packageManagerImports = workFunctionPythonImports.filter(x=>!findPythonModuleLoader(x));
|
|
111
|
+
if (packageManagerImports.length > 0)
|
|
112
|
+
{
|
|
113
|
+
await fetchAndInitPyodidePackages(packageManagerImports);
|
|
114
|
+
await pyodide.loadPackage(packageManagerImports);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
wrappedPythonSliceHandler = workFunctionWrapper;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Evaluates the Python WorkFunction string and then executes the slice
|
|
121
|
+
* handler Python function. If no call to `dcp.set_slice_handler` is passed
|
|
122
|
+
* or a non function is passed to it.
|
|
123
|
+
* This function specifically only takes one parameter since Pyodide Slice
|
|
124
|
+
* Handlers only accept one parameter.
|
|
125
|
+
*/
|
|
126
|
+
async function workFunctionWrapper(datum)
|
|
127
|
+
{
|
|
128
|
+
const pyodide = await pyodideInit(); // returns the same promise when called multiple times
|
|
129
|
+
|
|
130
|
+
// load and execute the Python Workfunction, this populates the pythonSliceHandler variable
|
|
131
|
+
await pyodide.runPython(job.workFunction);
|
|
132
|
+
|
|
133
|
+
// failure to specify a slice handler is considered an error
|
|
134
|
+
if (!pythonSliceHandler)
|
|
135
|
+
throw new Error('ENOSLICEHANDLER: Must specify the slice handler using `dcp.set_slice_handler(fn)`');
|
|
136
|
+
|
|
137
|
+
// setting the slice handler to a non function or lambda is not supported
|
|
138
|
+
else if (typeof pythonSliceHandler !== 'function')
|
|
139
|
+
throw new Error('ENOSLICEHANDLER: Slice Handler must be a function');
|
|
140
|
+
|
|
141
|
+
const sliceHandlerResult = await pythonSliceHandler(pyodide.toPy(datum));
|
|
142
|
+
|
|
143
|
+
// if it is a PyProxy, convert its value to JavaScript
|
|
144
|
+
if (sliceHandlerResult?.toJs)
|
|
145
|
+
return sliceHandlerResult.toJs();
|
|
146
|
+
|
|
147
|
+
return sliceHandlerResult;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/*
|
|
151
|
+
* Refer to the "The Pyodide Worktime"."Work Function (JS)"."Arguments"."Commands"
|
|
152
|
+
* part of the DCP Worktimes spec.
|
|
153
|
+
*/
|
|
154
|
+
function parsePyodideArguments(args)
|
|
155
|
+
{
|
|
156
|
+
var index = 1;
|
|
157
|
+
const numArgs = args[0];
|
|
158
|
+
const images = [];
|
|
159
|
+
const environmentVariables = {};
|
|
160
|
+
const sysArgv = args.slice(numArgs);
|
|
161
|
+
|
|
162
|
+
while (index < numArgs)
|
|
163
|
+
{
|
|
164
|
+
switch (args[index])
|
|
165
|
+
{
|
|
166
|
+
case 'gzImage':
|
|
167
|
+
const image = args[index+1];
|
|
168
|
+
images.push(image);
|
|
169
|
+
index+=2;
|
|
170
|
+
break;
|
|
171
|
+
case 'env':
|
|
172
|
+
const env = args[index+1].split(/=(.*)/s);
|
|
173
|
+
index+=2;
|
|
174
|
+
environmentVariables[env[0]] = env[1];
|
|
175
|
+
break;
|
|
176
|
+
default:
|
|
177
|
+
throw new Error(`Invalid argument ${args[index]}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return { sysArgv, images, environmentVariables };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* The processSlice hook function which runs the work function for input slice data
|
|
187
|
+
* @param {*} data The slice data
|
|
188
|
+
* @returns {*} The slice result
|
|
189
|
+
*/
|
|
190
|
+
async function processSlice(data)
|
|
191
|
+
{
|
|
192
|
+
const result = await wrappedPythonSliceHandler(data);
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
registerWorktime();
|
|
197
|
+
}); /* end of fn */
|
|
@@ -1,66 +1,294 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @author
|
|
7
|
-
*
|
|
8
|
-
* @
|
|
2
|
+
* @file dcp-client/libexec/sandbox/worktimes.js
|
|
3
|
+
* Specify available worktimes, allow registering custom worktimes
|
|
4
|
+
* The single source of authority for what Worktimes are available.
|
|
5
|
+
*
|
|
6
|
+
* @author Wes Garland, wes@distributive.network
|
|
7
|
+
* Severn Lortie, severn@distributive.network
|
|
8
|
+
* Will Pringle, will@distributive.network
|
|
9
|
+
* Hamada Gasmallah, hamada@distributive.network
|
|
10
|
+
* @date January 2024
|
|
11
|
+
* @copyright Copyright (c) 2018-2024, Distributive, Ltd. All Rights Reserved.
|
|
9
12
|
*/
|
|
10
|
-
'use
|
|
13
|
+
'use-strict';
|
|
14
|
+
/* global self, bravojs, addEventListener, postMessage */
|
|
15
|
+
// @ts-nocheck
|
|
11
16
|
|
|
12
|
-
function worktimes$$fn(protectedStorage,
|
|
13
|
-
|
|
17
|
+
self.wrapScriptLoading({ scriptName: 'worktimes' }, function worktimes$$fn(protectedStorage, ring1PostMessage, wrapPostMessage) {
|
|
18
|
+
// This file starts at ring 2, but transitions to ring 3 partway through it.
|
|
19
|
+
const ring2PostMessage = self.postMessage;
|
|
20
|
+
let ring3PostMessage;
|
|
21
|
+
protectedStorage.worktimes = { registeredWorktimes: [] };
|
|
14
22
|
// when preparing a worktime, add it's globals to this object.
|
|
15
23
|
// only if the job assigned to the evaluator uses that worktime, they will
|
|
16
24
|
// be added to the allow-list
|
|
17
25
|
protectedStorage.worktimeGlobals = {};
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Register a worktime with the evaluator.
|
|
29
|
+
* @param {object} worktimeInfo Information about the worktime
|
|
30
|
+
* @param {string} worktimeInfo.name The name of the worktime
|
|
31
|
+
* @param {string} worktimeInfo.version The semantic version (semver) number of the worktime
|
|
32
|
+
* @param {function} worktimeInfo.initSandbox A function which initializes the worktime. It is passed the job as its only argument
|
|
33
|
+
* @param {function} worktimeInfo.processSlice A function which runs a slice. Must take the form processSlice(data) --> result
|
|
34
|
+
*/
|
|
35
|
+
function registerWorktime(worktimeInfo)
|
|
36
|
+
{
|
|
37
|
+
protectedStorage.worktimes.registeredWorktimes.push(worktimeInfo);
|
|
38
|
+
}
|
|
39
|
+
protectedStorage.worktimes.registerWorktime = registerWorktime;
|
|
23
40
|
|
|
24
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Returns a list of worktimes in the form [{name: string, version:string}]
|
|
43
|
+
* @returns {object[]} array of worktimeInfo objects
|
|
44
|
+
*/
|
|
45
|
+
function getWorktimeList()
|
|
25
46
|
{
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// if this is a new worktime, add it
|
|
31
|
-
else if (!foundWorktime)
|
|
32
|
-
globalThis.worktimes.push({ name, versions: [version]});
|
|
47
|
+
const worktimeList = [];
|
|
48
|
+
for (const worktime of protectedStorage.worktimes.registeredWorktimes)
|
|
49
|
+
worktimeList.push({ name: worktime.name, version: worktime.version });
|
|
50
|
+
return worktimeList;
|
|
33
51
|
}
|
|
52
|
+
protectedStorage.worktimes.getWorktimeList = getWorktimeList;
|
|
53
|
+
globalThis.getWorktimeList = getWorktimeList; // hook for worker-info.js, removed by access-list before sandboxes see it
|
|
34
54
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Get a worktime given its name and version.
|
|
57
|
+
* @note The version parameter here is not a semver range. It must be a literal version, e.g. 0.23.1
|
|
58
|
+
* @param {string} name The name of the worktime
|
|
59
|
+
* @param {string} version The version of the worktime
|
|
60
|
+
* @returns {object} The worktime
|
|
61
|
+
*/
|
|
62
|
+
function getWorktime(name, version)
|
|
39
63
|
{
|
|
40
|
-
|
|
41
|
-
globalThis.registerWorktime = registerWorktime;
|
|
64
|
+
return protectedStorage.worktimes.registeredWorktimes.find(wt => wt.name === name && wt.version === version);
|
|
42
65
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
66
|
+
protectedStorage.worktimes.getWorktime = getWorktime;
|
|
67
|
+
|
|
68
|
+
//Listens for postMessage from the sandbox
|
|
69
|
+
addEventListener('message', async function worktimes$$sandboxPostMessageHandler(event) {
|
|
70
|
+
let message = event;
|
|
71
|
+
switch (message.request)
|
|
72
|
+
{
|
|
73
|
+
/* Sandbox assigned a specific job by supervisor */
|
|
74
|
+
case 'assign':
|
|
75
|
+
{
|
|
76
|
+
try
|
|
77
|
+
{
|
|
78
|
+
if (typeof module.main !== 'undefined')
|
|
79
|
+
throw new Error('Main module was provided before job assignment');
|
|
80
|
+
|
|
81
|
+
protectedStorage.sandboxConfig = message.sandboxConfig;
|
|
82
|
+
Object.assign(self.work.job.public, message.job.public); /* override locale-specific defaults if specified */
|
|
83
|
+
mainModuleFactoryFactory(message.job);
|
|
84
|
+
}
|
|
85
|
+
catch (error)
|
|
86
|
+
{
|
|
87
|
+
ring2PostMessage({request: 'reject', error});
|
|
88
|
+
}
|
|
89
|
+
break; /* end of assign */
|
|
90
|
+
}
|
|
91
|
+
/* Supervisor has asked us to execute the work function. message.data is input datum. */
|
|
92
|
+
case 'main':
|
|
93
|
+
{
|
|
94
|
+
try
|
|
95
|
+
{
|
|
96
|
+
await runWorkFunction(message.data);
|
|
97
|
+
}
|
|
98
|
+
catch (error)
|
|
99
|
+
{
|
|
100
|
+
ring3PostMessage({ request: 'sandboxError', error });
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
default:
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
/* Report metrics to sandbox/supervisor */
|
|
110
|
+
function reportTimes (metrics)
|
|
52
111
|
{
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return 1;
|
|
56
|
-
else if (semver.eq(left, right))
|
|
57
|
-
return 0;
|
|
58
|
-
else
|
|
59
|
-
return -1;
|
|
60
|
-
});
|
|
112
|
+
const { total, webGL, webGPU, CPU } = metrics;
|
|
113
|
+
ring3PostMessage({ request: 'measurement', total, webGL, webGPU, CPU });
|
|
61
114
|
}
|
|
62
|
-
}
|
|
63
|
-
// inside the sandbox
|
|
64
|
-
else
|
|
65
|
-
self.wrapScriptLoading({ scriptName: 'worktimes' }, worktimes$$fn);
|
|
66
115
|
|
|
116
|
+
/* Report an error from the work function to the supervisor */
|
|
117
|
+
function reportError (error, metrics)
|
|
118
|
+
{
|
|
119
|
+
const err = new Error('initial state');
|
|
120
|
+
|
|
121
|
+
for (let prop of [ 'message', 'name', 'code', 'stack', 'lineNumber', 'columnNumber' ])
|
|
122
|
+
{
|
|
123
|
+
try
|
|
124
|
+
{
|
|
125
|
+
if (typeof error[prop] !== 'undefined')
|
|
126
|
+
err[prop] = error[prop];
|
|
127
|
+
}
|
|
128
|
+
catch(e){};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
reportTimes(metrics); // Report metrics for both 'workReject' and 'workError'.
|
|
132
|
+
ring3PostMessage({ request: 'workError', error: err });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Report a result from work function and metrics to the supervisor.
|
|
137
|
+
* @param result the value that the work function returned promise resolved to
|
|
138
|
+
*/
|
|
139
|
+
function reportResult (result, metrics)
|
|
140
|
+
{
|
|
141
|
+
try
|
|
142
|
+
{
|
|
143
|
+
reportTimes(metrics);
|
|
144
|
+
ring3PostMessage({ request: 'complete', result });
|
|
145
|
+
}
|
|
146
|
+
catch (error)
|
|
147
|
+
{
|
|
148
|
+
ring3PostMessage({ request: 'sandboxError', error });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Actual mechanics for running a work function. ** This function will never reject **
|
|
154
|
+
*
|
|
155
|
+
* @param successCallback callback to invoke when the work function has finished running;
|
|
156
|
+
* it receives as its argument the resolved promise returned from
|
|
157
|
+
* the work function
|
|
158
|
+
* @param errorCallback callback to invoke when the work function rejects. It receives
|
|
159
|
+
* as its argument the error that it rejected with.
|
|
160
|
+
* @returns unused promise
|
|
161
|
+
*/
|
|
162
|
+
async function runWorkFunction_inner(datum, wallDuration, successCallback, errorCallback)
|
|
163
|
+
{
|
|
164
|
+
/** @typedef {import("./timer-classes.js").TimeInterval} TimeInterval */
|
|
165
|
+
var rejection = false;
|
|
166
|
+
var result;
|
|
167
|
+
let metrics;
|
|
168
|
+
protectedStorage.emitConsoleMessages = true;
|
|
169
|
+
protectedStorage.flushConsoleBuffer();
|
|
170
|
+
try
|
|
171
|
+
{
|
|
172
|
+
result = await module.main.worktime.processSlice(datum);
|
|
173
|
+
}
|
|
174
|
+
catch (error)
|
|
175
|
+
{
|
|
176
|
+
rejection = error;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
try
|
|
180
|
+
{
|
|
181
|
+
// reset the device states and flush all pending tasks
|
|
182
|
+
protectedStorage.lockTimers(); // lock timers so no new timeouts will be run.
|
|
183
|
+
if (protectedStorage.webGPU)
|
|
184
|
+
protectedStorage.webGPU.lock();
|
|
185
|
+
|
|
186
|
+
// Let microtask queue finish before getting metrics. With all event-loop possibilities locked,
|
|
187
|
+
// only the microtask could trigger new code, so waiting for a setTimeout guarantees everything's done
|
|
188
|
+
await new Promise((r) => protectedStorage.realSetTimeout.call(globalThis, r, 1));
|
|
189
|
+
|
|
190
|
+
// flush any pending console events, especially in the case of a repeating message that hasn't been emitted yet
|
|
191
|
+
// then, disable emission of any more console messages
|
|
192
|
+
try { protectedStorage.dispatchSameConsoleMessage(); } catch(e) {};
|
|
193
|
+
protectedStorage.emitConsoleMessages = false;
|
|
194
|
+
|
|
195
|
+
metrics = await protectedStorage.bigBrother.globalTrackers.getMetrics();
|
|
196
|
+
|
|
197
|
+
await protectedStorage.bigBrother.globalTrackers.reset();
|
|
198
|
+
}
|
|
199
|
+
catch (error)
|
|
200
|
+
{
|
|
201
|
+
ring3PostMessage({ request: 'sandboxError', error });
|
|
202
|
+
}
|
|
203
|
+
finally
|
|
204
|
+
{
|
|
205
|
+
// due to the nature of the micro task queue, await, our `reset()` cancels all the things that could cause new
|
|
206
|
+
// tasks, and we wait for all pending task to finish in `reset()`, we are guaranteed to have an empty task queue
|
|
207
|
+
// now. Hence it's ok to stop the wall clock measurement now
|
|
208
|
+
wallDuration.stop();
|
|
209
|
+
|
|
210
|
+
// safety: wallDuration is always stopped, `length` will not throw
|
|
211
|
+
metrics = { ...metrics, total: wallDuration.length };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (rejection)
|
|
215
|
+
errorCallback(rejection, metrics);
|
|
216
|
+
else
|
|
217
|
+
successCallback(result, metrics);
|
|
218
|
+
|
|
219
|
+
/* CPU time measurement ends when this function's return value is resolved or rejected */
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Run the work function, returning a promise that resolves once the function has finished
|
|
224
|
+
* executing.
|
|
225
|
+
*
|
|
226
|
+
* @param datam an element of the input set
|
|
227
|
+
*/
|
|
228
|
+
async function runWorkFunction(datum)
|
|
229
|
+
{
|
|
230
|
+
// reset the time used for feature detection
|
|
231
|
+
protectedStorage.bigBrother.globalTrackers.resetRecordedTime();
|
|
232
|
+
const wallDuration = new protectedStorage.TimeInterval();
|
|
233
|
+
protectedStorage.bigBrother.globalTrackers.wallDuration = wallDuration;
|
|
234
|
+
|
|
235
|
+
if (protectedStorage.webGPU)
|
|
236
|
+
protectedStorage.webGPU.unlock();
|
|
237
|
+
await protectedStorage.unlockTimers();
|
|
238
|
+
|
|
239
|
+
/* Use setTimeout trampoline to
|
|
240
|
+
* 1. shorten stack
|
|
241
|
+
* 2. initialize the event loop measurement code
|
|
242
|
+
*/
|
|
243
|
+
protectedStorage.setTimeout(() => runWorkFunction_inner(datum, wallDuration, reportResult, reportError));
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Factory function which returns the main module factory for use as the second argument in module.declare.
|
|
247
|
+
*
|
|
248
|
+
* @param {object} job the job property of the assign message from the supervisor.
|
|
249
|
+
* @returns {function} mainModuleFactory
|
|
250
|
+
*/
|
|
251
|
+
function mainModuleFactoryFactory(job)
|
|
252
|
+
{
|
|
253
|
+
module.declare(job.dependencies, mainModuleFactory);
|
|
254
|
+
|
|
255
|
+
/* mainModule: this is the function that is run first by BravoJS once the module system has been
|
|
256
|
+
* loaded. It functions as the main module in a CommonJS environment, and its job is to initialize
|
|
257
|
+
* the work function for use. Once initialized, the work function is accessible as the main module's
|
|
258
|
+
* `job` export. The work function is eventually invoked by a message from the supervisor which
|
|
259
|
+
* invokes runWorkFunction() above.
|
|
260
|
+
*
|
|
261
|
+
* This function is infallible; any exceptions or rejections caught are reported to the Supervisor.
|
|
262
|
+
*/
|
|
263
|
+
async function mainModuleFactory(require, exports, module)
|
|
264
|
+
{
|
|
265
|
+
try
|
|
266
|
+
{
|
|
267
|
+
if (exports.hasOwnProperty('job'))
|
|
268
|
+
throw new Error("Tried to assign sandbox when it was already assigned"); /* Should be impossible - might happen if throw during assign? */
|
|
269
|
+
exports.worktime = false;
|
|
270
|
+
job.requirePath.map(p => require.paths.push(p));
|
|
271
|
+
job.modulePath.map(p => module.paths.push(p));
|
|
272
|
+
|
|
273
|
+
exports.worktime = protectedStorage.worktimes.getWorktime(job.worktime.name, job.worktime.version);
|
|
274
|
+
if (!exports.worktime)
|
|
275
|
+
throw new Error(`Unsupported worktime: ${job.worktime.name}`);
|
|
276
|
+
await exports.worktime.initSandbox(job);
|
|
277
|
+
}
|
|
278
|
+
catch(error)
|
|
279
|
+
{
|
|
280
|
+
reportError(error);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
ring2PostMessage({
|
|
285
|
+
request: 'assigned',
|
|
286
|
+
jobAddress: job.address,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// Now that the evaluator is assigned, wrap post message for ring 3
|
|
290
|
+
wrapPostMessage();
|
|
291
|
+
ring3PostMessage = self.postMessage;
|
|
292
|
+
} /* end of main module */
|
|
293
|
+
}
|
|
294
|
+
}); /* end of worktimes$$wrapScriptLoading IIFE */
|
package/npm-hooks/prepublish
CHANGED
|
@@ -11,6 +11,28 @@ cd `dirname "$0"`/..
|
|
|
11
11
|
yellow='\e[33m'
|
|
12
12
|
normal='\e[0m'
|
|
13
13
|
|
|
14
|
+
currentWorktimes=$(CONSOLE_RESULTS=true node build/generate-worktimes-json)
|
|
15
|
+
recordedWorktimes=$(< ./generated/worktimes.json)
|
|
16
|
+
if [[ "$currentWorktimes" != "$recordedWorktimes" ]]; then
|
|
17
|
+
echo
|
|
18
|
+
echo -e "Generated worktimes: ${recordedWorktimes}" > /dev/stderr
|
|
19
|
+
echo -e "Current worktimes: ${currentWorktimes}" > /dev/stderr
|
|
20
|
+
echo -e "${yellow}pre-publish: abort due to generated worktimes not matching currently available worktimes${normal}" > /dev/stderr
|
|
21
|
+
echo
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
currentSandboxFiles=$(CONSOLE_RESULTS=true node build/generate-sandbox-definitions-json)
|
|
26
|
+
recordedSandboxFiles=$(< ./generated/sandbox-definitions.json)
|
|
27
|
+
if [[ "$currentSandboxFiles" != "$recordedSandboxFiles" ]]; then
|
|
28
|
+
echo
|
|
29
|
+
echo -e "Generated sandbox definitions: ${currentSandboxFiles}" > /dev/stderr
|
|
30
|
+
echo -e "Current sandbox definitions: ${recordedSandboxFiles}" > /dev/stderr
|
|
31
|
+
echo -e "${yellow}pre-publish: abort due to generated sandbox definitions not matching generation file${normal}" > /dev/stderr
|
|
32
|
+
echo
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
14
36
|
git ls-files --error-unmatch `find . -type f | egrep -v '^\./(node_modules|\.git|\.dcp-build|build)'` >/dev/null
|
|
15
37
|
if [ "$?" != "0" ]; then
|
|
16
38
|
echo
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dcp-client",
|
|
3
|
-
"version": "5.4.
|
|
3
|
+
"version": "5.4.2",
|
|
4
4
|
"dcp": {
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "3853a8dc41e0c33886fc337534f5c21aaf62dc56",
|
|
6
6
|
"repository": "git@gitlab.com:Distributed-Compute-Protocol/dcp.git"
|
|
7
7
|
},
|
|
8
8
|
"description": "Core libraries for accessing DCP network",
|
|
@@ -38,34 +38,34 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"atob": "2.1.2",
|
|
40
40
|
"bravojs": "^1.0.16",
|
|
41
|
-
"btoa": "
|
|
42
|
-
"chalk": "
|
|
43
|
-
"ethereumjs-util": "
|
|
44
|
-
"ethereumjs-wallet": "
|
|
45
|
-
"html-to-text": "
|
|
46
|
-
"http-proxy-agent": "
|
|
47
|
-
"https-agent": "
|
|
48
|
-
"https-proxy-agent": "
|
|
41
|
+
"btoa": "1.2.1",
|
|
42
|
+
"chalk": "4.1.2",
|
|
43
|
+
"ethereumjs-util": "7.1.5",
|
|
44
|
+
"ethereumjs-wallet": "1.0.2",
|
|
45
|
+
"html-to-text": "5.1.1",
|
|
46
|
+
"http-proxy-agent": "4.0.1",
|
|
47
|
+
"https-agent": "1.0.0",
|
|
48
|
+
"https-proxy-agent": "5.0.1",
|
|
49
49
|
"kvin": "^9.0.0",
|
|
50
50
|
"memfs": "4.17.2",
|
|
51
51
|
"nanoid": "3.3.11",
|
|
52
|
-
"physical-cpu-count": "
|
|
53
|
-
"polyfill-crypto.getrandomvalues": "
|
|
52
|
+
"physical-cpu-count": "2.0.0",
|
|
53
|
+
"polyfill-crypto.getrandomvalues": "1.0.0",
|
|
54
54
|
"posix-getopt": "1.2.1",
|
|
55
|
-
"regedit": "
|
|
56
|
-
"semver": "
|
|
57
|
-
"socket.io-client": "4.8.
|
|
55
|
+
"regedit": "3.0.3",
|
|
56
|
+
"semver": "7.7.3",
|
|
57
|
+
"socket.io-client": "4.8.1",
|
|
58
58
|
"source-map-support": "0.5.21",
|
|
59
59
|
"unionfs": "4.5.4",
|
|
60
60
|
"webpack": "5.94.0",
|
|
61
|
-
"webpack-cli": "
|
|
61
|
+
"webpack-cli": "4.10.0",
|
|
62
62
|
"xmlhttprequest-ssl": "3.0.0",
|
|
63
63
|
"yargs": "16.2.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@
|
|
67
|
-
"eslint": "
|
|
68
|
-
"peter": "
|
|
66
|
+
"@distributive/eslint-config": "2.1.4",
|
|
67
|
+
"eslint": "9.39.1",
|
|
68
|
+
"peter": "2.4.7"
|
|
69
69
|
},
|
|
70
70
|
"engines": {
|
|
71
71
|
"node": ">=18",
|