Skip to content

Commit 5236dab

Browse files
authored
feat: concurrency support (#41)
1 parent 540ec17 commit 5236dab

File tree

3 files changed

+133
-94
lines changed

3 files changed

+133
-94
lines changed

ecsact/wasm/detail/minst/minst.hh

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,6 @@ public:
8686
import_resolver_t import_resolver
8787
) -> std::variant<minst, minst_error>;
8888

89-
/**
90-
* Create a new minst with internal memory copied over. If 'initialize' was
91-
* already called then the memory should be identical, thus 'initialize'
92-
* should not be called again.
93-
*/
94-
static auto copy(minst& other) -> minst;
95-
9689
minst(minst&& other);
9790
~minst();
9891

ecsact/wasm/detail/wasm.cc

Lines changed: 132 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <iostream>
2020
#include <array>
2121
#include <cstddef>
22+
#include <thread>
2223
#include "ecsact/runtime/dynamic.h"
2324
#include "ecsact/wasm/detail/minst/minst.hh"
2425
#include "ecsact/wasm/detail/logger.hh"
@@ -70,21 +71,67 @@ struct minst_ecsact_system_impls {
7071

7172
auto trap_handler = ecsactsi_wasm_trap_handler{};
7273

73-
thread_local auto thread_minst = std::optional<minst_ecsact_system_impls>{};
74+
auto all_minsts = std::vector<minst_ecsact_system_impls>{};
75+
auto next_available_minst_index = std::atomic_size_t{};
76+
thread_local auto thread_minst =
77+
std::optional<std::reference_wrapper<minst_ecsact_system_impls>>{};
78+
79+
auto ensure_minst() -> minst_ecsact_system_impls& {
80+
if(!thread_minst) {
81+
auto index = ++next_available_minst_index % all_minsts.size();
82+
thread_minst = all_minsts[index];
83+
}
84+
85+
return thread_minst->get();
86+
}
7487

7588
void ecsact_si_wasm_system_impl(ecsact_system_execution_context* ctx) {
76-
auto system_id = ecsact_system_execution_context_id(ctx);
77-
auto itr = thread_minst->sys_impl_exports.find(system_id);
78-
assert(itr != thread_minst->sys_impl_exports.end());
89+
auto& minst = ensure_minst();
90+
auto system_id = ecsact_system_execution_context_id(ctx);
91+
auto itr = minst.sys_impl_exports.find(system_id);
92+
assert(itr != minst.sys_impl_exports.end());
7993

8094
auto mem_data = std::array<std::byte, 4096>{};
8195
set_call_mem_data(mem_data.data(), mem_data.size());
8296
defer {
8397
set_call_mem_data(nullptr, 0);
8498
};
85-
call_mem_alloc(thread_minst->memory.memory);
99+
call_mem_alloc(minst.memory.memory);
86100
itr->second.func_call(call_mem_alloc(ctx));
87101
}
102+
103+
auto get_system_impl_exports(
104+
minst& inst,
105+
int systems_count,
106+
ecsact_system_like_id* system_ids,
107+
const char** wasm_exports,
108+
std::unordered_map<ecsact_system_like_id, minst_export>& system_impl_exports
109+
) -> ecsactsi_wasm_error {
110+
system_impl_exports.clear();
111+
system_impl_exports.reserve(systems_count);
112+
113+
for(auto i = 0; systems_count > i; ++i) {
114+
auto sys_id = system_ids[i];
115+
auto system_impl_export_name = std::string_view{
116+
wasm_exports[i],
117+
std::strlen(wasm_exports[i]),
118+
};
119+
120+
auto exp = inst.find_export(system_impl_export_name);
121+
122+
if(!exp) {
123+
return ECSACTSI_WASM_ERR_EXPORT_NOT_FOUND;
124+
}
125+
126+
if(exp->kind() != WASM_EXTERN_FUNC) {
127+
return ECSACTSI_WASM_ERR_EXPORT_INVALID;
128+
}
129+
130+
system_impl_exports[sys_id] = *exp;
131+
}
132+
133+
return ECSACTSI_WASM_OK;
134+
}
88135
} // namespace
89136

