Skip to content

Commit 68df71f

Browse files
authored
[WasmFS] Add wasmfs_before_preload hook to run code before preloading (#16967)
Preloaded files are added during startup, but sometimes the user wants to load them under a custom backend and not the default. In the old FS, the user could mount directories in a Module.preRun, but with WasmFS that has to be done in compiled code. To allow that, this defines a hook that users can implement: wasmfs_before_preload If that the symbol is defined then it is called right before preloading. To make this work the file preloading code will now skip any directories that already exist, instead of erroring on them.
1 parent 32411ad commit 68df71f

File tree

6 files changed

+78
-10
lines changed

6 files changed

+78
-10
lines changed

system/include/emscripten/wasmfs.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ int wasmfs_create_directory(const char* path, long mode, backend_t backend);
3535
// Backend creation
3636

3737
// Creates a JSFile Backend in the new file system.
38-
backend_t wasmfs_create_js_file_backend();
38+
backend_t wasmfs_create_js_file_backend(void);
3939

4040
// A function that receives a void* and returns a backend.
4141
typedef backend_t (*backend_constructor_t)(void*);
@@ -50,6 +50,15 @@ backend_t wasmfs_create_node_backend(const char* root);
5050

5151
backend_t wasmfs_create_opfs_backend(void);
5252

53+
// Hooks
54+
55+
// A hook users can do to run code during WasmFS startup. This hook happens
56+
// before file preloading, so user code could create backends and mount them,
57+
// which would then affect in which backend the preloaded files are loaded (the
58+
// preloaded files have paths, and so they are added to that path and whichever
59+
// backend is present there).
60+
void wasmfs_before_preload(void);
61+
5362
#ifdef __cplusplus
5463
}
5564
#endif

system/lib/wasmfs/wasmfs.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ void _wasmfs_get_preloaded_path_name(int index, char* fileName);
3939
void _wasmfs_get_preloaded_child_path(int index, char* childName);
4040
}
4141

42+
// If the user does not implement this hook, do nothing.
43+
__attribute__((weak)) extern "C" void wasmfs_before_preload(void) {}
44+
45+
// Set up global data structures and preload files.
46+
WasmFS::WasmFS() : rootDirectory(initRootDirectory()), cwd(rootDirectory) {
47+
wasmfs_before_preload();
48+
preloadFiles();
49+
}
50+
4251
WasmFS::~WasmFS() {
4352
// Flush musl libc streams.
4453
// TODO: Integrate musl exit() which would call this for us. That might also
@@ -92,9 +101,6 @@ void WasmFS::preloadFiles() {
92101
assert(timesCalled == 1);
93102
#endif
94103

95-
// Obtain the backend of the root directory.
96-
auto rootBackend = getRootDirectory()->getBackend();
97-
98104
// Ensure that files are preloaded from the main thread.
99105
assert(emscripten_is_main_runtime_thread());
100106

@@ -124,8 +130,14 @@ void WasmFS::preloadFiles() {
124130
char childName[PATH_MAX] = {};
125131
_wasmfs_get_preloaded_child_path(i, childName);
126132

133+
auto lockedParentDir = parentDir->locked();
134+
if (lockedParentDir.getChild(childName)) {
135+
// The child already exists, so we don't need to do anything here.
136+
continue;
137+
}
138+
127139
auto inserted =
128-
parentDir->locked().insertDirectory(childName, S_IRUGO | S_IXUGO);
140+
lockedParentDir.insertDirectory(childName, S_IRUGO | S_IXUGO);
129141
assert(inserted && "TODO: handle preload insertion errors");
130142
}
131143

system/lib/wasmfs/wasmfs.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,7 @@ class WasmFS {
3737
void preloadFiles();
3838

3939
public:
40-
// Set up global data structures and preload files.
41-
WasmFS() : rootDirectory(initRootDirectory()), cwd(rootDirectory) {
42-
preloadFiles();
43-
}
44-
40+
WasmFS();
4541
~WasmFS();
4642

4743
FileTable& getFileTable() { return fileTable; }

tests/test_other.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11721,6 +11721,13 @@ def test_wasmfs_jsfile_proxying_backend(self):
1172111721
self.set_setting('EXIT_RUNTIME')
1172211722
self.test_wasmfs_jsfile()
1172311723

11724+
def test_wasmfs_before_preload(self):
11725+
self.set_setting('WASMFS')
11726+
os.mkdir('js_backend_files')
11727+
create_file('js_backend_files/file.dat', 'data')
11728+
self.emcc_args += ['--preload-file', 'js_backend_files/file.dat']
11729+
self.do_run_in_out_file_test('wasmfs/wasmfs_before_preload.c')
11730+
1172411731
@disabled('Running with initial >2GB heaps is not currently supported on the CI version of Node')
1172511732
def test_hello_world_above_2gb(self):
1172611733
self.run_process([EMCC, test_file('hello_world.c'), '-sGLOBAL_BASE=2147483648', '-sINITIAL_MEMORY=3GB'])

tests/wasmfs/wasmfs_before_preload.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2022 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#include <assert.h>
9+
#include <emscripten/console.h>
10+
#include <emscripten/wasmfs.h>
11+
#include <stdio.h>
12+
13+
static backend_t my_js_file_backend;
14+
15+
void wasmfs_before_preload(void) {
16+
emscripten_console_log("before_preload");
17+
18+
// Make a JS file backend during startup, by implementing this hook function,
19+
// so that the preloaded files end up stored there (instead of in the default
20+
// backend).
21+
my_js_file_backend = wasmfs_create_js_file_backend();
22+
assert(my_js_file_backend);
23+
int result =
24+
wasmfs_create_directory("/js_backend_files", 0777, my_js_file_backend);
25+
assert(result == 0);
26+
}
27+
28+
int main() {
29+
emscripten_console_log("main");
30+
31+
// The root uses the default backend.
32+
backend_t default_backend = wasmfs_get_backend_by_path("/");
33+
assert(default_backend != my_js_file_backend);
34+
35+
// The preloaded file uses the js backend we created during startup.
36+
backend_t file_backend =
37+
wasmfs_get_backend_by_path("/js_backend_files/file.dat");
38+
assert(file_backend == my_js_file_backend);
39+
40+
emscripten_console_log("success");
41+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
before_preload
2+
main
3+
success

0 commit comments

Comments
 (0)