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
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ members = [
"testsuite/smoke-test",
"testsuite/testcases",
"third_party/move/extensions/move-table-extension",
"third_party/move/mono-move/global-context",
"third_party/move/move-binary-format",
"third_party/move/move-binary-format/serializer-tests",
"third_party/move/move-borrow-graph",
Expand Down Expand Up @@ -865,6 +866,7 @@ x25519-dalek = { git = "https://github.com/aptos-labs/x25519-dalek", rev = "b9cd
z3tracer = "0.8.0"

# MOVE DEPENDENCIES
mono-move-global-context = { path = "third_party/move/mono-move/global-context" }
move-abigen = { path = "third_party/move/move-prover/move-abigen" }
move-asm = { path = "third_party/move/tools/move-asm" }
move-binary-format = { path = "third_party/move/move-binary-format" }
Expand Down
13 changes: 13 additions & 0 deletions third_party/move/mono-move/global-context/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "global-context"
version = "0.1.0"
authors = { workspace = true }
edition = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
publish = { workspace = true }
repository = { workspace = true }
rust-version = { workspace = true }

[dependencies]
parking_lot = { workspace = true }
85 changes: 85 additions & 0 deletions third_party/move/mono-move/global-context/src/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

//! Core types and implementation for the global execution context.

use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};

/// Placeholder struct for global context data.
#[derive(Debug, Default)]
struct Context {
// TODO: add data here
}

/// Global execution context with a two-phase state machine.
///
/// # Phases
///
/// 1. **Execution Phase**: Multiple [`ExecutionContext`] guards can be obtained
/// concurrently across threads. This allows parallel transaction execution
/// where each thread can read from shared caches and concurrently allocate
/// data.
///
/// 2. **Maintenance Phase**: A single [`MaintenanceContext`] guard provides
/// exclusive write access for inter-block maintenance operations such as
/// cache cleanup or data de-allocation.
pub struct GlobalContext {
inner: RwLock<Context>,
}

impl GlobalContext {
/// Creates a new global context with an empty internal state.
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
inner: RwLock::new(Context {}),
}
}

/// Transitions to execution mode by obtaining a read guard.
///
/// Multiple execution contexts can be held concurrently across
/// threads, enabling parallel transaction execution.
///
/// Returns [None] is a [`MaintenanceContext`] is currently held.
pub fn execution_context(&self) -> Option<ExecutionContext<'_>> {
Some(ExecutionContext {
guard: self.inner.try_read()?,
})
}

/// Transitions to maintenance mode by obtaining a write guard.
///
/// Only one maintenance context can be held at a time, providing
/// exclusive access to the internal state for maintenance operations.
///
/// Returns [None] is a [`ExecutionContext`] is currently held.
pub fn maintenance_context(&self) -> Option<MaintenanceContext<'_>> {
Some(MaintenanceContext {
guard: self.inner.try_write()?,
})
}
}

/// RAII guard for the execution phase providing concurrent read access
/// or data allocation.
///
/// Multiple execution contexts can exist simultaneously across threads,
/// allowing parallel transaction execution. The read lock is held for
/// the lifetime of this guard and automatically released when dropped.
#[derive(Debug)]
pub struct ExecutionContext<'a> {
#[allow(dead_code)]
guard: RwLockReadGuard<'a, Context>,
}

/// RAII guard for the maintenance phase providing exclusive write access.
///
/// Only one maintenance context can exist at a time, ensuring exclusive
/// access to the internal state for maintenance operations. The write lock
/// is held for the lifetime of this guard and automatically released when dropped.
#[derive(Debug)]
pub struct MaintenanceContext<'a> {
#[allow(dead_code)]
guard: RwLockWriteGuard<'a, Context>,
}
13 changes: 13 additions & 0 deletions third_party/move/mono-move/global-context/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

//! Global execution context for MonoMove.
//!
//! This crate provides a two-phase state machine for managing the global state:
//! - **Execution phase**: Multiple [`ExecutionContext`] guards can be held concurrently
//! across threads for parallel transaction execution.
//! - **Maintenance phase**: A single exclusive [`MaintenanceContext`] guard for inter-block
//! maintenance operations.

mod context;
pub use context::{ExecutionContext, GlobalContext, MaintenanceContext};
185 changes: 185 additions & 0 deletions third_party/move/mono-move/global-context/tests/context_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Copyright (c) Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

//! Integration tests for the global context.

use global_context::GlobalContext;
use std::{
sync::{Arc, Barrier},
thread,
time::Duration,
};

