Skip to content

Commit

Permalink
Add lock for command invocation
Browse files Browse the repository at this point in the history
  • Loading branch information
Officeyutong committed Dec 3, 2024
1 parent 8a56c8f commit 8c9b4e6
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 17 deletions.
2 changes: 1 addition & 1 deletion light-client-db-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub struct KV {
pub key: Vec<u8>,
pub value: Vec<u8>,
}
#[derive(Serialize, Deserialize, Default, Debug)]
#[derive(Serialize, Deserialize, Default, Debug, Clone, Copy)]
/// A serializable CursorDirection
pub enum CursorDirection {
#[default]
Expand Down
4 changes: 4 additions & 0 deletions light-client-db-worker/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@ where
)
.await
.with_context(|| anyhow!("Failed to collect iterator"))?;
debug!(
"Called iterator, args=<{:?}, {:?}, {:?}, {:?}>, result={:?}",
start_key_bound, order, limit, skip, kvs
);
DbCommandResponse::Iterator { kvs }
}
DbCommandRequest::IteratorKey {
Expand Down
1 change: 1 addition & 0 deletions light-client-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
},
"dependencies": {
"@ckb-ccc/core": "0.1.0-alpha.6",
"async-mutex": "^0.5.0",
"light-client-db-worker": "file:../light-client-db-worker",
"light-client-wasm": "file:../light-client-wasm",
"stream-browserify": "^3.0.0"
Expand Down
49 changes: 37 additions & 12 deletions light-client-js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ import { ClientFindCellsResponse, ClientFindTransactionsGroupedResponse, ClientF
import { FetchResponse, LocalNode, localNodeTo, NetworkFlag, RemoteNode, remoteNodeTo, ScriptStatus, scriptStatusFrom, scriptStatusTo, LightClientSetScriptsCommand, transformFetchResponse, cccOrderToLightClientWasmOrder, GetTransactionsResponse, TxWithCell, TxWithCells, lightClientGetTransactionsResultTo, LightClientLocalNode, LightClientRemoteNode, LightClientScriptStatus } from "./types";
import { ClientBlock, ClientBlockHeader, Hex, hexFrom, HexLike, Num, numFrom, NumLike, numToHex, TransactionLike } from "@ckb-ccc/core/barrel";
import { JsonRpcBlockHeader, JsonRpcTransformers } from "@ckb-ccc/core/advancedBarrel";
import { Mutex } from "async-mutex";

const DEFAULT_BUFFER_SIZE = 50 * (1 << 20);
/**
* A LightClient instance
*/
class LightClient {
dbWorker: Worker | null
lightClientWorker: Worker | null
inputBuffer: SharedArrayBuffer
outputBuffer: SharedArrayBuffer
private dbWorker: Worker | null
private lightClientWorker: Worker | null
private inputBuffer: SharedArrayBuffer
private outputBuffer: SharedArrayBuffer
private commandInvokeLock: Mutex
/**
* Construct a LightClient instance.
* inputBuffer and outputBuffer are buffers used for transporting data between database and light client. Set them to appropriate sizes.
Expand All @@ -22,6 +25,7 @@ class LightClient {
this.lightClientWorker = new Worker(new URL("./lightclient.worker.ts", import.meta.url), { type: "module" });
this.inputBuffer = new SharedArrayBuffer(inputBufferSize);
this.outputBuffer = new SharedArrayBuffer(outputBufferSize);
this.commandInvokeLock = new Mutex();

}
/**
Expand Down Expand Up @@ -51,14 +55,35 @@ class LightClient {
});
}
private invokeLightClientCommand(name: string, args?: any[]): Promise<any> {
this.lightClientWorker.postMessage({
name,
args: args || []
});
return new Promise((res, rej) => {
this.lightClientWorker.onmessage = (e) => res(e.data);
this.lightClientWorker.onerror = (evt) => rej(evt);
});
// Why use lock here?
// light-client-wasm provides synchronous APIs, means if we send a call request through postMessage, onmessage will be called only when the command call resolved.
// We use lock here to avoid multiple call to postMessage before onmessage fired, to avoid mixed result of different calls
// Since light-client-wasm is synchronous, we won't lose any performance by locking here
return this.commandInvokeLock.runExclusive(async () => {
this.lightClientWorker.postMessage({
name,
args: args || []
});
return await new Promise((resolve, reject) => {
const clean = () => {
this.lightClientWorker.removeEventListener("message", resolveFn);
this.lightClientWorker.removeEventListener("error", errorFn);
}
const resolveFn = (evt: MessageEvent<any>) => {
resolve(evt.data);
clean();

};
const errorFn = (evt: ErrorEvent) => {
reject(evt);
clean();

};
this.lightClientWorker.addEventListener("message", resolveFn);
this.lightClientWorker.addEventListener("error", errorFn);
})
})

}
/**
* Stop the light client instance.
Expand Down
2 changes: 1 addition & 1 deletion light-client-js/src/types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ test("test scriptStatusTo/From", () => {
args: "0x0011223344"
},
script_type: "lock",
block_number: 1234n
block_number: "0x1234"
};
const transformed = scriptStatusTo(raw);

Expand Down
8 changes: 5 additions & 3 deletions light-client-js/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { numFrom } from "@ckb-ccc/core";
import { Hex } from "@ckb-ccc/core";
import { hexFrom } from "@ckb-ccc/core";
import { numToHex } from "@ckb-ccc/core";
import { ClientBlockHeader } from "@ckb-ccc/core";
import { ScriptLike } from "@ckb-ccc/core";
import { JsonRpcBlockHeader, JsonRpcScript, JsonRpcTransaction, JsonRpcTransformers } from "@ckb-ccc/core/advancedBarrel";
Expand Down Expand Up @@ -40,7 +42,7 @@ type JsonRpcScriptType = "lock" | "type";
interface LightClientScriptStatus {
script: JsonRpcScript;
script_type: JsonRpcScriptType;
block_number: Num;
block_number: Hex;
}

interface ScriptStatus {
Expand All @@ -51,15 +53,15 @@ interface ScriptStatus {

export function scriptStatusTo(input: LightClientScriptStatus): ScriptStatus {
return ({
blockNumber: input.block_number,
blockNumber: numFrom(input.block_number),
script: JsonRpcTransformers.scriptTo(input.script),
scriptType: input.script_type
})
}

export function scriptStatusFrom(input: ScriptStatus): LightClientScriptStatus {
return ({
block_number: input.blockNumber,
block_number: numToHex(input.blockNumber),
script: JsonRpcTransformers.scriptFrom(input.script),
script_type: input.scriptType
})
Expand Down
5 changes: 5 additions & 0 deletions light-client-lib/src/storage/db/browser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ impl CommunicationChannel {
Some(take_while),
),
};
debug!("Dispatching database command: {:?}", new_cmd);
let CommunicationChannel {
input_i32_arr,
input_u8_arr,
Expand Down Expand Up @@ -1101,6 +1102,10 @@ impl Storage {
// (block-hash, proved)
matched_blocks: Vec<(Byte32, bool)>,
) {
debug!(
"Adding matched blocks: ({:?}, {:?}, {:?})",
start_number, blocks_count, matched_blocks
);
assert!(!matched_blocks.is_empty());
let mut key = Key::Meta(MATCHED_FILTER_BLOCKS_KEY).into_vec();
key.extend(start_number.to_be_bytes());
Expand Down

0 comments on commit 8c9b4e6

Please sign in to comment.