Skip to content

Commit f6e1a77

Browse files
committed
feat(pluginlab): safely share repl_vars behind Arc<Mutex<T>> between WasiState and PluginHost
PluginHost needs to have access to repl_vars now that there is plugin-api/host-state-plugin#get-repl-var in wit. Use Arc<Mutex<T>> instead of direct references to satisfy Send trait requirements for async WebAssembly operations. While the runtime is single-threaded, Wasmtime's async bindings require Send + Sync types.
1 parent 80f8c5f commit f6e1a77

File tree

4 files changed

+67
-26
lines changed

4 files changed

+67
-26
lines changed

crates/pluginlab/src/engine.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::cli::Cli;
22
use crate::permissions::NetworkPermissions;
33
use anyhow::Result;
4-
use std::collections::HashMap;
4+
55
use std::path::Path;
66
use wasmtime::component::{Component, Linker as ComponentLinker, ResourceTable};
77
use wasmtime::{Config, Engine, Store};
@@ -120,15 +120,19 @@ impl WasmEngine {
120120

121121
/// Create a new store with WASI context
122122
pub fn create_store(&self, wasi_ctx: WasiCtx, cli: &Cli) -> Store<WasiState> {
123+
let repl_vars =
124+
std::sync::Arc::new(std::sync::Mutex::new(std::collections::HashMap::new()));
125+
123126
Store::new(
124127
&self.engine,
125128
WasiState {
126129
ctx: wasi_ctx,
127130
table: ResourceTable::new(),
128131
plugin_host: PluginHost {
129132
network_permissions: NetworkPermissions::from(cli),
133+
repl_vars: repl_vars.clone(),
130134
},
131-
repl_vars: HashMap::new(),
135+
repl_vars,
132136
plugins_names: Vec::new(),
133137
},
134138
)

crates/pluginlab/src/helpers.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,32 @@
11
use std::collections::HashMap;
2+
use std::sync::{Arc, Mutex};
23

34
/// Handles setting exit status codes in REPL variables
45
pub struct StatusHandler;
56

67
impl StatusHandler {
78
/// Set the exit status in the REPL variables
8-
pub fn set_exit_status(repl_vars: &mut HashMap<String, String>, success: bool) {
9+
pub fn set_exit_status(repl_vars: &mut Arc<Mutex<HashMap<String, String>>>, success: bool) {
910
let status = if success { "0" } else { "1" };
10-
repl_vars.insert("?".to_string(), status.to_string());
11+
repl_vars
12+
.lock()
13+
.expect("Failed to acquire repl_vars lock")
14+
.insert("?".to_string(), status.to_string());
1115
}
1216
}
1317

1418
pub struct StdoutHandler;
1519

1620
impl StdoutHandler {
17-
pub fn print_and_set_last_result(repl_vars: &mut HashMap<String, String>, result: String) {
21+
pub fn print_and_set_last_result(
22+
repl_vars: &mut Arc<Mutex<HashMap<String, String>>>,
23+
result: String,
24+
) {
1825
println!("{}", result);
19-
repl_vars.insert("0".to_string(), result);
26+
repl_vars
27+
.lock()
28+
.expect("Failed to acquire repl_vars lock")
29+
.insert("0".to_string(), result);
2030
}
2131
}
2232

crates/pluginlab/src/lib.rs

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,33 @@ pub async fn run_async() -> Result<()> {
5757
eprintln!("[Host][Debug] Loaded plugins config: {:?}", plugins_config);
5858
}
5959

60-
host.store
61-
.data_mut()
62-
.repl_vars
63-
.insert("ROOT".to_string(), "/Users".to_string());
64-
host.store
65-
.data_mut()
66-
.repl_vars
67-
.insert("USER".to_string(), "Tophe".to_string());
68-
host.store
69-
.data_mut()
70-
.repl_vars
71-
.insert("?".to_string(), "0".to_string());
60+
{
61+
let mut repl_vars = host
62+
.store
63+
.data_mut()
64+
.repl_vars
65+
.lock()
66+
.expect("Failed to acquire repl_vars lock");
67+
repl_vars.insert("ROOT".to_string(), "/Users".to_string());
68+
}
69+
{
70+
let mut repl_vars = host
71+
.store
72+
.data_mut()
73+
.repl_vars
74+
.lock()
75+
.expect("Failed to acquire repl_vars lock");
76+
repl_vars.insert("USER".to_string(), "Tophe".to_string());
77+
repl_vars.insert("?".to_string(), "0".to_string());
78+
}
7279
if debug {
7380
eprintln!(
7481
"[Host][Debug] Loaded env vars: {:?}",
75-
host.store.data().repl_vars
82+
host.store
83+
.data()
84+
.repl_vars
85+
.lock()
86+
.expect("Failed to acquire repl_vars lock")
7687
);
7788
}
7889

@@ -82,7 +93,14 @@ pub async fn run_async() -> Result<()> {
8293

8394
loop {
8495
let mut line = String::new();
85-
match host.store.data().repl_vars.get("?") {
96+
match host
97+
.store
98+
.data()
99+
.repl_vars
100+
.lock()
101+
.expect("Failed to acquire repl_vars lock")
102+
.get("?")
103+
{
86104
Some(last_status) => {
87105
print!("repl({})> ", last_status);
88106
}

crates/pluginlab/src/store.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::permissions::NetworkPermissions;
22
use std::collections::HashMap;
3+
use std::sync::{Arc, Mutex};
34
use wasmtime::component::ResourceTable;
45
use wasmtime_wasi::p2::{IoView, WasiCtx, WasiView};
56

@@ -30,7 +31,7 @@ pub struct WasiState {
3031
/// and shared with the guest via the Guest bindings
3132
3233
/// Custom environment variables stored by the REPL
33-
pub repl_vars: HashMap<String, String>,
34+
pub repl_vars: Arc<Mutex<HashMap<String, String>>>,
3435

3536
/// Names of the plugins loaded in the host
3637
pub plugins_names: Vec<String>,
@@ -40,6 +41,8 @@ pub struct WasiState {
4041
pub struct PluginHost {
4142
/// Network permissions
4243
pub network_permissions: NetworkPermissions,
44+
/// Shared reference to repl_vars from WasiState
45+
pub repl_vars: Arc<Mutex<HashMap<String, String>>>,
4346
}
4447

4548
impl crate::api::plugin_api::repl::api::http_client::Host for PluginHost {
@@ -85,10 +88,11 @@ impl crate::api::plugin_api::repl::api::http_client::Host for PluginHost {
8588
///
8689
impl crate::api::plugin_api::repl::api::host_state_plugin::Host for PluginHost {
8790
async fn get_repl_var(&mut self, key: String) -> Option<String> {
88-
None
89-
// Can't do the following because `PluginHost` does not have access to repl_vars
90-
// we need to add a repl_vars field to PluginHost that points to the repl_vars field of WasiState
91-
// self.repl_vars.get(&key).cloned()
91+
self.repl_vars
92+
.lock()
93+
.expect("Failed to acquire repl_vars lock")
94+
.get(&key)
95+
.cloned()
9296
}
9397
}
9498

@@ -140,6 +144,8 @@ impl crate::api::host_api::repl::api::host_state::Host for WasiState {
140144
{
141145
// Return the stored environment variables
142146
self.repl_vars
147+
.lock()
148+
.expect("Failed to acquire repl_vars lock")
143149
.iter()
144150
.map(
145151
|(key, value)| crate::api::host_api::repl::api::transport::ReplVar {
@@ -152,6 +158,9 @@ impl crate::api::host_api::repl::api::host_state::Host for WasiState {
152158

153159
async fn set_repl_var(&mut self, var: crate::api::host_api::repl::api::transport::ReplVar) {
154160
// Set a single environment variable
155-
self.repl_vars.insert(var.key.clone(), var.value.clone());
161+
self.repl_vars
162+
.lock()
163+
.expect("Failed to acquire repl_vars lock")
164+
.insert(var.key.clone(), var.value.clone());
156165
}
157166
}

0 commit comments

Comments
 (0)