90137
void ecsactsi_wasm_last_error_message(
@@ -126,108 +173,106 @@ ecsactsi_wasm_error ecsactsi_wasm_load(
126173
return ECSACTSI_WASM_ERR_NO_SET_SYSTEM_EXECUTION;
127174
}
128175
#endif
129-
130-
auto result = ecsact::wasm::detail::minst::create(
131-
engine(),
132-
std::span{
133-
reinterpret_cast<std::byte*>(wasm_data),
134-
static_cast<size_t>(wasm_data_size),
135-
},
136-
[&](const minst_import imp) -> minst_import_resolve_t {
137-
auto module_name = imp.module();
138-
auto method_name = imp.name();
139-
140-
if(imp.module() == "env") {
141-
auto itr = guest_env_module_imports.find(method_name);
142-
if(itr == guest_env_module_imports.end()) {
143-
return std::nullopt;
144-
}
145-
return itr->second();
176+
auto import_resolver = [&](const minst_import imp) -> minst_import_resolve_t {
177+
auto module_name = imp.module();
178+
auto method_name = imp.name();
179+
180+
if(imp.module() == "env") {
181+
auto itr = guest_env_module_imports.find(method_name);
182+
if(itr == guest_env_module_imports.end()) {
183+
return std::nullopt;
146184
}
185+
return itr->second();
186+
}
147187

148-
if(imp.module() == "wasi_snapshot_preview1") {
149-
auto itr = guest_wasi_module_imports.find(method_name);
150-
if(itr == guest_wasi_module_imports.end()) {
151-
return std::nullopt;
152-
}
153-
return itr->second();
188+
if(imp.module() == "wasi_snapshot_preview1") {
189+
auto itr = guest_wasi_module_imports.find(method_name);
190+
if(itr == guest_wasi_module_imports.end()) {
191+
return std::nullopt;
154192
}
155-
156-
return std::nullopt;
193+
return itr->second();
157194
}
158-
);
159195

160-
if(std::holds_alternative<minst_error>(result)) {
161-
auto err = std::get<minst_error>(result);
162-
switch(err.code) {
163-
case minst_error_code::ok:
164-
assert(err.code != minst_error_code::ok);
165-
case minst_error_code::compile_fail:
166-
return ECSACTSI_WASM_ERR_COMPILE_FAIL;
167-
case minst_error_code::unresolved_guest_import:
168-
return ECSACTSI_WASM_ERR_GUEST_IMPORT_INVALID;
169-
case minst_error_code::instantiate_fail:
170-
return ECSACTSI_WASM_ERR_INSTANTIATE_FAIL;
171-
}
172-
}
196+
return std::nullopt;
197+
};
173198

174-
auto& inst = std::get<minst>(result);
175-
auto system_impl_exports =
176-
std::unordered_map<ecsact_system_like_id, minst_export>{};
199+
all_minsts.reserve(100);
177200

178-
system_impl_exports.reserve(systems_count);
201+
for(auto i = 0; 100 > i; ++i) {
202+
auto result = minst::create(
203+
engine(),
204+
std::span{
205+
reinterpret_cast<std::byte*>(wasm_data),
206+
static_cast<size_t>(wasm_data_size),
207+
},
208+
import_resolver
209+
);
179210

180-
for(auto i = 0; systems_count > i; ++i) {
181-
auto sys_id = system_ids[i];
182-
auto system_impl_export_name = std::string_view{
183-
wasm_exports[i],
184-
std::strlen(wasm_exports[i]),
185-
};
211+
if(std::holds_alternative<minst_error>(result)) {
212+
auto err = std::get<minst_error>(result);
213+
switch(err.code) {
214+
case minst_error_code::ok:
215+
assert(err.code != minst_error_code::ok);
216+
case minst_error_code::compile_fail:
217+
return ECSACTSI_WASM_ERR_COMPILE_FAIL;
218+
case minst_error_code::unresolved_guest_import:
219+
return ECSACTSI_WASM_ERR_GUEST_IMPORT_INVALID;
220+
case minst_error_code::instantiate_fail:
221+
return ECSACTSI_WASM_ERR_INSTANTIATE_FAIL;
222+
}
223+
}
186224

187-
auto exp = inst.find_export(system_impl_export_name);
225+
auto& inst = std::get<minst>(result);
226+
auto system_impl_exports =
227+
std::unordered_map<ecsact_system_like_id, minst_export>{};
188228

189-
if(!exp) {
190-
return ECSACTSI_WASM_ERR_EXPORT_NOT_FOUND;
191-
}
229+
auto err = get_system_impl_exports(
230+
inst,
231+
systems_count,
232+
system_ids,
233+
wasm_exports,
234+
system_impl_exports
235+
);
192236

193-
if(exp->kind() != WASM_EXTERN_FUNC) {
194-
return ECSACTSI_WASM_ERR_EXPORT_INVALID;
237+
if(err != ECSACTSI_WASM_OK) {
238+
return err;
195239
}
196240

197-
system_impl_exports[sys_id] = *exp;
198-
}
199-
200-
auto wasm_mem = std::optional<minst_export>{};
241+
auto wasm_mem = std::optional<minst_export>{};
201242

202-
for(auto exp : inst.exports()) {
203-
if(exp.kind() == WASM_EXTERN_MEMORY) {
204-
wasm_mem = exp;
205-
break;
243+
for(auto exp : inst.exports()) {
244+
if(exp.kind() == WASM_EXTERN_MEMORY) {
245+
wasm_mem = exp;
246+
break;
247+
}
206248
}
207-
}
208249

209-
assert(wasm_mem);
250+
assert(wasm_mem);
210251

211-
auto mem_data = std::array<std::byte, 4096>{};
212-
set_call_mem_data(mem_data.data(), mem_data.size());
213-
call_mem_alloc<wasm_memory_t*>(wasm_mem->memory);
214-
defer {
215-
set_call_mem_data(nullptr, 0);
216-
};
217-
auto init_trap = inst.initialize();
218-
if(init_trap) {
219-
return ECSACTSI_WASM_ERR_INITIALIZE_FAIL;
220-
}
252+
auto mem_data = std::array<std::byte, 4096>{};
253+
set_call_mem_data(mem_data.data(), mem_data.size());
254+
call_mem_alloc<wasm_memory_t*>(wasm_mem->memory);
255+
defer {
256+
set_call_mem_data(nullptr, 0);
257+
};
258+
auto init_trap = inst.initialize();
259+
if(init_trap) {
260+
return ECSACTSI_WASM_ERR_INITIALIZE_FAIL;
261+
}
221262

222-
for(auto&& [sys_id, exp] : system_impl_exports) {
223-
ecsact_set_system_execution_impl(sys_id, &ecsact_si_wasm_system_impl);
263+
all_minsts.emplace_back( //
264+
std::move(inst),
265+
system_impl_exports,
266+
*wasm_mem
267+
);
224268
}
225269

226-
thread_minst.emplace( //
227-
std::move(inst),
228-
system_impl_exports,
229-
*wasm_mem
230-
);
270+
for(auto i = 0; systems_count > i; ++i) {
271+
ecsact_set_system_execution_impl(
272+
system_ids[i],
273+
&ecsact_si_wasm_system_impl
274+
);
275+
}
231276

232277
return ECSACTSI_WASM_OK;
233278
}

test/MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ bazel_dep(name = "ecsact_cli", version = "0.3.4")
1717
bazel_dep(name = "rules_ecsact", version = "0.5.1")
1818
bazel_dep(name = "xxhash", version = "0.8.2")
1919
bazel_dep(name = "ecsact_si_wasm")
20+
2021
local_path_override(
2122
module_name = "ecsact_si_wasm",
2223
path = "..",

0 commit comments

Comments
 (0)