-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[execution] Add global context placeholder with guards #18639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
georgemitenkov
wants to merge
1
commit into
main
Choose a base branch
from
george/global-context
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+305
−0
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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>, | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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
185
third_party/move/mono-move/global-context/tests/context_tests.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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(); | ||
| 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)); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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, bothexecution_context()andmaintenance_context()returnOptionvalues that are silently assigned without.expect()or any assertion. If lock acquisition fails (returnsNone), 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)
third_party/move/mono-move/global-context/tests/context_tests.rs#L181-L182