Skip to content

Commit 1793ecc

Browse files
authored
Merge pull request emscripten-core#23 from rstz/file_packager_readme
Added Readme for file packager
2 parents 77f9b15 + a23ae18 commit 1793ecc

File tree

6 files changed

+143
-2
lines changed

6 files changed

+143
-2
lines changed

pthreadfs/examples/Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ dist/$(NODEJSTESTS)/%.js : $(OBJ)/%.o $(OBJ)/pthreadfs.o $(PTHREADFS_JS)
151151
# - $(PACKAGERTESTS)/input/mediumlarge/subfolder/mediumfile.txt: Size 138670 bytes, first line "Begin mediumfile.txt -------------------------------------------"
152152
# - $(PACKAGERTESTS)/input/mediumlarge/bigfile.txt: Size 212992000 bytes, first line "Begin bigfile.txt ----------------------------------------------"
153153
.PHONY: packager-tests
154-
packager-tests: dist/$(PACKAGERTESTS)/preloading_without_pthreadfs.html dist/$(PACKAGERTESTS)/preloading.html dist/$(PACKAGERTESTS)/intermediate_loading.html
154+
packager-tests: dist/$(PACKAGERTESTS)/preloading_without_pthreadfs.html dist/$(PACKAGERTESTS)/preloading.html dist/$(PACKAGERTESTS)/load_package_sync.html dist/$(PACKAGERTESTS)/load_package_async.html
155155

156156
# Create the file packages
157157
dist/$(PACKAGERTESTS)/pkg_preload_small.js: $(PACKAGER_INPUT_SMALL) $(FILE_PACKAGER)
@@ -177,7 +177,11 @@ dist/$(PACKAGERTESTS)/preloading.html: $(OBJ)/preloading.o $(OBJ)/pthreadfs.o di
177177
mkdir -p dist/$(PACKAGERTESTS)
178178
$(EMCC) $(LINK_FLAGS) -o $@ --js-library=$(PTHREADFS_JS) --pre-js $(word 3,$^) --pre-js $(word 4,$^) $< $(word 2,$^)
179179

180-
dist/$(PACKAGERTESTS)/intermediate_loading.html: $(OBJ)/intermediate_loading.o $(OBJ)/pthreadfs.o dist/$(PACKAGERTESTS)/pkg_intermediate_small.js dist/$(PACKAGERTESTS)/pkg_intermediate_mediumlarge.js $(PTHREADFS_JS)
180+
dist/$(PACKAGERTESTS)/load_package_sync.html: $(OBJ)/load_package_sync.o $(OBJ)/pthreadfs.o dist/$(PACKAGERTESTS)/pkg_intermediate_small.js dist/$(PACKAGERTESTS)/pkg_intermediate_mediumlarge.js $(PTHREADFS_JS)
181+
mkdir -p dist/$(PACKAGERTESTS)
182+
$(EMCC) $(LINK_FLAGS) -o $@ --js-library=$(PTHREADFS_JS) $< $(word 2,$^)
183+
184+
dist/$(PACKAGERTESTS)/load_package_async.html: $(OBJ)/load_package_async.o $(OBJ)/pthreadfs.o dist/$(PACKAGERTESTS)/pkg_intermediate_small.js $(PTHREADFS_JS)
181185
mkdir -p dist/$(PACKAGERTESTS)
182186
$(EMCC) $(LINK_FLAGS) -o $@ --js-library=$(PTHREADFS_JS) $< $(word 2,$^)
183187

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2021 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+
9+
#include <assert.h>
10+
#include <fstream>
11+
#include <iostream>
12+
#include <string>
13+
#include <sys/stat.h>
14+
#include "pthreadfs.h"
15+
16+
struct file_info {
17+
public:
18+
file_info(std::string path, std::string first_line, int size): path_(path), first_line_(first_line), size_(size) {}
19+
std::string path_;
20+
std::string first_line_;
21+
int size_;
22+
};
23+
24+
void test(void* arg) {
25+
file_info* file = (file_info*) arg;
26+
std::cout << "Start reading first line of file " << file->path_ << std::endl;
27+
std::ifstream stream(file->path_);
28+
std::string read_line;
29+
std::getline(stream, read_line);
30+
stream.close();
31+
32+
std::cout << " " << read_line << std::endl;
33+
assert(read_line == file->first_line_);
34+
35+
struct stat s;
36+
int err = stat(file->path_.c_str(), &s);
37+
assert(!err);
38+
assert(s.st_size == file->size_);
39+
40+
std::cout << "Success.\n";
41+
}
42+
43+
int main() {
44+
std::cout << "Do some work before loading files.\n";
45+
46+
file_info* small_file = new file_info("persistent/intermediate_loading/smallfile.txt", "These are the contents of a very small file.", 188);
47+
file_info* medium_file = new file_info("persistent/intermediate_loading/subfolder/mediumfile.txt",
48+
"Begin mediumfile.txt -------------------------------------------", 138670);
49+
file_info* big_file = new file_info("persistent/intermediate_loading/bigfile.txt", "Begin bigfile.txt ----------------------------------------------",
50+
212992000);
51+
52+
EM_ASM({
53+
importScripts("pkg_intermediate_small.js");
54+
PThreadFS.init('persistent').then(async () => {
55+
let load_fct = Module["pthreadfs_available_packages"].pop();
56+
await load_fct();
57+
58+
// Load the second function.
59+
importScripts("pkg_intermediate_mediumlarge.js");
60+
PThreadFS.init('persistent').then(async () => {
61+
let load_fct = Module["pthreadfs_available_packages"].pop();
62+
await load_fct();
63+
});
64+
});
65+
});
66+
// Wait 1 second for loading the small package.
67+
emscripten_async_call(test, small_file, 1000);
68+
69+
// Wait another second for loading the larger package.
70+
emscripten_async_call(test, medium_file, 2000);
71+
emscripten_async_call(test, big_file, 2000);
72+
73+
return 0;
74+
}

