Skip to content

Commit 668bc99

Browse files
authored
Delete failed Plugin/VMs via fail callbacks. (#181)
Signed-off-by: Takeshi Yoneda <[email protected]>
1 parent d32cb05 commit 668bc99

File tree

10 files changed

+210
-79
lines changed

10 files changed

+210
-79
lines changed

include/proxy-wasm/wasm_vm.h

+6-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <optional>
2121
#include <string>
2222
#include <unordered_map>
23+
#include <vector>
2324

2425
#include "include/proxy-wasm/word.h"
2526

@@ -297,12 +298,12 @@ class WasmVm {
297298
void fail(FailState fail_state, std::string_view message) {
298299
integration()->error(message);
299300
failed_ = fail_state;
300-
if (fail_callback_) {
301-
fail_callback_(fail_state);
301+
for (auto &callback : fail_callbacks_) {
302+
callback(fail_state);
302303
}
303304
}
304-
void setFailCallback(std::function<void(FailState)> fail_callback) {
305-
fail_callback_ = fail_callback;
305+
void addFailCallback(std::function<void(FailState)> fail_callback) {
306+
fail_callbacks_.push_back(fail_callback);
306307
}
307308

308309
// Integrator operations.
@@ -312,7 +313,7 @@ class WasmVm {
312313
protected:
313314
std::unique_ptr<WasmVmIntegration> integration_;
314315
FailState failed_ = FailState::Ok;
315-
std::function<void(FailState)> fail_callback_;
316+
std::vector<std::function<void(FailState)>> fail_callbacks_;
316317
};
317318

318319
// Thread local state set during a call into a WASM VM so that calls coming out of the

src/wasm.cc

+18-2
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ WasmBase::WasmBase(const std::shared_ptr<WasmHandleBase> &base_wasm_handle, Wasm
208208
if (!wasm_vm_) {
209209
failed_ = FailState::UnableToCreateVm;
210210
} else {
211-
wasm_vm_->setFailCallback([this](FailState fail_state) { failed_ = fail_state; });
211+
wasm_vm_->addFailCallback([this](FailState fail_state) { failed_ = fail_state; });
212212
}
213213
}
214214

@@ -222,7 +222,7 @@ WasmBase::WasmBase(std::unique_ptr<WasmVm> wasm_vm, std::string_view vm_id,
222222
if (!wasm_vm_) {
223223
failed_ = FailState::UnableToCreateVm;
224224
} else {
225-
wasm_vm_->setFailCallback([this](FailState fail_state) { failed_ = fail_state; });
225+
wasm_vm_->addFailCallback([this](FailState fail_state) { failed_ = fail_state; });
226226
}
227227
}
228228

@@ -559,6 +559,14 @@ getOrCreateThreadLocalWasm(std::shared_ptr<WasmHandleBase> base_handle,
559559
return nullptr;
560560
}
561561
local_wasms[vm_key] = wasm_handle;
562+
wasm_handle->wasm()->wasm_vm()->addFailCallback([vm_key](proxy_wasm::FailState fail_state) {
563+
if (fail_state == proxy_wasm::FailState::RuntimeError) {
564+
// If VM failed, erase the entry so that:
565+
// 1) we can recreate the new thread local VM from the same base_wasm.
566+
// 2) we wouldn't reuse the failed VM for new plugins accidentally.
567+
local_wasms.erase(vm_key);
568+
};
569+
});
562570
return wasm_handle;
563571
}
564572

@@ -594,6 +602,14 @@ std::shared_ptr<PluginHandleBase> getOrCreateThreadLocalPlugin(
594602
}
595603
auto plugin_handle = plugin_factory(wasm_handle, plugin);
596604
local_plugins[key] = plugin_handle;
605+
wasm_handle->wasm()->wasm_vm()->addFailCallback([key](proxy_wasm::FailState fail_state) {
606+
if (fail_state == proxy_wasm::FailState::RuntimeError) {
607+
// If VM failed, erase the entry so that:
608+
// 1) we can recreate the new thread local plugin from the same base_wasm.
609+
// 2) we wouldn't reuse the failed VM for new plugin configs accidentally.
610+
local_plugins.erase(key);
611+
};
612+
});
597613
return plugin_handle;
598614
}
599615

test/BUILD

+6-2
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,13 @@ cc_test(
7878
)
7979

8080
cc_test(
81-
name = "context_test",
82-
srcs = ["context_test.cc"],
81+
name = "wasm_test",
82+
srcs = ["wasm_test.cc"],
83+
data = [
84+
"//test/test_data:abi_export.wasm",
85+
],
8386
deps = [
87+
":utility_lib",
8488
"//:lib",
8589
"@com_google_googletest//:gtest",
8690
"@com_google_googletest//:gtest_main",

test/bytecode_util_test.cc

+8-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,14 @@ TEST(TestBytecodeUtil, getFunctionNameIndex) {
5959
// OK.
6060
EXPECT_TRUE(BytecodeUtil::getFunctionNameIndex(source, actual));
6161
EXPECT_FALSE(actual.empty());
62-
EXPECT_EQ(actual.find(0)->second, "proxy_abi_version_0_2_0");
62+
bool abi_version_found = false;
63+
for (auto it : actual) {
64+
if (it.second == "proxy_abi_version_0_2_0") {
65+
abi_version_found = true;
66+
break;
67+
}
68+
}
69+
EXPECT_TRUE(abi_version_found);
6370

6471
// Fail due to the corrupted bytecode.
6572
// TODO(@mathetake): here we haven't covered all the parsing failure branches. Add more cases.

test/context_test.cc

-35
This file was deleted.

test/exports_test.cc

+6-6
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ class TestContext : public ContextBase {
4949

5050
TEST_P(TestVM, Environment) {
5151
std::unordered_map<std::string, std::string> envs = {{"KEY1", "VALUE1"}, {"KEY2", "VALUE2"}};
52-
initialize("env.wasm");
52+
auto source = readTestWasmFile("env.wasm");
5353

5454
auto wasm_base = WasmBase(std::move(vm_), "vm_id", "", "", envs, {});
55-
ASSERT_TRUE(wasm_base.wasm_vm()->load(source_, {}, {}));
55+
ASSERT_TRUE(wasm_base.wasm_vm()->load(source, {}, {}));
5656

5757
TestContext context(&wasm_base);
5858
current_context_ = &context;
@@ -72,9 +72,9 @@ TEST_P(TestVM, Environment) {
7272
}
7373

7474
TEST_P(TestVM, WithoutEnvironment) {
75-
initialize("env.wasm");
75+
auto source = readTestWasmFile("env.wasm");
7676
auto wasm_base = WasmBase(std::move(vm_), "vm_id", "", "", {}, {});
77-
ASSERT_TRUE(wasm_base.wasm_vm()->load(source_, {}, {}));
77+
ASSERT_TRUE(wasm_base.wasm_vm()->load(source, {}, {}));
7878

7979
TestContext context(&wasm_base);
8080
current_context_ = &context;
@@ -92,9 +92,9 @@ TEST_P(TestVM, WithoutEnvironment) {
9292
}
9393

9494
TEST_P(TestVM, Clock) {
95-
initialize("clock.wasm");
95+
auto source = readTestWasmFile("clock.wasm");
9696
auto wasm_base = WasmBase(std::move(vm_), "vm_id", "", "", {}, {});
97-
ASSERT_TRUE(wasm_base.wasm_vm()->load(source_, {}, {}));
97+
ASSERT_TRUE(wasm_base.wasm_vm()->load(source, {}, {}));
9898

9999
TestContext context(&wasm_base);
100100
current_context_ = &context;

test/runtime_test.cc

+21-17
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ TEST_P(TestVM, Basic) {
4747
}
4848

4949
TEST_P(TestVM, Memory) {
50-
initialize("abi_export.wasm");
51-
ASSERT_TRUE(vm_->load(source_, {}, {}));
50+
auto source = readTestWasmFile("abi_export.wasm");
51+
ASSERT_TRUE(vm_->load(source, {}, {}));
5252
ASSERT_TRUE(vm_->link(""));
5353

5454
Word word;
@@ -68,8 +68,8 @@ TEST_P(TestVM, Clone) {
6868
if (vm_->cloneable() == proxy_wasm::Cloneable::NotCloneable) {
6969
return;
7070
}
71-
initialize("abi_export.wasm");
72-
ASSERT_TRUE(vm_->load(source_, {}, {}));
71+
auto source = readTestWasmFile("abi_export.wasm");
72+
ASSERT_TRUE(vm_->load(source, {}, {}));
7373
ASSERT_TRUE(vm_->link(""));
7474
const auto address = 0x2000;
7575
Word word;
@@ -113,8 +113,10 @@ TEST_P(TestVM, StraceLogLevel) {
113113
// See https://github.com/proxy-wasm/proxy-wasm-cpp-host/issues/120.
114114
return;
115115
}
116-
initialize("callback.wasm");
117-
ASSERT_TRUE(vm_->load(source_, {}, {}));
116+
117+
auto integration = static_cast<DummyIntegration *>(vm_->integration().get());
118+
auto source = readTestWasmFile("callback.wasm");
119+
ASSERT_TRUE(vm_->load(source, {}, {}));
118120
vm_->registerCallback("env", "callback", &nopCallback,
119121
&ConvertFunctionWordToUint32<decltype(nopCallback),
120122
nopCallback>::convertFunctionWordToUint32);
@@ -128,16 +130,16 @@ TEST_P(TestVM, StraceLogLevel) {
128130

129131
run(nullptr);
130132
// no trace message found since DummyIntegration's log_level_ defaults to LogLevel::info
131-
EXPECT_EQ(integration_->trace_message_, "");
133+
EXPECT_EQ(integration->trace_message_, "");
132134

133-
integration_->log_level_ = LogLevel::trace;
135+
integration->log_level_ = LogLevel::trace;
134136
run(nullptr);
135-
EXPECT_NE(integration_->trace_message_, "");
137+
EXPECT_NE(integration->trace_message_, "");
136138
}
137139

138140
TEST_P(TestVM, Callback) {
139-
initialize("callback.wasm");
140-
ASSERT_TRUE(vm_->load(source_, {}, {}));
141+
auto source = readTestWasmFile("callback.wasm");
142+
ASSERT_TRUE(vm_->load(source, {}, {}));
141143

142144
TestContext context;
143145

@@ -166,16 +168,17 @@ TEST_P(TestVM, Callback) {
166168
}
167169

168170
TEST_P(TestVM, Trap) {
169-
initialize("trap.wasm");
170-
ASSERT_TRUE(vm_->load(source_, {}, {}));
171+
auto source = readTestWasmFile("trap.wasm");
172+
ASSERT_TRUE(vm_->load(source, {}, {}));
171173
ASSERT_TRUE(vm_->link(""));
172174
TestContext context;
173175
WasmCallVoid<0> trigger;
174176
vm_->getFunction("trigger", &trigger);
175177
EXPECT_TRUE(trigger != nullptr);
176178
trigger(&context);
177179
std::string exp_message = "Function: trigger failed";
178-
ASSERT_TRUE(integration_->error_message_.find(exp_message) != std::string::npos);
180+
auto integration = static_cast<DummyIntegration *>(vm_->integration().get());
181+
ASSERT_TRUE(integration->error_message_.find(exp_message) != std::string::npos);
179182
}
180183

181184
TEST_P(TestVM, Trap2) {
@@ -185,16 +188,17 @@ TEST_P(TestVM, Trap2) {
185188
// WAVM::Runtime::describeCallStack. Needs further investigation.
186189
return;
187190
}
188-
initialize("trap.wasm");
189-
ASSERT_TRUE(vm_->load(source_, {}, {}));
191+
auto source = readTestWasmFile("trap.wasm");
192+
ASSERT_TRUE(vm_->load(source, {}, {}));
190193
ASSERT_TRUE(vm_->link(""));
191194
TestContext context;
192195
WasmCallWord<1> trigger2;
193196
vm_->getFunction("trigger2", &trigger2);
194197
EXPECT_TRUE(trigger2 != nullptr);
195198
trigger2(&context, 0);
196199
std::string exp_message = "Function: trigger2 failed";
197-
ASSERT_TRUE(integration_->error_message_.find(exp_message) != std::string::npos);
200+
auto integration = static_cast<DummyIntegration *>(vm_->integration().get());
201+
ASSERT_TRUE(integration->error_message_.find(exp_message) != std::string::npos);
198202
}
199203

200204
} // namespace

test/test_data/abi_export.rs

+18
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,21 @@
1414

1515
#[no_mangle]
1616
pub extern "C" fn proxy_abi_version_0_2_0() {}
17+
18+
#[no_mangle]
19+
pub extern "C" fn proxy_on_vm_start(_: u32, _: usize) -> bool {
20+
true
21+
}
22+
23+
#[no_mangle]
24+
pub extern "C" fn proxy_on_context_create(_: u32, _: u32) {}
25+
26+
#[no_mangle]
27+
pub extern "C" fn proxy_on_memory_allocate(size: usize) -> *mut u8 {
28+
let mut vec: Vec<u8> = Vec::with_capacity(size);
29+
unsafe {
30+
vec.set_len(size);
31+
}
32+
let slice = vec.into_boxed_slice();
33+
Box::into_raw(slice) as *mut u8
34+
}

test/utility.h

+13-11
Original file line numberDiff line numberDiff line change
@@ -67,37 +67,39 @@ class TestVM : public testing::TestWithParam<std::string> {
6767
public:
6868
std::unique_ptr<proxy_wasm::WasmVm> vm_;
6969

70-
TestVM() : integration_(new DummyIntegration{}) {
70+
TestVM() {
7171
runtime_ = GetParam();
72+
vm_ = newVm();
73+
}
74+
75+
std::unique_ptr<proxy_wasm::WasmVm> newVm() {
76+
std::unique_ptr<proxy_wasm::WasmVm> vm;
7277
if (runtime_ == "") {
7378
EXPECT_TRUE(false) << "runtime must not be empty";
7479
#if defined(PROXY_WASM_HAS_RUNTIME_V8)
7580
} else if (runtime_ == "v8") {
76-
vm_ = proxy_wasm::createV8Vm();
81+
vm = proxy_wasm::createV8Vm();
7782
#endif
7883
#if defined(PROXY_WASM_HAS_RUNTIME_WAVM)
7984
} else if (runtime_ == "wavm") {
80-
vm_ = proxy_wasm::createWavmVm();
85+
vm = proxy_wasm::createWavmVm();
8186
#endif
8287
#if defined(PROXY_WASM_HAS_RUNTIME_WASMTIME)
8388
} else if (runtime_ == "wasmtime") {
84-
vm_ = proxy_wasm::createWasmtimeVm();
89+
vm = proxy_wasm::createWasmtimeVm();
8590
#endif
8691
#if defined(PROXY_WASM_HAS_RUNTIME_WAMR)
8792
} else if (runtime_ == "wamr") {
88-
vm_ = proxy_wasm::createWamrVm();
93+
vm = proxy_wasm::createWamrVm();
8994
#endif
9095
} else {
9196
EXPECT_TRUE(false) << "compiled without support for the requested \"" << runtime_
9297
<< "\" runtime";
9398
}
94-
vm_->integration().reset(integration_);
95-
}
96-
97-
void initialize(std::string filename) { source_ = readTestWasmFile(filename); }
99+
vm->integration().reset(new DummyIntegration{});
100+
return vm;
101+
};
98102

99-
DummyIntegration *integration_;
100-
std::string source_;
101103
std::string runtime_;
102104
};
103105
} // namespace proxy_wasm

0 commit comments

Comments
 (0)