use crate::error::Result;
use crate::runtime::protocol::Envelope;
use crate::datatype::{Rank, Tag};
#[derive(Debug, Clone, Copy)]
pub struct BrowserEnv {
pub rank: Rank,
pub size: Rank,
}
#[cfg(target_arch = "wasm32")]
mod imp {
use js_sys::{Function, Reflect};
use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;
use super::{BrowserEnv, Envelope, Rank, Result, Tag};
use crate::error::Error;
pub fn detect_environment() -> Result<BrowserEnv> {
console_error_panic_hook::set_once();
let global = js_sys::global();
let rank = Reflect::get(&global, &JsValue::from_str("__jsmpi_rank"))
.ok()
.and_then(|value| value.as_f64())
.map(|value| value as Rank)
.unwrap_or(0);
let size = Reflect::get(&global, &JsValue::from_str("__jsmpi_size"))
.ok()
.and_then(|value| value.as_f64())
.map(|value| value as Rank)
.unwrap_or(1);
Ok(BrowserEnv { rank, size })
}
pub fn post_envelope(envelope: &Envelope) -> Result<()> {
let global = js_sys::global();
let maybe_fn = Reflect::get(&global, &JsValue::from_str("__jsmpi_send"))
.map_err(|_| Error::RuntimeNotConfigured)?;
let send_fn = maybe_fn
.dyn_into::<Function>()
.map_err(|_| Error::RuntimeNotConfigured)?;
let js_envelope = serde_wasm_bindgen::to_value(envelope)
.map_err(|err| Error::Serialization(err.to_string()))?;
send_fn
.call1(&global, &js_envelope)
.map_err(|err| Error::Protocol(format!("send bridge failed: {err:?}")))?;
Ok(())
}
pub fn recv_envelope(source: Option<Rank>, tag: Option<Tag>) -> Result<Option<Envelope>> {
let global = js_sys::global();
let maybe_fn = Reflect::get(&global, &JsValue::from_str("__jsmpi_recv"))
.unwrap_or(JsValue::UNDEFINED);
if maybe_fn.is_undefined() || maybe_fn.is_null() {
return Ok(None);
}
let recv_fn = maybe_fn
.dyn_into::<Function>()
.map_err(|_| Error::RuntimeNotConfigured)?;
let js_value = recv_fn
.call2(
&global,
&source
.map(|value| JsValue::from_f64(value as f64))
.unwrap_or(JsValue::NULL),
&tag
.map(|value| JsValue::from_f64(value as f64))
.unwrap_or(JsValue::NULL),
)
.map_err(|err| Error::Protocol(format!("receive bridge failed: {err:?}")))?;
if js_value.is_null() || js_value.is_undefined() {
return Ok(None);
}
serde_wasm_bindgen::from_value(js_value)
.map(Some)
.map_err(|err| Error::Serialization(err.to_string()))
}
pub fn recv_envelope_blocking(source: Option<Rank>, tag: Option<Tag>) -> Result<Option<Envelope>> {
let global = js_sys::global();
let maybe_fn = Reflect::get(&global, &JsValue::from_str("__jsmpi_recv_blocking"))
.unwrap_or(JsValue::UNDEFINED);
if maybe_fn.is_undefined() || maybe_fn.is_null() {
return recv_envelope(source, tag);
}
let recv_fn = maybe_fn
.dyn_into::<Function>()
.map_err(|_| Error::RuntimeNotConfigured)?;
let js_value = recv_fn
.call2(
&global,
&source
.map(|value| JsValue::from_f64(value as f64))
.unwrap_or(JsValue::NULL),
&tag
.map(|value| JsValue::from_f64(value as f64))
.unwrap_or(JsValue::NULL),
)
.map_err(|err| Error::Protocol(format!("receive bridge failed: {err:?}")))?;
if js_value.is_null() || js_value.is_undefined() {
return Ok(None);
}
serde_wasm_bindgen::from_value(js_value)
.map(Some)
.map_err(|err| Error::Serialization(err.to_string()))
}
pub fn barrier(rank: Rank, size: Rank, timeout_ms: Option<u32>) -> Result<bool> {
let global = js_sys::global();
let maybe_fn = Reflect::get(&global, &JsValue::from_str("__jsmpi_barrier_timeout"))
.unwrap_or(JsValue::UNDEFINED);
if maybe_fn.is_undefined() || maybe_fn.is_null() {
return Ok(true);
}
let barrier_fn = maybe_fn
.dyn_into::<Function>()
.map_err(|_| Error::RuntimeNotConfigured)?;
let result = barrier_fn
.call3(
&global,
&JsValue::from_f64(rank as f64),
&JsValue::from_f64(size as f64),
&timeout_ms
.map(|v| JsValue::from_f64(v as f64))
.unwrap_or(JsValue::NULL),
)
.map_err(|err| Error::Protocol(format!("barrier bridge failed: {err:?}")))?;
Ok(result.as_bool().unwrap_or(true))
}
pub fn barrier_begin(rank: Rank, size: Rank) -> Result<i32> {
let global = js_sys::global();
let maybe_fn = Reflect::get(&global, &JsValue::from_str("__jsmpi_barrier_begin"))
.unwrap_or(JsValue::UNDEFINED);
if maybe_fn.is_undefined() || maybe_fn.is_null() {
return Ok(0);
}
let barrier_fn = maybe_fn
.dyn_into::<Function>()
.map_err(|_| Error::RuntimeNotConfigured)?;
let result = barrier_fn
.call2(
&global,
&JsValue::from_f64(rank as f64),
&JsValue::from_f64(size as f64),
)
.map_err(|err| Error::Protocol(format!("barrier begin bridge failed: {err:?}")))?;
Ok(result.as_f64().unwrap_or(0.0) as i32)
}
pub fn barrier_ready(version: i32) -> Result<bool> {
let global = js_sys::global();
let maybe_fn = Reflect::get(&global, &JsValue::from_str("__jsmpi_barrier_ready"))
.unwrap_or(JsValue::UNDEFINED);
if maybe_fn.is_undefined() || maybe_fn.is_null() {
return Ok(true);
}
let barrier_fn = maybe_fn
.dyn_into::<Function>()
.map_err(|_| Error::RuntimeNotConfigured)?;
let result = barrier_fn
.call1(&global, &JsValue::from_f64(version as f64))
.map_err(|err| Error::Protocol(format!("barrier ready bridge failed: {err:?}")))?;
Ok(result.as_bool().unwrap_or(true))
}
pub fn mark_finished() -> Result<()> {
let global = js_sys::global();
let maybe_fn = Reflect::get(&global, &JsValue::from_str("__jsmpi_mark_finished"))
.unwrap_or(JsValue::UNDEFINED);
if maybe_fn.is_undefined() || maybe_fn.is_null() {
return Ok(());
}
let finished_fn = maybe_fn
.dyn_into::<Function>()
.map_err(|_| Error::RuntimeNotConfigured)?;
finished_fn
.call0(&global)
.map_err(|err| Error::Protocol(format!("finished bridge failed: {err:?}")))?;
Ok(())
}
}
#[cfg(not(target_arch = "wasm32"))]
mod imp {
use super::{BrowserEnv, Envelope, Rank, Result, Tag};
pub fn detect_environment() -> Result<BrowserEnv> {
Ok(BrowserEnv { rank: 0, size: 1 })
}
pub fn post_envelope(_envelope: &Envelope) -> Result<()> {
Ok(())
}
pub fn recv_envelope(_source: Option<Rank>, _tag: Option<Tag>) -> Result<Option<Envelope>> {
Ok(None)
}
pub fn recv_envelope_blocking(
_source: Option<Rank>,
_tag: Option<Tag>,
) -> Result<Option<Envelope>> {
Ok(None)
}
pub fn barrier(_rank: Rank, _size: Rank, _timeout_ms: Option<u32>) -> Result<bool> {
Ok(true)
}
pub fn barrier_begin(_rank: Rank, _size: Rank) -> Result<i32> {
Ok(0)
}
pub fn barrier_ready(_version: i32) -> Result<bool> {
Ok(true)
}
pub fn mark_finished() -> Result<()> {
Ok(())
}
}
pub use imp::{
barrier,
barrier_begin,
barrier_ready,
detect_environment,
mark_finished,
post_envelope,
recv_envelope,
recv_envelope_blocking,
};