Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .config/dictionaries/project.dic
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ fontawesome
frontends
fstat
fstatat
FULLMUTEX
futimens
genhtml
getfl
Expand Down
27 changes: 24 additions & 3 deletions hermes/bin/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Hermes app implementation.

use std::{collections::HashMap, sync::Arc};
use std::{
collections::HashMap,
sync::{Arc, Once},
};

use crate::{
event::HermesEventPayload,
Expand All @@ -10,6 +13,19 @@ use crate::{
wasm::module::{Module, ModuleId},
};

/// Controls [`is_parallel_event_execution`] value.
static NO_PARALLEL_EVENT_EXECUTION: Once = Once::new();

/// Disables parallel event execution.
pub(crate) fn set_no_parallel_event_execution() {
NO_PARALLEL_EVENT_EXECUTION.call_once(|| ());
}

/// Returns whether events are executed in parallel.
pub(crate) fn is_parallel_event_execution() -> bool {
!NO_PARALLEL_EVENT_EXECUTION.is_completed()
}

/// Hermes App Name type
#[cfg_attr(debug_assertions, derive(Debug))]
#[derive(Clone, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -137,9 +153,14 @@ pub(crate) fn module_dispatch_event(
vfs: Arc<Vfs>,
event: Arc<dyn HermesEventPayload>,
) {
pool::execute(move || {
let f = move || {
if let Err(err) = module.execute_event(event.as_ref(), vfs) {
tracing::error!("module event execution failed: {err}");
}
});
};
if is_parallel_event_execution() {
pool::execute(f);
} else {
f();
}
}
18 changes: 17 additions & 1 deletion hermes/bin/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod run;
use std::{path::PathBuf, process::ExitCode};

use build_info::BUILD_INFO;
use clap::{Parser, Subcommand};
use clap::{Args, Parser, Subcommand};
use console::{Emoji, style};
use tracing::error;

Expand Down Expand Up @@ -127,3 +127,19 @@ impl Cli {
exit_code
}
}

/// Additional Hermes run arguments
#[derive(Args, Debug)]
pub(crate) struct RuntimeConfig {
/// Shutdown Hermes after the timeout (milliseconds)
#[arg(long)]
timeout_ms: Option<u64>,

/// Disables parallel execution of event handlers
#[arg(long, default_value_t = false)]
no_parallel: bool,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a more explicit name: no_parallel_event_execution.
I think in the future we may need another similar option and it is usually hard to rename the already existing, publicly available option.


/// Serializes `SQLite` database access
#[arg(long, default_value_t = false)]
serialize_sqlite: bool,
}
33 changes: 24 additions & 9 deletions hermes/bin/src/cli/playground.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ use console::Emoji;
use temp_dir::TempDir;

use crate::{
app::{Application, ApplicationName},
app::{Application, ApplicationName, set_no_parallel_event_execution},
cli::RuntimeConfig,
event::queue::Exit,
ipfs, pool, reactor,
runtime_extensions::init::trait_app::{RteApp, RteInitApp},
runtime_extensions::{
hermes::sqlite,
init::trait_app::{RteApp, RteInitApp},
},
vfs::VfsBootstrapper,
wasm::module::Module,
};
Expand All @@ -42,9 +46,9 @@ pub struct Playground {
#[arg(long, default_value = "playground-app")]
app_name: String,

/// Shutdown the playground after the timeout (milliseconds)
#[arg(long)]
timeout_ms: Option<u64>,
/// See [`RuntimeConfig`] docs
#[clap(flatten)]
rt_config: RuntimeConfig,
}

