Use a class to hold state for Rust GbtGenerator

This commit is contained in:
junderw
2023-06-24 23:05:43 -07:00
committed by Mononaut
parent 8cfda1a546
commit ded2352cf8
6 changed files with 126 additions and 46 deletions

View File

@@ -1,9 +1,9 @@
use napi::bindgen_prelude::*;
use napi_derive::napi;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::sync::Mutex;
use std::sync::{Arc, Mutex};
use utils::U32HasherState;
mod audit_transaction;
mod gbt;
@@ -11,41 +11,48 @@ mod thread_transaction;
mod utils;
use thread_transaction::ThreadTransaction;
static THREAD_TRANSACTIONS: Lazy<Mutex<HashMap<u32, ThreadTransaction>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
type ThreadTransactionsMap = HashMap<u32, ThreadTransaction, U32HasherState>;
#[napi(ts_args_type = "mempoolBuffer: Uint8Array")]
pub async fn make(mempool_buffer: Uint8Array) -> Result<GbtResult> {
let mut map = HashMap::new();
for tx in ThreadTransaction::batch_from_buffer(&mempool_buffer) {
map.insert(tx.uid, tx);
}
{
let mut global_map = THREAD_TRANSACTIONS
.lock()
.map_err(|_| napi::Error::from_reason("THREAD_TRANSACTIONS Mutex poisoned"))?;
*global_map = map;
}
run_in_thread().await
#[napi]
pub struct GbtGenerator {
thread_transactions: Arc<Mutex<ThreadTransactionsMap>>,
}
#[napi(ts_args_type = "newTxs: Uint8Array, removeTxs: Uint8Array")]
pub async fn update(new_txs: Uint8Array, remove_txs: Uint8Array) -> Result<GbtResult> {
{
let mut map = THREAD_TRANSACTIONS
.lock()
.map_err(|_| napi::Error::from_reason("THREAD_TRANSACTIONS Mutex poisoned"))?;
for tx in ThreadTransaction::batch_from_buffer(&new_txs) {
map.insert(tx.uid, tx);
}
for txid in &utils::txids_from_buffer(&remove_txs) {
map.remove(txid);
#[napi]
impl GbtGenerator {
#[napi(constructor)]
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
thread_transactions: Arc::new(Mutex::new(HashMap::with_capacity_and_hasher(
2048,
U32HasherState,
))),
}
}
run_in_thread().await
#[napi]
pub async fn make(&self, mempool_buffer: Uint8Array) -> Result<GbtResult> {
run_task(Arc::clone(&self.thread_transactions), move |map| {
for tx in ThreadTransaction::batch_from_buffer(&mempool_buffer) {
map.insert(tx.uid, tx);
}
})
.await
}
#[napi]
pub async fn update(&self, new_txs: Uint8Array, remove_txs: Uint8Array) -> Result<GbtResult> {
run_task(Arc::clone(&self.thread_transactions), move |map| {
for tx in ThreadTransaction::batch_from_buffer(&new_txs) {
map.insert(tx.uid, tx);
}
for txid in &utils::txids_from_buffer(&remove_txs) {
map.remove(txid);
}
})
.await
}
}
/// The result from calling the gbt function.
@@ -61,11 +68,27 @@ pub struct GbtResult {
pub rates: Vec<Vec<f64>>, // Tuples not supported. u32 fits inside f64
}
async fn run_in_thread() -> Result<GbtResult> {
/// All on another thread, this runs an arbitrary task in between
/// taking the lock and running gbt.
///
/// Rather than filling / updating the HashMap on the main thread,
/// this allows for HashMap modifying tasks to be run before running and returning gbt results.
///
/// `thread_transactions` is a cloned Arc of the Mutex for the HashMap state.
/// `callback` is a `'static + Send` `FnOnce` closure/function that takes a mutable reference
/// to the HashMap as the only argument. (A move closure is recommended to meet the bounds)
async fn run_task<F>(
thread_transactions: Arc<Mutex<ThreadTransactionsMap>>,
callback: F,
) -> Result<GbtResult>
where
F: FnOnce(&mut ThreadTransactionsMap) + Send + 'static,
{
let handle = napi::tokio::task::spawn_blocking(move || {
let mut map = THREAD_TRANSACTIONS
let mut map = thread_transactions
.lock()
.map_err(|_| napi::Error::from_reason("THREAD_TRANSACTIONS Mutex poisoned"))?;
callback(&mut map);
gbt::gbt(&mut map).ok_or_else(|| napi::Error::from_reason("gbt failed"))
});