#[test]
fn test_different_contexts() {
let ctx = GlobalContext::new();

{
let _guard = ctx
.execution_context()
.expect("Execution context must be acquired");
}
{
let _guard = ctx
.maintenance_context()
.expect("Maintenance context must be acquired");
}
{
let _guard1 = ctx
.execution_context()
.expect("Execution context must be acquired");
let _guard2 = ctx
.execution_context()
.expect("Execution context must be acquired");
let _guard3 = ctx
.execution_context()
.expect("Execution context must be acquired");
}
}

#[test]
fn test_concurrent_execution_contexts() {
let ctx = Arc::new(GlobalContext::new());

let num_threads = 4;
let barrier = Arc::new(Barrier::new(num_threads));

let handles: Vec<_> = (0..num_threads)
.map(|_| {
let ctx = Arc::clone(&ctx);
let barrier = Arc::clone(&barrier);
thread::spawn(move || {
// Wait for all threads to be ready.
barrier.wait();

// All threads should be able to acquire execution context simultaneously.
let _guard = ctx
.execution_context()
.expect("Execution context must be acquired");
thread::sleep(Duration::from_millis(100));
})
})
.collect();

for handle in handles {
handle.join().unwrap();
}
}

#[test]
fn test_maintenance_blocks_maintenance() {
let ctx = Arc::new(GlobalContext::new());
let barrier = Arc::new(Barrier::new(2));

let handle = thread::spawn({
let ctx = Arc::clone(&ctx);
let barrier = Arc::clone(&barrier);
move || {
let _guard = ctx
.maintenance_context()
.expect("Maintenance context must be acquired");

// Signal that we have the lock.
barrier.wait();
thread::sleep(Duration::from_millis(2000));
}
});

// Wait for thread 1 to be in maintenance mode.
barrier.wait();
thread::sleep(Duration::from_millis(10));
assert!(ctx.maintenance_context().is_none());

handle.join().unwrap();
}

#[test]
fn test_maintenance_blocks_execution() {
let ctx = Arc::new(GlobalContext::new());
let barrier = Arc::new(Barrier::new(2));

// Thread 1: Hold execution context for 100ms
let handle1 = thread::spawn({
let ctx = Arc::clone(&ctx);
let barrier = Arc::clone(&barrier);
move || {
let _guard = ctx
.maintenance_context()
.expect("Maintenance context must be acquired");

// Signal that we have the lock.
barrier.wait();
thread::sleep(Duration::from_millis(2000));
}
});

// Wait for thread 1 to be in maintenance mode.
barrier.wait();
thread::sleep(Duration::from_millis(10));
assert!(ctx.execution_context().is_none());

handle1.join().unwrap();
}

#[test]
fn test_execution_blocks_maintenance() {
let num_threads = 4;

let ctx = Arc::new(GlobalContext::new());
let barrier = Arc::new(Barrier::new(num_threads + 1)); // +1 for main thread

// Spawn multiple threads holding execution contexts
let handles: Vec<_> = (0..num_threads)
.map(|_| {
let ctx = Arc::clone(&ctx);
let barrier = Arc::clone(&barrier);
thread::spawn(move || {
let _guard = ctx
.execution_context()
.expect("Execution context must be acquired");

// Signal that we have the lock.
barrier.wait();
thread::sleep(Duration::from_millis(2000));
})
})
.collect();

barrier.wait();
thread::sleep(Duration::from_millis(10));

assert!(ctx.maintenance_context().is_none());

for handle in handles {
handle.join().unwrap();
}
}

#[test]
fn test_block_execution_simulation() {
let num_threads = 4;
let num_iterations = 5;

let ctx = Arc::new(GlobalContext::new());

for _ in 0..num_iterations {
// Execution phase: concurrent execution.
let handles: Vec<_> = (0..num_threads)
.map(|_| {
let ctx = Arc::clone(&ctx);
thread::spawn(move || {
let _guard = ctx.execution_context();
Copy link

Choose a reason for hiding this comment

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

Simulation test silently ignores failed lock acquisitions

Medium Severity

In test_block_execution_simulation, both execution_context() and maintenance_context() return Option values that are silently assigned without .expect() or any assertion. If lock acquisition fails (returns None), the test passes without actually holding any guard, defeating the purpose. Every other test in this file uses .expect() to verify successful acquisition.

Additional Locations (1)

Fix in Cursor Fix in Web

thread::sleep(Duration::from_millis(100));
})
})
.collect();

for handle in handles {
handle.join().unwrap();
}

// Maintenance phase: single thread with exclusive access.
let _guard = ctx.maintenance_context();
thread::sleep(Duration::from_millis(100));
}
}
Loading