impl Playground {
Expand All @@ -67,19 +71,30 @@ impl Playground {
tracing::info!("{} Bootstrapping IPFS node", console::Emoji::new("🖧", ""),);
init_ipfs(&temp_dir)?;

pool::init()?;
if self.rt_config.serialize_sqlite {
sqlite::set_serialized();
}

if self.rt_config.no_parallel {
set_no_parallel_event_execution();
} else {
pool::init()?;
}

println!("{} Loading an application...", Emoji::new("🛠️", ""),);

reactor::load_app(app)?;

let exit = if let Some(timeout_ms) = self.timeout_ms {
let exit = if let Some(timeout_ms) = self.rt_config.timeout_ms {
exit_lock.wait_timeout(Duration::from_millis(timeout_ms))
} else {
exit_lock.wait()
};

// Wait for scheduled tasks to be finished.
pool::terminate();
if !self.rt_config.no_parallel {
// Wait for scheduled tasks to be finished.
pool::terminate();
}
reactor::drop_all_apps()?;
Ok(exit)
}
Expand Down
29 changes: 21 additions & 8 deletions hermes/bin/src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ use clap::Args;
use console::Emoji;

use crate::{
cli::Cli,
app::set_no_parallel_event_execution,
cli::{Cli, RuntimeConfig},
event::queue::Exit,
ipfs,
packaging::{
app::{ApplicationPackage, build_app},
sign::certificate::{self, Certificate},
},
pool, reactor,
runtime_extensions::hermes::sqlite,
};

/// Run cli command
Expand All @@ -30,9 +32,9 @@ pub(crate) struct Run {
#[clap(long, action = clap::ArgAction::SetTrue)]
untrusted: bool,

/// Shutdown an application after the timeout (milliseconds)
#[arg(long)]
timeout_ms: Option<u64>,
/// See [`RuntimeConfig`] docs
#[clap(flatten)]
rt_config: RuntimeConfig,
}

impl Run {
Expand All @@ -57,7 +59,16 @@ impl Run {
ipfs::bootstrap(hermes_home_dir.as_path(), default_bootstrap)?;
let app = build_app(&package, hermes_home_dir)?;

pool::init()?;
if self.rt_config.serialize_sqlite {
sqlite::set_serialized();
}

if self.rt_config.no_parallel {
set_no_parallel_event_execution();
} else {
pool::init()?;
}

println!(
"{} Loading application {}...",
Emoji::new("🛠️", ""),
Expand All @@ -67,14 +78,16 @@ impl Run {
// TODO[RC]: Currently, when a module fails to initialize, the whole app fails to run.
reactor::load_app(app)?;

let exit = if let Some(timeout_ms) = self.timeout_ms {
let exit = if let Some(timeout_ms) = self.rt_config.timeout_ms {
exit_lock.wait_timeout(Duration::from_millis(timeout_ms))
} else {
exit_lock.wait()
};

// Wait for scheduled tasks to be finished.
pool::terminate();
if !self.rt_config.no_parallel {
// Wait for scheduled tasks to be finished.
pool::terminate();
}
Ok(exit)
}
}
16 changes: 11 additions & 5 deletions hermes/bin/src/runtime_extensions/hermes/sqlite/kernel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ use std::{
};

use libsqlite3_sys::{
SQLITE_OK, SQLITE_OPEN_CREATE, SQLITE_OPEN_NOMUTEX, SQLITE_OPEN_READONLY,
SQLITE_OPEN_READWRITE, sqlite3, sqlite3_busy_handler, sqlite3_db_filename, sqlite3_db_name,
sqlite3_exec, sqlite3_filename_database, sqlite3_filename_journal, sqlite3_filename_wal,
sqlite3_open_v2, sqlite3_soft_heap_limit64, sqlite3_wal_autocheckpoint,
SQLITE_OK, SQLITE_OPEN_CREATE, SQLITE_OPEN_FULLMUTEX, SQLITE_OPEN_NOMUTEX,
SQLITE_OPEN_READONLY, SQLITE_OPEN_READWRITE, sqlite3, sqlite3_busy_handler,
sqlite3_db_filename, sqlite3_db_name, sqlite3_exec, sqlite3_filename_database,
sqlite3_filename_journal, sqlite3_filename_wal, sqlite3_open_v2, sqlite3_soft_heap_limit64,
sqlite3_wal_autocheckpoint,
};
use rand::random;

Expand All @@ -19,6 +20,7 @@ use crate::{
runtime_extensions::{
app_config::{get_app_in_memory_sqlite_db_cfg, get_app_persistent_sqlite_db_cfg},
bindings::hermes::sqlite::api::Errno,
hermes::sqlite::is_serialized,
},
};

Expand Down Expand Up @@ -145,7 +147,11 @@ pub(super) fn open(
SQLITE_OPEN_READONLY
} else {
SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE
} | SQLITE_OPEN_NOMUTEX;
} | if is_serialized() {
SQLITE_OPEN_FULLMUTEX
} else {
SQLITE_OPEN_NOMUTEX
};

let c_path =
CString::new(db_path.to_string_lossy().as_bytes()).map_err(|_| Errno::ConvertingCString)?;
Expand Down
15 changes: 15 additions & 0 deletions hermes/bin/src/runtime_extensions/hermes/sqlite/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! `SQLite` runtime extension implementation.

use std::sync::Once;

use tracing::debug;

use crate::{
Expand Down Expand Up @@ -29,6 +31,19 @@ mod kernel;
mod state;
mod statement;

/// Controls [`is_serialized`] value.
static SERIALIZED: Once = Once::new();

/// Make `SQLite` access serialized.
pub(crate) fn set_serialized() {
SERIALIZED.call_once(|| ());
}

/// Returns whether `SQLite` access is serialized.
pub(crate) fn is_serialized() -> bool {
SERIALIZED.is_completed()
}

/// Runtime Extension for `SQLite`
#[derive(Default)]
struct RteSqlite;
Expand Down