pthreadfs/file_packager_readme.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# PThreadFS File Packager Manual
2+
3+
The PThreadFS file packager adapts [Emscripten's file packager](https://emscripten.org/docs/porting/files/packaging_files.html) for PThreadFS. Support for PThreadFS is activated with the switch `--use-pthreadfs`, e.g.
4+
```
5+
python3 file_packager.py --preload ./input/files@/persistent --use_pthreadfs --js-output=my_package.js my_package.data
6+
```
7+
8+
The packaged files can be loaded into PThreadFS via multiple paths. In the remainder of this manual, `my_package.js` will be the _javascript helper_ file generated by the file packager (i.e., the file specified in the `--js-output=` switch). The generated `mypackage.data` file is called the _package_ and may hold multiple files.
9+
10+
## Howto for common scenarios
11+
12+
### Loading files on startup
13+
If files need to be available before the first file I/O (or very early during the program's execution), they can be loaded as soon as PThreadFS initializes. This can be achieved by running `my_package.js` before the Wasm module is instantiated (e.g. during `--pre-js`).
14+
15+
It is possible to load multiple packages at startup by adding all of their respective javascript helpers before instantiation.
16+
17+
See `examples/preloading.cpp` for an example.
18+
19+
### Loading files during execution (synchronously)
20+
Additional packages can be loaded synchronously during execution by running the following from the WebAssembly module
21+
```c++
22+
#include "pthreadfs.h"
23+
// [other code]
24+
pthreadfs_load_package("my_package.js");
25+
```
26+
This command can also be triggered from the Javascript side by exporting the `pthreadfs_load_package` function. Calling `pthreadfs_load_package` from the Javascript's main thread is *unsupported*, since doing so may block the main thread. See the next section for alternatives.
27+
28+
See `examples/load_package_sync.cpp` for an example.
29+
30+
### Loading files during execution (asynchronously)
31+
32+
It is possible to asynchronously load packages from a Javascript context. The first step is to execute `my_package.js` script (e.g. by using `importScripts()` in workers, or any other method). Right after that, it suffices to run the following code:
33+
```
34+
// This requires a JS context that has defined the `Module` object.
35+
await PThreadFS.init('persistent');
36+
let load_fct = Module["pthreadfs_available_packages"].pop();
37+
await load_fct();
38+
```
39+
Due to limitations of OPFS Access Handles, loading packages from the main thread may be quite slow. The expected slowdown is between 200% and 1000%. The same applies to calling PThreadFS functions such as `PThreadFS.writeFile()` directly. Asynchronously loading files during execution from a worker thread should not experience any slowdown when compared to loading on startup.
40+
41+
See `examples/load_package_async.cpp` for an example.
42+
43+
### Compiling the examples
44+
45+
The examples for the file packager are found in `examples/packager-tests`.
46+
Building the packager tests requires manual creation of the following files:
47+
- packager-tests/input/small/smallfile.txt: Size 188 bytes, first line "These are the contents of a very small file."
48+
- packager-tests/input/mediumlarge/subfolder/mediumfile.txt: Size 138670 bytes, first line "Begin mediumfile.txt -------------------------------------------"
49+
- packager-tests/input/mediumlarge/bigfile.txt: Size 212992000 bytes, first line "Begin bigfile.txt ----------------------------------------------"
50+
51+
After creating these files, the tests can be built by running `make packager-tests`.
52+
53+
## Known limitations
54+
- File packager options `--embed` and `--lz4` cannot be used.
55+
- Asynchronously loading packages is not possible when using the MEMFS backend

pthreadfs/library_pthreadfs.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2438,6 +2438,10 @@ mergeInto(LibraryManager.library, SyscallsLibrary);
24382438
}
24392439
},
24402440
init: async function(folder) {
2441+
if (PThreadFS.initialized) {
2442+
return;
2443+
}
2444+
PThreadFS.initialized = true;
24412445
if (typeof folder !== 'string' || folder.includes('/')) {
24422446
console.log("PThreadFS warning: Bad folder name: " + folder);
24432447
console.log(" The folder name should be a string that does not include /");

pthreadfs/src/js/library_asyncfs.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,10 @@
15211521
}
15221522
},
15231523
init: async function(folder) {
1524+
if (PThreadFS.initialized) {
1525+
return;
1526+
}
1527+
PThreadFS.initialized = true;
15241528
if (typeof folder !== 'string' || folder.includes('/')) {
15251529
console.log("PThreadFS warning: Bad folder name: " + folder);
15261530
console.log(" The folder name should be a string that does not include /");

0 commit comments

Comments
 (0)