@lix-js/sdk 0.6.0-preview.5 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -4
- package/dist/errors.d.ts +7 -0
- package/dist/errors.js +19 -0
- package/dist/index.d.ts +4 -5
- package/dist/index.js +3 -3
- package/dist/native.d.ts +1 -0
- package/dist/native.js +47 -0
- package/dist/open-lix.d.ts +38 -207
- package/dist/open-lix.js +59 -284
- package/dist/result.d.ts +18 -0
- package/dist/result.js +48 -0
- package/dist/types.d.ts +114 -1
- package/dist/value.d.ts +28 -0
- package/dist/value.js +245 -0
- package/package.json +38 -71
- package/SKILL.md +0 -507
- package/dist/builtin-schemas.d.ts +0 -1
- package/dist/builtin-schemas.js +0 -1
- package/dist/engine-wasm/index.d.ts +0 -87
- package/dist/engine-wasm/index.js +0 -339
- package/dist/engine-wasm/wasm/lix_engine.d.ts +0 -79
- package/dist/engine-wasm/wasm/lix_engine.js +0 -833
- package/dist/engine-wasm/wasm/lix_engine.wasm +0 -0
- package/dist/engine-wasm/wasm/lix_engine.wasm.d.ts +0 -27
- package/dist/generated/builtin-schemas.d.ts +0 -427
- package/dist/generated/builtin-schemas.js +0 -643
- package/dist/sqlite/index.d.ts +0 -12
- package/dist/sqlite/index.js +0 -359
- package/dist-engine-src/README.md +0 -18
- package/dist-engine-src/src/backend/capabilities.rs +0 -67
- package/dist-engine-src/src/backend/conformance/baseline.rs +0 -1127
- package/dist-engine-src/src/backend/conformance/factory.rs +0 -93
- package/dist-engine-src/src/backend/conformance/failure_tests.rs +0 -608
- package/dist-engine-src/src/backend/conformance/fixtures.rs +0 -26
- package/dist-engine-src/src/backend/conformance/mod.rs +0 -75
- package/dist-engine-src/src/backend/conformance/model.rs +0 -28
- package/dist-engine-src/src/backend/conformance/model_based.rs +0 -257
- package/dist-engine-src/src/backend/conformance/persistence.rs +0 -204
- package/dist-engine-src/src/backend/conformance/projection.rs +0 -21
- package/dist-engine-src/src/backend/conformance/pushdown.rs +0 -24
- package/dist-engine-src/src/backend/conformance/runner.rs +0 -90
- package/dist-engine-src/src/backend/conformance/scan.rs +0 -24
- package/dist-engine-src/src/backend/conformance/write.rs +0 -16
- package/dist-engine-src/src/backend/error.rs +0 -94
- package/dist-engine-src/src/backend/in_memory.rs +0 -670
- package/dist-engine-src/src/backend/mod.rs +0 -39
- package/dist-engine-src/src/backend/predicate.rs +0 -80
- package/dist-engine-src/src/backend/traits.rs +0 -260
- package/dist-engine-src/src/backend/types.rs +0 -239
- package/dist-engine-src/src/binary_cas/chunking.rs +0 -31
- package/dist-engine-src/src/binary_cas/codec.rs +0 -346
- package/dist-engine-src/src/binary_cas/context.rs +0 -139
- package/dist-engine-src/src/binary_cas/kv.rs +0 -1038
- package/dist-engine-src/src/binary_cas/mod.rs +0 -11
- package/dist-engine-src/src/binary_cas/types.rs +0 -121
- package/dist-engine-src/src/branch/context.rs +0 -40
- package/dist-engine-src/src/branch/lifecycle.rs +0 -221
- package/dist-engine-src/src/branch/mod.rs +0 -13
- package/dist-engine-src/src/branch/refs.rs +0 -321
- package/dist-engine-src/src/branch/stage_rows.rs +0 -67
- package/dist-engine-src/src/branch/types.rs +0 -21
- package/dist-engine-src/src/catalog/context.rs +0 -412
- package/dist-engine-src/src/catalog/mod.rs +0 -10
- package/dist-engine-src/src/catalog/schema.rs +0 -4
- package/dist-engine-src/src/catalog/snapshot.rs +0 -1114
- package/dist-engine-src/src/cel/context.rs +0 -86
- package/dist-engine-src/src/cel/error.rs +0 -19
- package/dist-engine-src/src/cel/mod.rs +0 -8
- package/dist-engine-src/src/cel/provider.rs +0 -9
- package/dist-engine-src/src/cel/runtime.rs +0 -167
- package/dist-engine-src/src/cel/value.rs +0 -50
- package/dist-engine-src/src/changelog/bench_support.rs +0 -785
- package/dist-engine-src/src/changelog/change.rs +0 -1
- package/dist-engine-src/src/changelog/codec.rs +0 -497
- package/dist-engine-src/src/changelog/commit.rs +0 -1
- package/dist-engine-src/src/changelog/context.rs +0 -1614
- package/dist-engine-src/src/changelog/mod.rs +0 -29
- package/dist-engine-src/src/changelog/store.rs +0 -163
- package/dist-engine-src/src/changelog/test_support.rs +0 -54
- package/dist-engine-src/src/changelog/types.rs +0 -213
- package/dist-engine-src/src/commit_graph/context.rs +0 -944
- package/dist-engine-src/src/commit_graph/mod.rs +0 -9
- package/dist-engine-src/src/commit_graph/types.rs +0 -89
- package/dist-engine-src/src/commit_graph/walker.rs +0 -786
- package/dist-engine-src/src/common/error.rs +0 -347
- package/dist-engine-src/src/common/fingerprint.rs +0 -3
- package/dist-engine-src/src/common/fs_path.rs +0 -1336
- package/dist-engine-src/src/common/identity.rs +0 -145
- package/dist-engine-src/src/common/json_pointer.rs +0 -67
- package/dist-engine-src/src/common/metadata.rs +0 -40
- package/dist-engine-src/src/common/mod.rs +0 -23
- package/dist-engine-src/src/common/types.rs +0 -105
- package/dist-engine-src/src/common/wire.rs +0 -222
- package/dist-engine-src/src/domain.rs +0 -320
- package/dist-engine-src/src/engine.rs +0 -203
- package/dist-engine-src/src/entity_pk.rs +0 -402
- package/dist-engine-src/src/functions/context.rs +0 -296
- package/dist-engine-src/src/functions/deterministic.rs +0 -113
- package/dist-engine-src/src/functions/mod.rs +0 -18
- package/dist-engine-src/src/functions/provider.rs +0 -130
- package/dist-engine-src/src/functions/state.rs +0 -335
- package/dist-engine-src/src/functions/types.rs +0 -37
- package/dist-engine-src/src/init.rs +0 -692
- package/dist-engine-src/src/json_store/compression.rs +0 -77
- package/dist-engine-src/src/json_store/context.rs +0 -172
- package/dist-engine-src/src/json_store/encoded.rs +0 -15
- package/dist-engine-src/src/json_store/mod.rs +0 -38
- package/dist-engine-src/src/json_store/store.rs +0 -494
- package/dist-engine-src/src/json_store/types.rs +0 -212
- package/dist-engine-src/src/lib.rs +0 -92
- package/dist-engine-src/src/live_state/context.rs +0 -1883
- package/dist-engine-src/src/live_state/mod.rs +0 -21
- package/dist-engine-src/src/live_state/overlay.rs +0 -75
- package/dist-engine-src/src/live_state/reader.rs +0 -23
- package/dist-engine-src/src/live_state/types.rs +0 -231
- package/dist-engine-src/src/live_state/visibility.rs +0 -666
- package/dist-engine-src/src/plugin/archive.rs +0 -438
- package/dist-engine-src/src/plugin/component.rs +0 -183
- package/dist-engine-src/src/plugin/install.rs +0 -619
- package/dist-engine-src/src/plugin/manifest.rs +0 -516
- package/dist-engine-src/src/plugin/materializer.rs +0 -202
- package/dist-engine-src/src/plugin/mod.rs +0 -33
- package/dist-engine-src/src/plugin/plugin_manifest.json +0 -119
- package/dist-engine-src/src/plugin/storage.rs +0 -74
- package/dist-engine-src/src/schema/annotations/defaults.rs +0 -275
- package/dist-engine-src/src/schema/annotations/mod.rs +0 -1
- package/dist-engine-src/src/schema/builtin/lix_account.json +0 -21
- package/dist-engine-src/src/schema/builtin/lix_active_account.json +0 -29
- package/dist-engine-src/src/schema/builtin/lix_binary_blob_ref.json +0 -29
- package/dist-engine-src/src/schema/builtin/lix_branch_descriptor.json +0 -34
- package/dist-engine-src/src/schema/builtin/lix_branch_ref.json +0 -48
- package/dist-engine-src/src/schema/builtin/lix_change.json +0 -63
- package/dist-engine-src/src/schema/builtin/lix_change_author.json +0 -45
- package/dist-engine-src/src/schema/builtin/lix_commit.json +0 -24
- package/dist-engine-src/src/schema/builtin/lix_commit_edge.json +0 -53
- package/dist-engine-src/src/schema/builtin/lix_directory_descriptor.json +0 -52
- package/dist-engine-src/src/schema/builtin/lix_file_descriptor.json +0 -52
- package/dist-engine-src/src/schema/builtin/lix_key_value.json +0 -40
- package/dist-engine-src/src/schema/builtin/lix_label.json +0 -29
- package/dist-engine-src/src/schema/builtin/lix_label_assignment.json +0 -74
- package/dist-engine-src/src/schema/builtin/lix_registered_schema.json +0 -25
- package/dist-engine-src/src/schema/builtin/mod.rs +0 -220
- package/dist-engine-src/src/schema/compatibility.rs +0 -787
- package/dist-engine-src/src/schema/definition.json +0 -187
- package/dist-engine-src/src/schema/definition.rs +0 -742
- package/dist-engine-src/src/schema/key.rs +0 -138
- package/dist-engine-src/src/schema/mod.rs +0 -20
- package/dist-engine-src/src/schema/seed.rs +0 -14
- package/dist-engine-src/src/schema/tests.rs +0 -780
- package/dist-engine-src/src/session/context.rs +0 -1059
- package/dist-engine-src/src/session/create_branch.rs +0 -94
- package/dist-engine-src/src/session/execute.rs +0 -681
- package/dist-engine-src/src/session/merge/analysis.rs +0 -108
- package/dist-engine-src/src/session/merge/branch.rs +0 -417
- package/dist-engine-src/src/session/merge/conflicts.rs +0 -63
- package/dist-engine-src/src/session/merge/mod.rs +0 -10
- package/dist-engine-src/src/session/merge/stats.rs +0 -61
- package/dist-engine-src/src/session/mod.rs +0 -30
- package/dist-engine-src/src/session/switch_branch.rs +0 -113
- package/dist-engine-src/src/session/transaction.rs +0 -557
- package/dist-engine-src/src/sql2/bind/classify.rs +0 -102
- package/dist-engine-src/src/sql2/bind/error.rs +0 -5
- package/dist-engine-src/src/sql2/bind/expr.rs +0 -29
- package/dist-engine-src/src/sql2/bind/mod.rs +0 -12
- package/dist-engine-src/src/sql2/bind/public_udf.rs +0 -306
- package/dist-engine-src/src/sql2/bind/read.rs +0 -65
- package/dist-engine-src/src/sql2/bind/statement.rs +0 -2236
- package/dist-engine-src/src/sql2/bind/table.rs +0 -273
- package/dist-engine-src/src/sql2/bind/write.rs +0 -86
- package/dist-engine-src/src/sql2/branch_scope.rs +0 -436
- package/dist-engine-src/src/sql2/catalog/capability.rs +0 -20
- package/dist-engine-src/src/sql2/catalog/entity_surface.rs +0 -296
- package/dist-engine-src/src/sql2/catalog/mod.rs +0 -15
- package/dist-engine-src/src/sql2/catalog/registry.rs +0 -556
- package/dist-engine-src/src/sql2/catalog/schema.rs +0 -88
- package/dist-engine-src/src/sql2/catalog/surface.rs +0 -41
- package/dist-engine-src/src/sql2/change_materialization.rs +0 -122
- package/dist-engine-src/src/sql2/context.rs +0 -317
- package/dist-engine-src/src/sql2/dml.rs +0 -148
- package/dist-engine-src/src/sql2/error.rs +0 -215
- package/dist-engine-src/src/sql2/exec/bound_public_write.rs +0 -1593
- package/dist-engine-src/src/sql2/exec/datafusion.rs +0 -5266
- package/dist-engine-src/src/sql2/exec/fast_write.rs +0 -82
- package/dist-engine-src/src/sql2/exec/mod.rs +0 -24
- package/dist-engine-src/src/sql2/exec/write.rs +0 -661
- package/dist-engine-src/src/sql2/filesystem_planner.rs +0 -1485
- package/dist-engine-src/src/sql2/filesystem_predicates.rs +0 -159
- package/dist-engine-src/src/sql2/filesystem_visibility.rs +0 -383
- package/dist-engine-src/src/sql2/history_projection.rs +0 -56
- package/dist-engine-src/src/sql2/history_route.rs +0 -661
- package/dist-engine-src/src/sql2/mod.rs +0 -52
- package/dist-engine-src/src/sql2/optimize/datafusion.rs +0 -1
- package/dist-engine-src/src/sql2/optimize/mod.rs +0 -2
- package/dist-engine-src/src/sql2/optimize/simple_write.rs +0 -116
- package/dist-engine-src/src/sql2/parse/mod.rs +0 -69
- package/dist-engine-src/src/sql2/parse/normalize.rs +0 -1
- package/dist-engine-src/src/sql2/plan/branch_scope.rs +0 -24
- package/dist-engine-src/src/sql2/plan/mod.rs +0 -5
- package/dist-engine-src/src/sql2/plan/predicate.rs +0 -22
- package/dist-engine-src/src/sql2/plan/write.rs +0 -147
- package/dist-engine-src/src/sql2/predicate_typecheck.rs +0 -504
- package/dist-engine-src/src/sql2/providers/branch.rs +0 -1206
- package/dist-engine-src/src/sql2/providers/change.rs +0 -445
- package/dist-engine-src/src/sql2/providers/directory.rs +0 -2422
- package/dist-engine-src/src/sql2/providers/directory_history.rs +0 -645
- package/dist-engine-src/src/sql2/providers/entity.rs +0 -1484
- package/dist-engine-src/src/sql2/providers/entity_history.rs +0 -452
- package/dist-engine-src/src/sql2/providers/file.rs +0 -3686
- package/dist-engine-src/src/sql2/providers/file_history.rs +0 -924
- package/dist-engine-src/src/sql2/providers/history.rs +0 -426
- package/dist-engine-src/src/sql2/providers/lix_state.rs +0 -2542
- package/dist-engine-src/src/sql2/providers/mod.rs +0 -508
- package/dist-engine-src/src/sql2/read_only.rs +0 -63
- package/dist-engine-src/src/sql2/record_batch.rs +0 -17
- package/dist-engine-src/src/sql2/result_metadata.rs +0 -29
- package/dist-engine-src/src/sql2/runtime.rs +0 -60
- package/dist-engine-src/src/sql2/session.rs +0 -83
- package/dist-engine-src/src/sql2/storage/constraints.rs +0 -1
- package/dist-engine-src/src/sql2/storage/mod.rs +0 -1
- package/dist-engine-src/src/sql2/test_support/differential.rs +0 -712
- package/dist-engine-src/src/sql2/test_support/generators.rs +0 -354
- package/dist-engine-src/src/sql2/test_support/mod.rs +0 -2
- package/dist-engine-src/src/sql2/udfs/common.rs +0 -295
- package/dist-engine-src/src/sql2/udfs/lix_active_branch_commit_id.rs +0 -53
- package/dist-engine-src/src/sql2/udfs/lix_empty_blob.rs +0 -47
- package/dist-engine-src/src/sql2/udfs/lix_json.rs +0 -100
- package/dist-engine-src/src/sql2/udfs/lix_json_get.rs +0 -99
- package/dist-engine-src/src/sql2/udfs/lix_json_get_text.rs +0 -99
- package/dist-engine-src/src/sql2/udfs/lix_text_decode.rs +0 -82
- package/dist-engine-src/src/sql2/udfs/lix_text_encode.rs +0 -85
- package/dist-engine-src/src/sql2/udfs/lix_timestamp.rs +0 -76
- package/dist-engine-src/src/sql2/udfs/lix_uuid_v7.rs +0 -76
- package/dist-engine-src/src/sql2/udfs/mod.rs +0 -86
- package/dist-engine-src/src/sql2/write_normalization.rs +0 -368
- package/dist-engine-src/src/storage/conformance.rs +0 -399
- package/dist-engine-src/src/storage/context.rs +0 -620
- package/dist-engine-src/src/storage/mod.rs +0 -52
- package/dist-engine-src/src/storage/point.rs +0 -440
- package/dist-engine-src/src/storage/read_scope.rs +0 -67
- package/dist-engine-src/src/storage/reader.rs +0 -867
- package/dist-engine-src/src/storage/scan.rs +0 -784
- package/dist-engine-src/src/storage/spaces.rs +0 -236
- package/dist-engine-src/src/storage/stats.rs +0 -80
- package/dist-engine-src/src/storage/write_set.rs +0 -962
- package/dist-engine-src/src/storage_bench.rs +0 -171
- package/dist-engine-src/src/test_support.rs +0 -450
- package/dist-engine-src/src/tracked_state/bench_support.rs +0 -394
- package/dist-engine-src/src/tracked_state/codec.rs +0 -1183
- package/dist-engine-src/src/tracked_state/commit_root_rebuild.rs +0 -358
- package/dist-engine-src/src/tracked_state/context.rs +0 -2801
- package/dist-engine-src/src/tracked_state/diff.rs +0 -2140
- package/dist-engine-src/src/tracked_state/merge.rs +0 -478
- package/dist-engine-src/src/tracked_state/mod.rs +0 -35
- package/dist-engine-src/src/tracked_state/row_materialization.rs +0 -275
- package/dist-engine-src/src/tracked_state/storage.rs +0 -427
- package/dist-engine-src/src/tracked_state/tree.rs +0 -3063
- package/dist-engine-src/src/tracked_state/types.rs +0 -238
- package/dist-engine-src/src/transaction/bench_support.rs +0 -407
- package/dist-engine-src/src/transaction/commit.rs +0 -1592
- package/dist-engine-src/src/transaction/context.rs +0 -1653
- package/dist-engine-src/src/transaction/mod.rs +0 -24
- package/dist-engine-src/src/transaction/normalization.rs +0 -877
- package/dist-engine-src/src/transaction/prep.rs +0 -37
- package/dist-engine-src/src/transaction/schema_resolver.rs +0 -163
- package/dist-engine-src/src/transaction/staging.rs +0 -1525
- package/dist-engine-src/src/transaction/types.rs +0 -403
- package/dist-engine-src/src/transaction/validation.rs +0 -5766
- package/dist-engine-src/src/untracked_state/codec.rs +0 -615
- package/dist-engine-src/src/untracked_state/context.rs +0 -98
- package/dist-engine-src/src/untracked_state/materialization.rs +0 -63
- package/dist-engine-src/src/untracked_state/mod.rs +0 -15
- package/dist-engine-src/src/untracked_state/storage.rs +0 -898
- package/dist-engine-src/src/untracked_state/types.rs +0 -146
- package/dist-engine-src/src/wasm/mod.rs +0 -60
|
@@ -1,1059 +0,0 @@
|
|
|
1
|
-
use std::future::Future;
|
|
2
|
-
use std::pin::Pin;
|
|
3
|
-
use std::sync::Arc;
|
|
4
|
-
|
|
5
|
-
use serde_json::Value as JsonValue;
|
|
6
|
-
|
|
7
|
-
use crate::binary_cas::{BinaryCasContext, BlobDataReader};
|
|
8
|
-
use crate::branch::{
|
|
9
|
-
BranchContext, BranchLifecycle, BranchOperation, BranchRefReader, BranchReferenceRole,
|
|
10
|
-
};
|
|
11
|
-
use crate::catalog::CatalogContext;
|
|
12
|
-
use crate::commit_graph::{CommitGraphContext, CommitGraphReader};
|
|
13
|
-
use crate::entity_pk::EntityPk;
|
|
14
|
-
use crate::functions::FunctionProviderHandle;
|
|
15
|
-
use crate::json_store::JsonStoreContext;
|
|
16
|
-
use crate::live_state::{LiveStateContext, LiveStateReader, LiveStateRowRequest};
|
|
17
|
-
use crate::sql2::{
|
|
18
|
-
ChangelogQuerySource, HistoryQuerySource, SqlChangelogQuerySource, SqlExecutionContext,
|
|
19
|
-
SqlHistoryQuerySource,
|
|
20
|
-
};
|
|
21
|
-
use crate::storage::{
|
|
22
|
-
DurableWriteGuard, DurableWriteLock, InMemoryStorageBackend, StorageBackend, StorageReadOptions,
|
|
23
|
-
};
|
|
24
|
-
use crate::storage::{StorageContext, StorageRead, StorageReadScope};
|
|
25
|
-
use crate::tracked_state::TrackedStateContext;
|
|
26
|
-
use crate::transaction::{open_transaction, Transaction};
|
|
27
|
-
use crate::GLOBAL_BRANCH_ID;
|
|
28
|
-
use crate::{LixError, NullableKeyFilter};
|
|
29
|
-
|
|
30
|
-
use super::transaction::{SessionOperationGuard, SessionTransactionManager, SessionWriteLease};
|
|
31
|
-
|
|
32
|
-
pub(crate) const WORKSPACE_BRANCH_KEY: &str = "lix_workspace_branch_id";
|
|
33
|
-
|
|
34
|
-
#[derive(Clone)]
|
|
35
|
-
pub(crate) enum SessionMode {
|
|
36
|
-
Pinned { branch_id: String },
|
|
37
|
-
Workspace,
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/// Session-context state for engine execution.
|
|
41
|
-
///
|
|
42
|
-
/// A session context pins the active branch selector and shared execution
|
|
43
|
-
/// services. Parent-handle `execute(...)` runs as an implicit single-statement
|
|
44
|
-
/// transaction. Explicit transactions hold the session execution lease until
|
|
45
|
-
/// commit or rollback, so all SQL during that window must run through the
|
|
46
|
-
/// transaction handle.
|
|
47
|
-
#[derive(Clone)]
|
|
48
|
-
pub struct SessionContext<B: StorageBackend = InMemoryStorageBackend> {
|
|
49
|
-
pub(super) mode: SessionMode,
|
|
50
|
-
pub(super) storage: StorageContext<B>,
|
|
51
|
-
pub(super) live_state: Arc<LiveStateContext>,
|
|
52
|
-
pub(super) tracked_state: Arc<TrackedStateContext>,
|
|
53
|
-
pub(super) binary_cas: Arc<BinaryCasContext>,
|
|
54
|
-
pub(super) branch_ctx: Arc<BranchContext>,
|
|
55
|
-
pub(super) catalog_context: Arc<CatalogContext>,
|
|
56
|
-
pub(super) write_lock: DurableWriteLock,
|
|
57
|
-
transaction_manager: SessionTransactionManager,
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
impl<B> SessionContext<B>
|
|
61
|
-
where
|
|
62
|
-
B: StorageBackend + Clone + Send + Sync + 'static,
|
|
63
|
-
for<'backend> B::Read<'backend>: Clone + Send + Sync + 'static,
|
|
64
|
-
for<'backend> B::Write<'backend>: Send,
|
|
65
|
-
{
|
|
66
|
-
pub(crate) async fn open_workspace(
|
|
67
|
-
storage: StorageContext<B>,
|
|
68
|
-
live_state: Arc<LiveStateContext>,
|
|
69
|
-
tracked_state: Arc<TrackedStateContext>,
|
|
70
|
-
binary_cas: Arc<BinaryCasContext>,
|
|
71
|
-
branch_ctx: Arc<BranchContext>,
|
|
72
|
-
catalog_context: Arc<CatalogContext>,
|
|
73
|
-
write_lock: DurableWriteLock,
|
|
74
|
-
) -> Result<Self, LixError> {
|
|
75
|
-
let session = Self::new(
|
|
76
|
-
SessionMode::Workspace,
|
|
77
|
-
storage,
|
|
78
|
-
live_state,
|
|
79
|
-
tracked_state,
|
|
80
|
-
binary_cas,
|
|
81
|
-
branch_ctx,
|
|
82
|
-
catalog_context,
|
|
83
|
-
write_lock,
|
|
84
|
-
);
|
|
85
|
-
session.active_branch_id().await?;
|
|
86
|
-
Ok(session)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
pub(crate) async fn open(
|
|
90
|
-
active_branch_id: String,
|
|
91
|
-
storage: StorageContext<B>,
|
|
92
|
-
live_state: Arc<LiveStateContext>,
|
|
93
|
-
tracked_state: Arc<TrackedStateContext>,
|
|
94
|
-
binary_cas: Arc<BinaryCasContext>,
|
|
95
|
-
branch_ctx: Arc<BranchContext>,
|
|
96
|
-
catalog_context: Arc<CatalogContext>,
|
|
97
|
-
write_lock: DurableWriteLock,
|
|
98
|
-
) -> Result<Self, LixError> {
|
|
99
|
-
Ok(Self::new(
|
|
100
|
-
SessionMode::Pinned {
|
|
101
|
-
branch_id: active_branch_id,
|
|
102
|
-
},
|
|
103
|
-
storage,
|
|
104
|
-
live_state,
|
|
105
|
-
tracked_state,
|
|
106
|
-
binary_cas,
|
|
107
|
-
branch_ctx,
|
|
108
|
-
catalog_context,
|
|
109
|
-
write_lock,
|
|
110
|
-
))
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
pub(super) fn new(
|
|
114
|
-
mode: SessionMode,
|
|
115
|
-
storage: StorageContext<B>,
|
|
116
|
-
live_state: Arc<LiveStateContext>,
|
|
117
|
-
tracked_state: Arc<TrackedStateContext>,
|
|
118
|
-
binary_cas: Arc<BinaryCasContext>,
|
|
119
|
-
branch_ctx: Arc<BranchContext>,
|
|
120
|
-
catalog_context: Arc<CatalogContext>,
|
|
121
|
-
write_lock: DurableWriteLock,
|
|
122
|
-
) -> Self {
|
|
123
|
-
Self::new_with_transaction_manager(
|
|
124
|
-
mode,
|
|
125
|
-
storage,
|
|
126
|
-
live_state,
|
|
127
|
-
tracked_state,
|
|
128
|
-
binary_cas,
|
|
129
|
-
branch_ctx,
|
|
130
|
-
catalog_context,
|
|
131
|
-
write_lock,
|
|
132
|
-
SessionTransactionManager::new(),
|
|
133
|
-
)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
pub(super) fn new_with_transaction_manager(
|
|
137
|
-
mode: SessionMode,
|
|
138
|
-
storage: StorageContext<B>,
|
|
139
|
-
live_state: Arc<LiveStateContext>,
|
|
140
|
-
tracked_state: Arc<TrackedStateContext>,
|
|
141
|
-
binary_cas: Arc<BinaryCasContext>,
|
|
142
|
-
branch_ctx: Arc<BranchContext>,
|
|
143
|
-
catalog_context: Arc<CatalogContext>,
|
|
144
|
-
write_lock: DurableWriteLock,
|
|
145
|
-
transaction_manager: SessionTransactionManager,
|
|
146
|
-
) -> Self {
|
|
147
|
-
Self {
|
|
148
|
-
mode,
|
|
149
|
-
storage,
|
|
150
|
-
live_state,
|
|
151
|
-
tracked_state,
|
|
152
|
-
binary_cas,
|
|
153
|
-
branch_ctx,
|
|
154
|
-
catalog_context,
|
|
155
|
-
write_lock,
|
|
156
|
-
transaction_manager,
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/// Releases this logical session handle. This is a lifecycle boundary only:
|
|
161
|
-
/// successful writes are committed before their operation returns.
|
|
162
|
-
pub async fn close(&self) -> Result<(), LixError> {
|
|
163
|
-
self.transaction_manager.close().await
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
pub fn is_closed(&self) -> bool {
|
|
167
|
-
self.transaction_manager.is_closed()
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
#[cfg(test)]
|
|
171
|
-
pub(crate) fn operation_in_progress_count_for_test(&self) -> usize {
|
|
172
|
-
self.transaction_manager.operation_count_for_test()
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
#[cfg(test)]
|
|
176
|
-
pub(crate) fn commit_in_progress_for_test(&self) -> bool {
|
|
177
|
-
self.transaction_manager.commit_in_progress_for_test()
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
#[cfg(test)]
|
|
181
|
-
pub(crate) fn active_transaction_for_test(&self) -> bool {
|
|
182
|
-
self.transaction_manager.active_transaction_for_test()
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
pub(super) fn transaction_manager(&self) -> SessionTransactionManager {
|
|
186
|
-
self.transaction_manager.clone()
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
pub(crate) fn ensure_open(&self) -> Result<(), LixError> {
|
|
190
|
-
self.transaction_manager.ensure_open()
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
pub(super) fn begin_session_operation(&self) -> Result<SessionOperationGuard, LixError> {
|
|
194
|
-
self.transaction_manager.begin_session_operation()
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
pub(super) fn begin_session_write_lease(&self) -> Result<SessionWriteLease, LixError> {
|
|
198
|
-
self.transaction_manager.begin_write_lease()
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
pub(super) fn begin_explicit_session_write_lease(&self) -> Result<SessionWriteLease, LixError> {
|
|
202
|
-
self.transaction_manager.begin_explicit_write_lease()
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
pub(super) async fn begin_session_write_access(&self) -> Result<SessionWriteAccess, LixError> {
|
|
206
|
-
let write_lease = self.begin_session_write_lease()?;
|
|
207
|
-
self.begin_session_write_access_with_lease(write_lease)
|
|
208
|
-
.await
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
pub(super) async fn begin_explicit_session_write_access(
|
|
212
|
-
&self,
|
|
213
|
-
) -> Result<SessionWriteAccess, LixError> {
|
|
214
|
-
let write_lease = self.begin_explicit_session_write_lease()?;
|
|
215
|
-
self.begin_session_write_access_with_lease(write_lease)
|
|
216
|
-
.await
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
async fn begin_session_write_access_with_lease(
|
|
220
|
-
&self,
|
|
221
|
-
write_lease: SessionWriteLease,
|
|
222
|
-
) -> Result<SessionWriteAccess, LixError> {
|
|
223
|
-
let write_guard = self.write_lock.lock_owned().await;
|
|
224
|
-
let write_access = SessionWriteAccess {
|
|
225
|
-
_write_lease: write_lease,
|
|
226
|
-
_write_guard: write_guard,
|
|
227
|
-
};
|
|
228
|
-
self.ensure_open()?;
|
|
229
|
-
Ok(write_access)
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/// Resolves the branch this session should operate on right now.
|
|
233
|
-
///
|
|
234
|
-
/// This is a read-path helper. Write flows must resolve the active branch
|
|
235
|
-
/// through the transaction capability so the read is scoped to the
|
|
236
|
-
/// same backend transaction as the writes it influences.
|
|
237
|
-
///
|
|
238
|
-
/// Pinned sessions are pure in-memory views over one branch. Workspace
|
|
239
|
-
/// sessions read the shared workspace selector from untracked global
|
|
240
|
-
/// `lix_key_value` state so multiple open app sessions can observe the same
|
|
241
|
-
/// active workspace branch.
|
|
242
|
-
pub async fn active_branch_id(&self) -> Result<String, LixError> {
|
|
243
|
-
let _operation_guard = self.begin_session_operation()?;
|
|
244
|
-
let transaction = self.storage.begin_read(StorageReadOptions::default())?;
|
|
245
|
-
let result = self.active_branch_id_from_reader(&transaction).await;
|
|
246
|
-
match result {
|
|
247
|
-
Ok(branch_id) => Ok(branch_id),
|
|
248
|
-
Err(error) => Err(error),
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
pub(super) async fn active_branch_id_from_reader<S>(
|
|
253
|
-
&self,
|
|
254
|
-
reader: &S,
|
|
255
|
-
) -> Result<String, LixError>
|
|
256
|
-
where
|
|
257
|
-
S: StorageRead + Send + Sync + ?Sized,
|
|
258
|
-
{
|
|
259
|
-
self.ensure_open()?;
|
|
260
|
-
match &self.mode {
|
|
261
|
-
SessionMode::Pinned { branch_id } => Ok(branch_id.clone()),
|
|
262
|
-
SessionMode::Workspace => self.load_workspace_branch_id(reader).await,
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
async fn load_workspace_branch_id<S>(&self, reader: &S) -> Result<String, LixError>
|
|
267
|
-
where
|
|
268
|
-
S: StorageRead + Send + Sync + ?Sized,
|
|
269
|
-
{
|
|
270
|
-
let row = self
|
|
271
|
-
.live_state
|
|
272
|
-
.reader(reader)
|
|
273
|
-
.load_row(&LiveStateRowRequest {
|
|
274
|
-
schema_key: "lix_key_value".to_string(),
|
|
275
|
-
branch_id: GLOBAL_BRANCH_ID.to_string(),
|
|
276
|
-
entity_pk: EntityPk::single(WORKSPACE_BRANCH_KEY),
|
|
277
|
-
file_id: NullableKeyFilter::Null,
|
|
278
|
-
})
|
|
279
|
-
.await?
|
|
280
|
-
.ok_or_else(|| {
|
|
281
|
-
LixError::new(
|
|
282
|
-
"LIX_ERROR_UNKNOWN",
|
|
283
|
-
"workspace branch selector is missing lix_key_value:lix_workspace_branch_id",
|
|
284
|
-
)
|
|
285
|
-
})?;
|
|
286
|
-
let snapshot_content = row.snapshot_content.as_deref().ok_or_else(|| {
|
|
287
|
-
LixError::new(
|
|
288
|
-
"LIX_ERROR_UNKNOWN",
|
|
289
|
-
"workspace branch selector is missing snapshot_content",
|
|
290
|
-
)
|
|
291
|
-
})?;
|
|
292
|
-
let snapshot = serde_json::from_str::<JsonValue>(snapshot_content).map_err(|error| {
|
|
293
|
-
LixError::new(
|
|
294
|
-
"LIX_ERROR_UNKNOWN",
|
|
295
|
-
format!("workspace branch selector snapshot is invalid JSON: {error}"),
|
|
296
|
-
)
|
|
297
|
-
})?;
|
|
298
|
-
let branch_id = snapshot
|
|
299
|
-
.get("value")
|
|
300
|
-
.and_then(JsonValue::as_str)
|
|
301
|
-
.filter(|value| !value.is_empty())
|
|
302
|
-
.ok_or_else(|| {
|
|
303
|
-
LixError::new(
|
|
304
|
-
"LIX_ERROR_UNKNOWN",
|
|
305
|
-
"workspace branch selector value must be a non-empty string",
|
|
306
|
-
)
|
|
307
|
-
})?
|
|
308
|
-
.to_string();
|
|
309
|
-
|
|
310
|
-
let branch_ref = self.branch_ctx.ref_reader(reader);
|
|
311
|
-
BranchLifecycle::new(&branch_ref)
|
|
312
|
-
.require_existing_ref(
|
|
313
|
-
&branch_id,
|
|
314
|
-
BranchOperation::LoadWorkspaceSelector,
|
|
315
|
-
BranchReferenceRole::WorkspaceSelector,
|
|
316
|
-
)
|
|
317
|
-
.await?;
|
|
318
|
-
|
|
319
|
-
Ok(branch_id)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
pub(crate) async fn with_write_transaction<T, F>(&self, f: F) -> Result<T, LixError>
|
|
323
|
-
where
|
|
324
|
-
F: for<'tx> FnOnce(
|
|
325
|
-
&'tx mut Transaction<B>,
|
|
326
|
-
) -> Pin<Box<dyn Future<Output = Result<T, LixError>> + 'tx>>,
|
|
327
|
-
{
|
|
328
|
-
self.ensure_open()?;
|
|
329
|
-
let write_access = self.begin_session_write_access().await?;
|
|
330
|
-
self.with_write_transaction_reserved(write_access, f).await
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
pub(super) async fn with_write_transaction_reserved<T, F>(
|
|
334
|
-
&self,
|
|
335
|
-
_write_access: SessionWriteAccess,
|
|
336
|
-
f: F,
|
|
337
|
-
) -> Result<T, LixError>
|
|
338
|
-
where
|
|
339
|
-
F: for<'tx> FnOnce(
|
|
340
|
-
&'tx mut Transaction<B>,
|
|
341
|
-
) -> Pin<Box<dyn Future<Output = Result<T, LixError>> + 'tx>>,
|
|
342
|
-
{
|
|
343
|
-
let opened = open_transaction(
|
|
344
|
-
&self.mode,
|
|
345
|
-
self.storage.clone(),
|
|
346
|
-
Arc::clone(&self.live_state),
|
|
347
|
-
Arc::clone(&self.tracked_state),
|
|
348
|
-
Arc::clone(&self.binary_cas),
|
|
349
|
-
Arc::clone(&self.branch_ctx),
|
|
350
|
-
Arc::clone(&self.catalog_context),
|
|
351
|
-
)
|
|
352
|
-
.await?;
|
|
353
|
-
self.ensure_open()?;
|
|
354
|
-
let mut transaction = opened.transaction;
|
|
355
|
-
transaction.attach_commit_boundary(self.transaction_commit_boundary());
|
|
356
|
-
let runtime_functions = opened.runtime_functions;
|
|
357
|
-
|
|
358
|
-
match f(&mut transaction).await {
|
|
359
|
-
Ok(value) => {
|
|
360
|
-
self.ensure_open()?;
|
|
361
|
-
transaction.commit(&runtime_functions).await?;
|
|
362
|
-
Ok(value)
|
|
363
|
-
}
|
|
364
|
-
Err(error) => Err(error),
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
#[cfg(test)]
|
|
369
|
-
pub(super) fn begin_commit(&self) -> crate::transaction::CommitBoundaryGuard {
|
|
370
|
-
self.transaction_manager.begin_commit()
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
pub(super) fn transaction_commit_boundary(
|
|
374
|
-
&self,
|
|
375
|
-
) -> crate::transaction::TransactionCommitBoundary {
|
|
376
|
-
self.transaction_manager.transaction_commit_boundary()
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
pub(super) struct SessionWriteAccess {
|
|
381
|
-
_write_guard: DurableWriteGuard,
|
|
382
|
-
_write_lease: SessionWriteLease,
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
pub(super) fn closed_error() -> LixError {
|
|
386
|
-
LixError::new(LixError::CODE_CLOSED, "Lix handle is closed")
|
|
387
|
-
.with_hint("Open a new Lix handle before calling this method.")
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
/// Read-only SQL execution context derived from a session.
|
|
391
|
-
///
|
|
392
|
-
/// Write statements re-plan against `Transaction`; this context intentionally
|
|
393
|
-
/// has no write stager.
|
|
394
|
-
pub(super) struct SessionSqlExecutionContext<'a, R> {
|
|
395
|
-
pub(super) active_branch_id: &'a str,
|
|
396
|
-
pub(super) read_store: StorageReadScope<R>,
|
|
397
|
-
pub(super) live_state: Arc<LiveStateContext>,
|
|
398
|
-
pub(super) binary_cas: Arc<BinaryCasContext>,
|
|
399
|
-
pub(super) branch_ctx: Arc<BranchContext>,
|
|
400
|
-
pub(super) visible_schemas: Vec<JsonValue>,
|
|
401
|
-
pub(super) functions: FunctionProviderHandle,
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
impl<R> SqlExecutionContext for SessionSqlExecutionContext<'_, R>
|
|
405
|
-
where
|
|
406
|
-
R: crate::storage::StorageBackendRead + Clone + Send + Sync + 'static,
|
|
407
|
-
{
|
|
408
|
-
type ReadStore = StorageReadScope<R>;
|
|
409
|
-
|
|
410
|
-
fn active_branch_id(&self) -> &str {
|
|
411
|
-
self.active_branch_id
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
fn live_state(&self) -> Arc<dyn LiveStateReader> {
|
|
415
|
-
Arc::new(self.live_state.reader(self.read_store.clone())) as Arc<dyn LiveStateReader>
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
fn history_query_source(&self) -> SqlHistoryQuerySource<Self::ReadStore> {
|
|
419
|
-
HistoryQuerySource {
|
|
420
|
-
json_reader: JsonStoreContext::new().reader(self.read_store.store()),
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
fn changelog_query_source(&self) -> SqlChangelogQuerySource<Self::ReadStore> {
|
|
425
|
-
ChangelogQuerySource {
|
|
426
|
-
store: self.read_store.clone(),
|
|
427
|
-
json_reader: JsonStoreContext::new().reader(self.read_store.store()),
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
fn commit_graph(&self) -> Box<dyn CommitGraphReader> {
|
|
432
|
-
Box::new(CommitGraphContext::new().reader(self.read_store.clone()))
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
fn branch_ref(&self) -> Arc<dyn BranchRefReader> {
|
|
436
|
-
Arc::new(self.branch_ctx.ref_reader(self.read_store.clone()))
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
fn functions(&self) -> FunctionProviderHandle {
|
|
440
|
-
self.functions.clone()
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
fn blob_reader(&self) -> Arc<dyn BlobDataReader> {
|
|
444
|
-
Arc::new(self.binary_cas.reader(self.read_store.clone())) as Arc<dyn BlobDataReader>
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
fn list_visible_schemas(&self) -> Result<Vec<JsonValue>, LixError> {
|
|
448
|
-
Ok(self.visible_schemas.clone())
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
#[cfg(test)]
|
|
453
|
-
mod tests {
|
|
454
|
-
use std::future::Future;
|
|
455
|
-
use std::pin::Pin;
|
|
456
|
-
use std::sync::Condvar;
|
|
457
|
-
use std::sync::Mutex;
|
|
458
|
-
use std::task::{Context, Poll};
|
|
459
|
-
use std::thread;
|
|
460
|
-
use std::time::{Duration, Instant};
|
|
461
|
-
|
|
462
|
-
use crate::backend::{
|
|
463
|
-
Backend, BackendCapabilities, BackendError, DurableWriteLock, InMemoryBackend,
|
|
464
|
-
InMemoryRead, InMemoryWrite, ReadOptions, WriteOptions,
|
|
465
|
-
};
|
|
466
|
-
use crate::Engine;
|
|
467
|
-
use futures_util::task::noop_waker_ref;
|
|
468
|
-
|
|
469
|
-
const TEST_WAIT_TIMEOUT: Duration = Duration::from_secs(2);
|
|
470
|
-
|
|
471
|
-
fn wait_until(description: &str, mut condition: impl FnMut() -> bool) {
|
|
472
|
-
let deadline = Instant::now() + TEST_WAIT_TIMEOUT;
|
|
473
|
-
while !condition() {
|
|
474
|
-
assert!(
|
|
475
|
-
Instant::now() < deadline,
|
|
476
|
-
"timed out waiting for {description}"
|
|
477
|
-
);
|
|
478
|
-
thread::yield_now();
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
fn assert_close_pending<F>(mut future: Pin<&mut F>)
|
|
483
|
-
where
|
|
484
|
-
F: Future<Output = Result<(), crate::LixError>>,
|
|
485
|
-
{
|
|
486
|
-
let mut cx = Context::from_waker(noop_waker_ref());
|
|
487
|
-
assert!(
|
|
488
|
-
matches!(future.as_mut().poll(&mut cx), Poll::Pending),
|
|
489
|
-
"close should remain pending while guarded work is in progress"
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
async fn assert_close_finishes<F>(future: Pin<&mut F>, description: &str)
|
|
494
|
-
where
|
|
495
|
-
F: Future<Output = Result<(), crate::LixError>>,
|
|
496
|
-
{
|
|
497
|
-
tokio::time::timeout(TEST_WAIT_TIMEOUT, future)
|
|
498
|
-
.await
|
|
499
|
-
.unwrap_or_else(|_| panic!("timed out waiting for {description}"))
|
|
500
|
-
.unwrap_or_else(|error| panic!("{description} failed: {error:?}"));
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
fn join_thread<T>(handle: thread::JoinHandle<T>, description: &str) -> T {
|
|
504
|
-
wait_until(description, || handle.is_finished());
|
|
505
|
-
match handle.join() {
|
|
506
|
-
Ok(result) => result,
|
|
507
|
-
Err(_) => panic!("{description} panicked"),
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
async fn open_session() -> std::sync::Arc<super::SessionContext<InMemoryBackend>> {
|
|
512
|
-
let backend = InMemoryBackend::default();
|
|
513
|
-
let _receipt = Engine::initialize(backend.clone())
|
|
514
|
-
.await
|
|
515
|
-
.expect("backend should initialize");
|
|
516
|
-
let engine = Engine::new(backend)
|
|
517
|
-
.await
|
|
518
|
-
.expect("initialized backend should create engine");
|
|
519
|
-
std::sync::Arc::new(
|
|
520
|
-
engine
|
|
521
|
-
.open_workspace_session()
|
|
522
|
-
.await
|
|
523
|
-
.expect("workspace session should open"),
|
|
524
|
-
)
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
async fn open_blocking_read_session() -> (
|
|
528
|
-
std::sync::Arc<super::SessionContext<BlockingBeginReadBackend>>,
|
|
529
|
-
BlockingGate,
|
|
530
|
-
) {
|
|
531
|
-
let backend = BlockingBeginReadBackend::new();
|
|
532
|
-
let gate = backend.gate();
|
|
533
|
-
let _receipt = Engine::initialize(backend.clone())
|
|
534
|
-
.await
|
|
535
|
-
.expect("backend should initialize");
|
|
536
|
-
let engine = Engine::new(backend)
|
|
537
|
-
.await
|
|
538
|
-
.expect("initialized backend should create engine");
|
|
539
|
-
(
|
|
540
|
-
std::sync::Arc::new(
|
|
541
|
-
engine
|
|
542
|
-
.open_workspace_session()
|
|
543
|
-
.await
|
|
544
|
-
.expect("workspace session should open"),
|
|
545
|
-
),
|
|
546
|
-
gate,
|
|
547
|
-
)
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
async fn open_blocking_write_session() -> (
|
|
551
|
-
std::sync::Arc<super::SessionContext<BlockingBeginWriteBackend>>,
|
|
552
|
-
BlockingGate,
|
|
553
|
-
) {
|
|
554
|
-
let backend = BlockingBeginWriteBackend::new();
|
|
555
|
-
let gate = backend.gate();
|
|
556
|
-
let _receipt = Engine::initialize(backend.clone())
|
|
557
|
-
.await
|
|
558
|
-
.expect("backend should initialize");
|
|
559
|
-
let engine = Engine::new(backend)
|
|
560
|
-
.await
|
|
561
|
-
.expect("initialized backend should create engine");
|
|
562
|
-
(
|
|
563
|
-
std::sync::Arc::new(
|
|
564
|
-
engine
|
|
565
|
-
.open_workspace_session()
|
|
566
|
-
.await
|
|
567
|
-
.expect("workspace session should open"),
|
|
568
|
-
),
|
|
569
|
-
gate,
|
|
570
|
-
)
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
#[tokio::test]
|
|
574
|
-
async fn close_waits_for_session_operation_guard_to_drop() {
|
|
575
|
-
let session = open_session().await;
|
|
576
|
-
let guard = session
|
|
577
|
-
.begin_session_operation()
|
|
578
|
-
.expect("session operation should begin");
|
|
579
|
-
let mut close = Box::pin(session.close());
|
|
580
|
-
assert_close_pending(close.as_mut());
|
|
581
|
-
|
|
582
|
-
drop(guard);
|
|
583
|
-
assert_close_finishes(close.as_mut(), "close after operation guard drops").await;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
#[tokio::test]
|
|
587
|
-
async fn close_waits_for_commit_guard_to_drop() {
|
|
588
|
-
let session = open_session().await;
|
|
589
|
-
let guard = session.begin_commit();
|
|
590
|
-
let mut close = Box::pin(session.close());
|
|
591
|
-
assert_close_pending(close.as_mut());
|
|
592
|
-
|
|
593
|
-
drop(guard);
|
|
594
|
-
assert_close_finishes(close.as_mut(), "close after commit guard drops").await;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
#[tokio::test]
|
|
598
|
-
async fn session_read_execute_holds_operation_guard() {
|
|
599
|
-
let session = open_session().await;
|
|
600
|
-
let result = session
|
|
601
|
-
.execute("SELECT 1", &[])
|
|
602
|
-
.await
|
|
603
|
-
.expect("read should succeed");
|
|
604
|
-
assert_eq!(result.len(), 1);
|
|
605
|
-
assert_eq!(session.operation_in_progress_count_for_test(), 0);
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
#[tokio::test]
|
|
609
|
-
async fn active_transaction_read_execute_holds_operation_guard() {
|
|
610
|
-
let session = open_session().await;
|
|
611
|
-
let mut transaction = session
|
|
612
|
-
.begin_transaction()
|
|
613
|
-
.await
|
|
614
|
-
.expect("transaction should begin");
|
|
615
|
-
assert!(session.active_transaction_for_test());
|
|
616
|
-
let result = transaction
|
|
617
|
-
.execute("SELECT 1", &[])
|
|
618
|
-
.await
|
|
619
|
-
.expect("transaction read should succeed");
|
|
620
|
-
assert_eq!(result.len(), 1);
|
|
621
|
-
assert_eq!(session.operation_in_progress_count_for_test(), 1);
|
|
622
|
-
assert!(session.active_transaction_for_test());
|
|
623
|
-
transaction
|
|
624
|
-
.rollback()
|
|
625
|
-
.await
|
|
626
|
-
.expect("transaction rollback should succeed");
|
|
627
|
-
assert_eq!(session.operation_in_progress_count_for_test(), 0);
|
|
628
|
-
assert!(!session.active_transaction_for_test());
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
#[tokio::test]
|
|
632
|
-
async fn close_rejects_idle_explicit_transaction_without_waiting() {
|
|
633
|
-
let session = open_session().await;
|
|
634
|
-
let transaction = session
|
|
635
|
-
.begin_transaction()
|
|
636
|
-
.await
|
|
637
|
-
.expect("transaction should begin");
|
|
638
|
-
|
|
639
|
-
let error = session
|
|
640
|
-
.close()
|
|
641
|
-
.await
|
|
642
|
-
.expect_err("close should reject an idle explicit transaction");
|
|
643
|
-
assert_eq!(error.code, "LIX_INVALID_TRANSACTION_STATE");
|
|
644
|
-
|
|
645
|
-
transaction
|
|
646
|
-
.rollback()
|
|
647
|
-
.await
|
|
648
|
-
.expect("rollback should remain available after rejected close");
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
#[tokio::test]
|
|
652
|
-
async fn transaction_open_waits_for_write_lock() {
|
|
653
|
-
let session = open_session().await;
|
|
654
|
-
let write_guard = session.write_lock.lock_owned().await;
|
|
655
|
-
|
|
656
|
-
let opener_session = std::sync::Arc::clone(&session);
|
|
657
|
-
let opener = thread::spawn(move || {
|
|
658
|
-
let runtime = tokio::runtime::Builder::new_current_thread()
|
|
659
|
-
.build()
|
|
660
|
-
.expect("test runtime should build");
|
|
661
|
-
runtime.block_on(async move { opener_session.begin_transaction().await })
|
|
662
|
-
});
|
|
663
|
-
wait_until("explicit transaction open to reserve the session", || {
|
|
664
|
-
session.operation_in_progress_count_for_test() > 0
|
|
665
|
-
&& session.active_transaction_for_test()
|
|
666
|
-
&& !opener.is_finished()
|
|
667
|
-
});
|
|
668
|
-
|
|
669
|
-
assert!(
|
|
670
|
-
!opener.is_finished(),
|
|
671
|
-
"transaction open should wait for the write lock"
|
|
672
|
-
);
|
|
673
|
-
assert!(session.active_transaction_for_test());
|
|
674
|
-
|
|
675
|
-
drop(write_guard);
|
|
676
|
-
let transaction = join_thread(opener, "queued transaction opener")
|
|
677
|
-
.expect("transaction should begin after write lock is released");
|
|
678
|
-
transaction
|
|
679
|
-
.rollback()
|
|
680
|
-
.await
|
|
681
|
-
.expect("transaction rollback should succeed");
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
#[tokio::test]
|
|
685
|
-
async fn close_waits_for_session_write_queued_on_write_lock() {
|
|
686
|
-
let session = open_session().await;
|
|
687
|
-
let write_guard = session.write_lock.lock_owned().await;
|
|
688
|
-
|
|
689
|
-
let writer_session = std::sync::Arc::clone(&session);
|
|
690
|
-
let writer = thread::spawn(move || {
|
|
691
|
-
let runtime = tokio::runtime::Builder::new_current_thread()
|
|
692
|
-
.build()
|
|
693
|
-
.expect("test runtime should build");
|
|
694
|
-
runtime.block_on(async move {
|
|
695
|
-
writer_session
|
|
696
|
-
.execute(
|
|
697
|
-
"INSERT INTO lix_key_value (key, value) VALUES ('queued-write-close', 'value')",
|
|
698
|
-
&[],
|
|
699
|
-
)
|
|
700
|
-
.await
|
|
701
|
-
})
|
|
702
|
-
});
|
|
703
|
-
wait_until("queued session write to reserve the session", || {
|
|
704
|
-
session.operation_in_progress_count_for_test() > 0
|
|
705
|
-
&& session.active_transaction_for_test()
|
|
706
|
-
});
|
|
707
|
-
|
|
708
|
-
let mut close = Box::pin(session.close());
|
|
709
|
-
assert_close_pending(close.as_mut());
|
|
710
|
-
|
|
711
|
-
drop(write_guard);
|
|
712
|
-
let write_error =
|
|
713
|
-
join_thread(writer, "queued writer").expect_err("queued write should observe close");
|
|
714
|
-
assert_eq!(write_error.code, crate::LixError::CODE_CLOSED);
|
|
715
|
-
assert_close_finishes(close.as_mut(), "close after queued write exits").await;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
#[tokio::test]
|
|
719
|
-
async fn session_read_does_not_wait_for_write_lock() {
|
|
720
|
-
let session = open_session().await;
|
|
721
|
-
let write_guard = session.write_lock.lock_owned().await;
|
|
722
|
-
|
|
723
|
-
let result = tokio::time::timeout(TEST_WAIT_TIMEOUT, session.execute("SELECT 1", &[]))
|
|
724
|
-
.await
|
|
725
|
-
.expect("read should not wait for the write lock")
|
|
726
|
-
.expect("read should succeed");
|
|
727
|
-
|
|
728
|
-
assert_eq!(result.len(), 1);
|
|
729
|
-
drop(write_guard);
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
#[tokio::test]
|
|
733
|
-
async fn explicit_transaction_commit_sets_commit_guard() {
|
|
734
|
-
let session = open_session().await;
|
|
735
|
-
let mut transaction = session
|
|
736
|
-
.begin_transaction()
|
|
737
|
-
.await
|
|
738
|
-
.expect("transaction should begin");
|
|
739
|
-
transaction
|
|
740
|
-
.execute(
|
|
741
|
-
"INSERT INTO lix_key_value (key, value) VALUES ('commit-guard-test', 'value')",
|
|
742
|
-
&[],
|
|
743
|
-
)
|
|
744
|
-
.await
|
|
745
|
-
.expect("transaction write should stage");
|
|
746
|
-
transaction
|
|
747
|
-
.commit()
|
|
748
|
-
.await
|
|
749
|
-
.expect("transaction commit should succeed");
|
|
750
|
-
assert!(!session.commit_in_progress_for_test());
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
#[tokio::test]
|
|
754
|
-
async fn close_waits_for_explicit_transaction_open_queued_on_write_lock() {
|
|
755
|
-
let session = open_session().await;
|
|
756
|
-
let write_guard = session.write_lock.lock_owned().await;
|
|
757
|
-
|
|
758
|
-
let opener_session = std::sync::Arc::clone(&session);
|
|
759
|
-
let opener = thread::spawn(move || {
|
|
760
|
-
let runtime = tokio::runtime::Builder::new_current_thread()
|
|
761
|
-
.build()
|
|
762
|
-
.expect("test runtime should build");
|
|
763
|
-
runtime.block_on(async move { opener_session.begin_transaction().await })
|
|
764
|
-
});
|
|
765
|
-
wait_until("explicit transaction open to queue on write lock", || {
|
|
766
|
-
session.operation_in_progress_count_for_test() > 0
|
|
767
|
-
&& session.active_transaction_for_test()
|
|
768
|
-
&& !opener.is_finished()
|
|
769
|
-
});
|
|
770
|
-
assert!(
|
|
771
|
-
!opener.is_finished(),
|
|
772
|
-
"transaction open should still be queued on write lock"
|
|
773
|
-
);
|
|
774
|
-
|
|
775
|
-
let mut close = Box::pin(session.close());
|
|
776
|
-
assert_close_pending(close.as_mut());
|
|
777
|
-
|
|
778
|
-
drop(write_guard);
|
|
779
|
-
let open_error = match join_thread(opener, "queued explicit transaction opener") {
|
|
780
|
-
Ok(_) => panic!("queued explicit transaction open should observe close"),
|
|
781
|
-
Err(error) => error,
|
|
782
|
-
};
|
|
783
|
-
assert_eq!(open_error.code, crate::LixError::CODE_CLOSED);
|
|
784
|
-
assert_close_finishes(close.as_mut(), "close after queued explicit open exits").await;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
#[tokio::test]
|
|
788
|
-
async fn close_waits_for_session_read_blocked_in_backend_read() {
|
|
789
|
-
let (session, gate) = open_blocking_read_session().await;
|
|
790
|
-
|
|
791
|
-
gate.block_next();
|
|
792
|
-
let reader_session = std::sync::Arc::clone(&session);
|
|
793
|
-
let reader = std::thread::spawn(move || {
|
|
794
|
-
let runtime = tokio::runtime::Builder::new_current_thread()
|
|
795
|
-
.build()
|
|
796
|
-
.expect("test runtime should build");
|
|
797
|
-
runtime.block_on(async move { reader_session.execute("SELECT 1", &[]).await })
|
|
798
|
-
});
|
|
799
|
-
gate.wait_until_blocked();
|
|
800
|
-
|
|
801
|
-
let mut close = Box::pin(session.close());
|
|
802
|
-
assert_close_pending(close.as_mut());
|
|
803
|
-
|
|
804
|
-
gate.release();
|
|
805
|
-
let error = join_thread(reader, "blocked reader")
|
|
806
|
-
.expect_err("read should observe close after backend read resumes");
|
|
807
|
-
assert_eq!(error.code, crate::LixError::CODE_CLOSED);
|
|
808
|
-
assert_close_finishes(close.as_mut(), "close after blocked read exits").await;
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
#[tokio::test]
|
|
812
|
-
async fn close_rejects_active_transaction_read_blocked_in_backend_read() {
|
|
813
|
-
let (session, gate) = open_blocking_read_session().await;
|
|
814
|
-
let mut transaction = session
|
|
815
|
-
.begin_transaction()
|
|
816
|
-
.await
|
|
817
|
-
.expect("transaction should begin");
|
|
818
|
-
|
|
819
|
-
gate.block_next();
|
|
820
|
-
let reader = std::thread::spawn(move || {
|
|
821
|
-
let runtime = tokio::runtime::Builder::new_current_thread()
|
|
822
|
-
.build()
|
|
823
|
-
.expect("test runtime should build");
|
|
824
|
-
runtime.block_on(async move { transaction.execute("SELECT 1", &[]).await })
|
|
825
|
-
});
|
|
826
|
-
gate.wait_until_blocked();
|
|
827
|
-
|
|
828
|
-
let close_error = session
|
|
829
|
-
.close()
|
|
830
|
-
.await
|
|
831
|
-
.expect_err("close should reject an active explicit transaction read");
|
|
832
|
-
assert_eq!(close_error.code, "LIX_INVALID_TRANSACTION_STATE");
|
|
833
|
-
|
|
834
|
-
gate.release();
|
|
835
|
-
let result = join_thread(reader, "blocked transaction reader")
|
|
836
|
-
.expect("in-flight transaction read should finish after rejected close");
|
|
837
|
-
assert_eq!(result.len(), 1);
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
#[tokio::test]
|
|
841
|
-
async fn close_waits_for_explicit_transaction_blocked_in_backend_commit() {
|
|
842
|
-
let (session, gate) = open_blocking_write_session().await;
|
|
843
|
-
let mut transaction = session
|
|
844
|
-
.begin_transaction()
|
|
845
|
-
.await
|
|
846
|
-
.expect("transaction should begin");
|
|
847
|
-
transaction
|
|
848
|
-
.execute(
|
|
849
|
-
"INSERT INTO lix_key_value (key, value) VALUES ('blocked-commit', 'value')",
|
|
850
|
-
&[],
|
|
851
|
-
)
|
|
852
|
-
.await
|
|
853
|
-
.expect("transaction write should stage");
|
|
854
|
-
|
|
855
|
-
gate.block_next();
|
|
856
|
-
let committer = std::thread::spawn(move || {
|
|
857
|
-
let runtime = tokio::runtime::Builder::new_current_thread()
|
|
858
|
-
.build()
|
|
859
|
-
.expect("test runtime should build");
|
|
860
|
-
runtime.block_on(async move { transaction.commit().await })
|
|
861
|
-
});
|
|
862
|
-
gate.wait_until_blocked();
|
|
863
|
-
assert!(
|
|
864
|
-
session.commit_in_progress_for_test(),
|
|
865
|
-
"blocked explicit transaction commit should set the commit guard"
|
|
866
|
-
);
|
|
867
|
-
|
|
868
|
-
let mut close = Box::pin(session.close());
|
|
869
|
-
assert_close_pending(close.as_mut());
|
|
870
|
-
|
|
871
|
-
gate.release();
|
|
872
|
-
join_thread(committer, "blocked committer")
|
|
873
|
-
.expect("commit already at durable boundary should finish");
|
|
874
|
-
assert_close_finishes(close.as_mut(), "close after commit exits").await;
|
|
875
|
-
assert!(
|
|
876
|
-
!session.commit_in_progress_for_test(),
|
|
877
|
-
"commit guard should clear after the blocked commit exits"
|
|
878
|
-
);
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
#[derive(Clone)]
|
|
882
|
-
struct BlockingBeginReadBackend {
|
|
883
|
-
inner: InMemoryBackend,
|
|
884
|
-
gate: BlockingGate,
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
impl BlockingBeginReadBackend {
|
|
888
|
-
fn new() -> Self {
|
|
889
|
-
Self {
|
|
890
|
-
inner: InMemoryBackend::default(),
|
|
891
|
-
gate: BlockingGate::new(),
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
fn gate(&self) -> BlockingGate {
|
|
896
|
-
self.gate.clone()
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
impl Backend for BlockingBeginReadBackend {
|
|
901
|
-
type Read<'a>
|
|
902
|
-
= InMemoryRead
|
|
903
|
-
where
|
|
904
|
-
Self: 'a;
|
|
905
|
-
|
|
906
|
-
type Write<'a>
|
|
907
|
-
= InMemoryWrite
|
|
908
|
-
where
|
|
909
|
-
Self: 'a;
|
|
910
|
-
|
|
911
|
-
fn capabilities(&self) -> BackendCapabilities {
|
|
912
|
-
self.inner.capabilities()
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
fn begin_read(&self, opts: ReadOptions) -> Result<Self::Read<'_>, BackendError> {
|
|
916
|
-
self.gate.maybe_block();
|
|
917
|
-
self.inner.begin_read(opts)
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
fn begin_write(&self, opts: WriteOptions) -> Result<Self::Write<'_>, BackendError> {
|
|
921
|
-
self.inner.begin_write(opts)
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
fn durable_write_lock(&self) -> DurableWriteLock {
|
|
925
|
-
self.inner.durable_write_lock()
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
#[derive(Clone)]
|
|
930
|
-
struct BlockingBeginWriteBackend {
|
|
931
|
-
inner: InMemoryBackend,
|
|
932
|
-
gate: BlockingGate,
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
impl BlockingBeginWriteBackend {
|
|
936
|
-
fn new() -> Self {
|
|
937
|
-
Self {
|
|
938
|
-
inner: InMemoryBackend::default(),
|
|
939
|
-
gate: BlockingGate::new(),
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
fn gate(&self) -> BlockingGate {
|
|
944
|
-
self.gate.clone()
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
impl Backend for BlockingBeginWriteBackend {
|
|
949
|
-
type Read<'a>
|
|
950
|
-
= InMemoryRead
|
|
951
|
-
where
|
|
952
|
-
Self: 'a;
|
|
953
|
-
|
|
954
|
-
type Write<'a>
|
|
955
|
-
= InMemoryWrite
|
|
956
|
-
where
|
|
957
|
-
Self: 'a;
|
|
958
|
-
|
|
959
|
-
fn capabilities(&self) -> BackendCapabilities {
|
|
960
|
-
self.inner.capabilities()
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
fn begin_read(&self, opts: ReadOptions) -> Result<Self::Read<'_>, BackendError> {
|
|
964
|
-
self.inner.begin_read(opts)
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
fn begin_write(&self, opts: WriteOptions) -> Result<Self::Write<'_>, BackendError> {
|
|
968
|
-
self.gate.maybe_block();
|
|
969
|
-
self.inner.begin_write(opts)
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
fn durable_write_lock(&self) -> DurableWriteLock {
|
|
973
|
-
self.inner.durable_write_lock()
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
#[derive(Clone)]
|
|
978
|
-
struct BlockingGate {
|
|
979
|
-
state: std::sync::Arc<(Mutex<BlockingGateState>, Condvar)>,
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
impl BlockingGate {
|
|
983
|
-
fn new() -> Self {
|
|
984
|
-
Self {
|
|
985
|
-
state: std::sync::Arc::new((
|
|
986
|
-
Mutex::new(BlockingGateState::default()),
|
|
987
|
-
Condvar::new(),
|
|
988
|
-
)),
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
fn block_next(&self) {
|
|
993
|
-
let (lock, _) = &*self.state;
|
|
994
|
-
let mut state = lock.lock().expect("blocking gate lock should not poison");
|
|
995
|
-
state.block_next = true;
|
|
996
|
-
state.blocked = false;
|
|
997
|
-
state.released = false;
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
fn maybe_block(&self) {
|
|
1001
|
-
let (lock, condvar) = &*self.state;
|
|
1002
|
-
let mut state = lock.lock().expect("blocking gate lock should not poison");
|
|
1003
|
-
if !state.block_next {
|
|
1004
|
-
return;
|
|
1005
|
-
}
|
|
1006
|
-
state.block_next = false;
|
|
1007
|
-
state.blocked = true;
|
|
1008
|
-
condvar.notify_all();
|
|
1009
|
-
let deadline = Instant::now() + TEST_WAIT_TIMEOUT;
|
|
1010
|
-
while !state.released {
|
|
1011
|
-
let remaining = deadline.saturating_duration_since(Instant::now());
|
|
1012
|
-
assert!(
|
|
1013
|
-
!remaining.is_zero(),
|
|
1014
|
-
"timed out waiting for blocking gate release"
|
|
1015
|
-
);
|
|
1016
|
-
let (next_state, wait_result) = condvar
|
|
1017
|
-
.wait_timeout(state, remaining)
|
|
1018
|
-
.expect("blocking gate lock should not poison after wait");
|
|
1019
|
-
state = next_state;
|
|
1020
|
-
assert!(
|
|
1021
|
-
!wait_result.timed_out() || state.released,
|
|
1022
|
-
"timed out waiting for blocking gate release"
|
|
1023
|
-
);
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
fn wait_until_blocked(&self) {
|
|
1028
|
-
let (lock, condvar) = &*self.state;
|
|
1029
|
-
let mut state = lock.lock().expect("blocking gate lock should not poison");
|
|
1030
|
-
let deadline = Instant::now() + TEST_WAIT_TIMEOUT;
|
|
1031
|
-
while !state.blocked {
|
|
1032
|
-
let remaining = deadline.saturating_duration_since(Instant::now());
|
|
1033
|
-
assert!(!remaining.is_zero(), "timed out waiting for blocking gate");
|
|
1034
|
-
let (next_state, wait_result) = condvar
|
|
1035
|
-
.wait_timeout(state, remaining)
|
|
1036
|
-
.expect("blocking gate lock should not poison after wait");
|
|
1037
|
-
state = next_state;
|
|
1038
|
-
assert!(
|
|
1039
|
-
!wait_result.timed_out() || state.blocked,
|
|
1040
|
-
"timed out waiting for blocking gate"
|
|
1041
|
-
);
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
fn release(&self) {
|
|
1046
|
-
let (lock, condvar) = &*self.state;
|
|
1047
|
-
let mut state = lock.lock().expect("blocking gate lock should not poison");
|
|
1048
|
-
state.released = true;
|
|
1049
|
-
condvar.notify_all();
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
#[derive(Default)]
|
|
1054
|
-
struct BlockingGateState {
|
|
1055
|
-
block_next: bool,
|
|
1056
|
-
blocked: bool,
|
|
1057
|
-
released: bool,
|
|
1058
|
-
}
|
|
1059
|
-
}
|