Skip to content

Commit 5dfeb89

Browse files
authored
[WasmFS][NFC] Refactor special files (#16892)
Move the standard stream implementations from the streams.h header to a new special_files.cpp file in preparation for adding addition special files like /dev/random. Add a new special_files.h header that exposes only the functions to get the singleton instances for each special file rather than the class structure of their implementations. Simplify the implementation of StdoutFile and StderrFile by moving their duplicated flush implementations into their shared WritingStdFile parent.
1 parent ebc3145 commit 5dfeb89

File tree

9 files changed

+170
-191
lines changed

9 files changed

+170
-191
lines changed

system/lib/wasmfs/file.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ class File : public std::enable_shared_from_this<File> {
122122
};
123123

124124
class DataFile : public File {
125+
protected:
125126
// Notify the backend when this file is opened or closed. The backend is
126127
// responsible for keeping files accessible as long as they are open, even if
127128
// they are unlinked.
@@ -176,6 +177,7 @@ class Directory : public File {
176177
// TODO: Use a cache data structure with smaller code size.
177178
std::map<std::string, DCacheEntry> dcache;
178179

180+
protected:
179181
// Return the `File` object corresponding to the file with the given name or
180182
// null if there is none.
181183
virtual std::shared_ptr<File> getChild(const std::string& name) = 0;

system/lib/wasmfs/file_table.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77
// See https://github.com/emscripten-core/emscripten/issues/15041.
88

99
#include "file_table.h"
10-
#include "streams.h"
10+
#include "special_files.h"
1111

1212
namespace wasmfs {
1313

1414
FileTable::FileTable() {
1515
entries.push_back(
16-
std::make_shared<OpenFileState>(0, O_RDONLY, StdinFile::getSingleton()));
16+
std::make_shared<OpenFileState>(0, O_RDONLY, SpecialFiles::getStdin()));
1717
entries.push_back(
18-
std::make_shared<OpenFileState>(0, O_WRONLY, StdoutFile::getSingleton()));
18+
std::make_shared<OpenFileState>(0, O_WRONLY, SpecialFiles::getStdout()));
1919
entries.push_back(
20-
std::make_shared<OpenFileState>(0, O_WRONLY, StderrFile::getSingleton()));
20+
std::make_shared<OpenFileState>(0, O_WRONLY, SpecialFiles::getStderr()));
2121
}
2222

2323
std::shared_ptr<OpenFileState> FileTable::Handle::getEntry(__wasi_fd_t fd) {

system/lib/wasmfs/special_files.cpp

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Copyright 2021 The Emscripten Authors. All rights reserved.
2+
// Emscripten is available under two separate licenses, the MIT license and the
3+
// University of Illinois/NCSA Open Source License. Both these licenses can be
4+
// found in the LICENSE file.
5+
// This file defines the standard streams of the new file system.
6+
// Current Status: Work in Progress.
7+
// See https://github.com/emscripten-core/emscripten/issues/15041.
8+
9+
#include <emscripten/html5.h>
10+
#include <vector>
11+
#include <wasi/api.h>
12+
13+
#include "special_files.h"
14+
15+
namespace wasmfs::SpecialFiles {
16+
17+
namespace {
18+
19+
class StdinFile : public DataFile {
20+
void open(oflags_t) override {}
21+
void close() override {}
22+
23+
__wasi_errno_t write(const uint8_t* buf, size_t len, off_t offset) override {
24+
return __WASI_ERRNO_INVAL;
25+
}
26+
27+
__wasi_errno_t read(uint8_t* buf, size_t len, off_t offset) override {
28+
// TODO: Implement reading from stdin.
29+
abort();
30+
};
31+
32+
void flush() override {}
33+
size_t getSize() override { return 0; }
34+
void setSize(size_t size) override {}
35+
36+
public:
37+
StdinFile() : DataFile(S_IRUGO, NullBackend, S_IFCHR) { seekable = false; }
38+
};
39+
40+
// A standard stream that writes: stdout or stderr.
41+
class WritingStdFile : public DataFile {
42+
protected:
43+
std::vector<char> writeBuffer;
44+
45+
void open(oflags_t) override {}
46+
void close() override {}
47+
48+
__wasi_errno_t read(uint8_t* buf, size_t len, off_t offset) override {
49+
return __WASI_ERRNO_INVAL;
50+
};
51+
52+
void flush() override {
53+
// Write a null to flush the output if we have content.
54+
if (!writeBuffer.empty()) {
55+
const uint8_t nothing = '\0';
56+
write(&nothing, 1, 0);
57+
}
58+
}
59+
60+
size_t getSize() override { return 0; }
61+
void setSize(size_t size) override {}
62+
63+
__wasi_errno_t writeToJS(const uint8_t* buf,
64+
size_t len,
65+
void (*console_write)(const char*),
66+
std::vector<char>& fd_write_buffer) {
67+
for (size_t j = 0; j < len; j++) {
68+
uint8_t current = buf[j];
69+
// Flush on either a null or a newline.
70+
if (current == '\0' || current == '\n') {
71+
fd_write_buffer.push_back('\0'); // for null-terminated C strings
72+
console_write(fd_write_buffer.data());
73+
fd_write_buffer.clear();
74+
} else {
75+
fd_write_buffer.push_back(current);
76+
}
77+
}
78+
return __WASI_ERRNO_SUCCESS;
79+
}
80+
81+
public:
82+
WritingStdFile() : DataFile(S_IWUGO, NullBackend, S_IFCHR) {
83+
seekable = false;
84+
}
85+
};
86+
87+
class StdoutFile : public WritingStdFile {
88+
__wasi_errno_t write(const uint8_t* buf, size_t len, off_t offset) override {
89+
// Node and worker threads issue in Emscripten:
90+
// https://github.com/emscripten-core/emscripten/issues/14804.
91+
// Issue filed in Node: https://github.com/nodejs/node/issues/40961
92+
// This is confirmed to occur when running with EXIT_RUNTIME and
93+
// PROXY_TO_PTHREAD. This results in only a single console.log statement
94+
// being outputted. The solution for now is to use out() and err() instead.
95+
return writeToJS(buf, len, &_emscripten_out, writeBuffer);
96+
}
97+
98+
public:
99+
StdoutFile() {}
100+
};
101+
102+
class StderrFile : public WritingStdFile {
103+
__wasi_errno_t write(const uint8_t* buf, size_t len, off_t offset) override {
104+
// Similar issue with Node and worker threads as emscripten_out.
105+
// TODO: May not want to proxy stderr (fd == 2) to the main thread, as
106+
// emscripten_err does.
107+
// This will not show in HTML - a console.warn in a worker is
108+
// sufficient. This would be a change from the current FS.
109+
return writeToJS(buf, len, &_emscripten_err, writeBuffer);
110+
}
111+
112+
public:
113+
StderrFile() {}
114+
};
115+
116+
} // anonymous namespace
117+
118+
std::shared_ptr<DataFile> getStdin() {
119+
static auto stdin = std::make_shared<StdinFile>();
120+
return stdin;
121+
}
122+
123+
std::shared_ptr<DataFile> getStdout() {
124+
static auto stdout = std::make_shared<StdoutFile>();
125+
return stdout;
126+
}
127+
128+
std::shared_ptr<DataFile> getStderr() {
129+
static auto stderr = std::make_shared<StderrFile>();
130+
return stderr;
131+
}
132+
133+
} // namespace wasmfs::SpecialFiles

system/lib/wasmfs/special_files.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2022 The Emscripten Authors. All rights reserved.
2+
// Emscripten is available under two separate licenses, the MIT license and the
3+
// University of Illinois/NCSA Open Source License. Both these licenses can be
4+
// found in the LICENSE file.
5+
6+
#pragma once
7+
8+
#include "file.h"
9+
10+
namespace wasmfs::SpecialFiles {
11+
12+
// /dev/stdin/
13+
std::shared_ptr<DataFile> getStdin();
14+
15+
// /dev/stdout
16+
std::shared_ptr<DataFile> getStdout();
17+
18+
// /dev/stderr
19+
std::shared_ptr<DataFile> getStderr();
20+
21+
} // namespace wasmfs::SpecialFiles

system/lib/wasmfs/streams.cpp

Lines changed: 0 additions & 85 deletions
This file was deleted.

system/lib/wasmfs/streams.h

Lines changed: 0 additions & 91 deletions
This file was deleted.

system/lib/wasmfs/syscalls.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
#include "file_table.h"
2828
#include "paths.h"
2929
#include "pipe_backend.h"
30-
#include "streams.h"
30+
#include "special_files.h"
3131
#include "wasmfs.h"
3232

3333
// File permission macros for wasmfs.
@@ -1150,9 +1150,8 @@ int __syscall_ftruncate64(int fd, uint64_t size) {
11501150
static bool isTTY(std::shared_ptr<File>& file) {
11511151
// TODO: Full TTY support. For now, just see stdin/out/err as terminals and
11521152
// nothing else.
1153-
return file == StdinFile::getSingleton() ||
1154-
file == StdoutFile::getSingleton() ||
1155-
file == StderrFile::getSingleton();
1153+
return file == SpecialFiles::getStdin() ||
1154+
file == SpecialFiles::getStdout() || file == SpecialFiles::getStderr();
11561155
}
11571156

11581157
int __syscall_ioctl(int fd, int request, ...) {

0 commit comments

Comments
 (0)