Skip to content
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

Refactor - LoadedProgramType::Loaded and LoadedProgramOwner #606

Merged
merged 2 commits into from
Apr 17, 2024

Conversation

Lichtso
Copy link

@Lichtso Lichtso commented Apr 5, 2024

Problems

  1. The loaded program tests currently only test themselves (with LoadedProgramType::TestLoaded) instead of the production code.
  2. The account owner / loader is monomorphized into LegacyV0, LegacyV1 and Typed, which has to be undone everywhere the carried executable is needed. It should be a separate field of LoadedProgram instead and that could also be used by other LoadedProgramTypes.

Summary of Changes

  • Unifies LoadedProgramTypes LegacyV0, LegacyV1, Typed and TestLoaded into Loaded.
  • Adds LoadedProgram::account_owner() and LoadedProgramOwner. Using the pubkey of the loader directly was also an option, but I didn't see any reason to waste storing and comparing 256 bits when there can be at most log_2(5) ~ 2.3219 bits of entropy.

@Lichtso Lichtso force-pushed the refactor/remove_unloaded branch 2 times, most recently from 4199d18 to 26e2adf Compare April 9, 2024 12:35
@dmakarov
Copy link

dmakarov commented Apr 9, 2024

The account owner / loader is monomorphized into LegacyV0, LegacyV1 and Typed, which has to be undone everywhere the carried executable is needed.

Could you, please, explain in plain English what this means?

@Lichtso
Copy link
Author

Lichtso commented Apr 9, 2024

What I meant is that instead of a generic LoadedProgramType::Loaded which would be paried with LoadedProgramOwner like so:

  • program_type: LoadedProgramType::Loaded(executable), account_owner: LoadedProgramOwner::LoaderV1
  • program_type: LoadedProgramType::Loaded(executable), account_owner: LoadedProgramOwner::LoaderV2
  • program_type: LoadedProgramType::Loaded(executable), account_owner: LoadedProgramOwner::LoaderV3
  • program_type: LoadedProgramType::Loaded(executable), account_owner: LoadedProgramOwner::LoaderV4

we instead have a combined one (basically a form of monomorphization):

  • program_type: LoadedProgramType::LegacyV1(executable)
  • program_type: LoadedProgramType::LegacyV1(executable)
  • program_type: LoadedProgramType::LegacyV2(executable)
  • program_type: LoadedProgramType::Typed(executable)

So whenever you want to get the contained executable, you have to explicitly match program_type to destructure them:

  • LoadedProgramType::LegacyV1(executable) => executable,
  • LoadedProgramType::LegacyV2(executable) => executable,
  • LoadedProgramType::Typed(executable) => executable,

@codecov-commenter
Copy link

codecov-commenter commented Apr 9, 2024

Codecov Report

Attention: Patch coverage is 77.61194% with 30 lines in your changes are missing coverage. Please review.

Project coverage is 81.9%. Comparing base (499d36e) to head (e4aaf05).
Report is 63 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##           master     #606     +/-   ##
=========================================
- Coverage    81.9%    81.9%   -0.1%     
=========================================
  Files         851      851             
  Lines      231209   231288     +79     
=========================================
+ Hits       189495   189536     +41     
- Misses      41714    41752     +38     

#[cfg(test)]
TestLoaded(ProgramRuntimeEnvironment),
/// Verified and compiled program
Loaded(Executable<InvokeContext<'static>>),
Copy link

Choose a reason for hiding this comment

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

maybe not to address in this PR, but I think the enum name is misnomer. A LoadedProgramType value can be 'Loaded', 'Unloaded', or something else. It doesn't make sense when a 'loaded program' is in fact 'unloaded'. It also doesn't make sense that for a LoadedProgramType you add an enum value 'Loaded'. If only 'Loaded' programs are loaded, then what are other 'LoadedProgramType' values?

Copy link
Author

Choose a reason for hiding this comment

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

Yes you are right, it evolved over time and we never adjusted the naming.
How about renaming (in a different PR):

  • LoadedProgram => ProgramCacheEntry
  • LoadedProgramType => ProgramCacheEntryType

Copy link

Choose a reason for hiding this comment

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

Definitely, sounds better. I don't insist that these name changes should be in this PR. It's up to you.

@dmakarov
Copy link

dmakarov commented Apr 9, 2024

What I meant is that instead of a generic LoadedProgramType::Loaded which would be paried with LoadedProgramOwner like so:

* `program_type: LoadedProgramType::Loaded(executable), account_owner: LoadedProgramOwner::LoaderV1`

* `program_type: LoadedProgramType::Loaded(executable), account_owner: LoadedProgramOwner::LoaderV2`

* `program_type: LoadedProgramType::Loaded(executable), account_owner: LoadedProgramOwner::LoaderV3`

* `program_type: LoadedProgramType::Loaded(executable), account_owner: LoadedProgramOwner::LoaderV4`

we instead have a combined one (basically a form of monomorphization):

* `program_type: LoadedProgramType::LegacyV1(executable)`

* `program_type: LoadedProgramType::LegacyV1(executable)`

* `program_type: LoadedProgramType::LegacyV2(executable)`

* `program_type: LoadedProgramType::Typed(executable)`

So whenever you want to get the contained executable, you have to explicitly match program_type to destructure them:

* `LoadedProgramType::LegacyV1(executable) => executable,`

* `LoadedProgramType::LegacyV2(executable) => executable,`

* `LoadedProgramType::Typed(executable) => executable,`

Thanks for the explanation. I'm not convinced that monomorphization is applicable nomenclature in this situation. As far as I understand monomorphization is performed by compiler if a trait can be resolved to a concrete type at compile time, when static dispatch is used.

@Lichtso
Copy link
Author

Lichtso commented Apr 9, 2024

It might be a generous view of what "monomorphization" includes, but IMO it means instantiating generics by giving them a distinct code path each. And because the enum items have individual code paths it made sense to me.

@Lichtso Lichtso force-pushed the refactor/remove_unloaded branch 2 times, most recently from 3339803 to cf8f6ba Compare April 12, 2024 08:53
Merges LoadedProgramTypes LegacyV0, LegacyV1, Typed and TestLoaded into Loaded.
pgarg66
pgarg66 previously approved these changes Apr 16, 2024
alessandrod
alessandrod previously approved these changes Apr 16, 2024
Copy link

@alessandrod alessandrod left a comment

Choose a reason for hiding this comment

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

Sorry it took so long to review!

I love this, we're testing more, code looks better and logic is simpler. Thanks a lot for doing it.

I only left a few nits, otherwise looks great.

LoadedProgramType::LegacyV0(executable)
} else if bpf_loader::check_id(loader_key) || bpf_loader_upgradeable::check_id(loader_key) {
LoadedProgramType::LegacyV1(executable)
let account_owner = if bpf_loader_deprecated::check_id(loader_key) {

Choose a reason for hiding this comment

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

nit: this is LoadedProgramOwner::try_from(loader_key)

@@ -393,6 +391,7 @@ impl LoadedProgram {
}
Some(Self {
program: LoadedProgramType::Unloaded(self.program.get_environment()?.clone()),
account_owner: self.account_owner.clone(),

Choose a reason for hiding this comment

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

nit: the owner type should probably be Copy?

@@ -464,6 +469,16 @@ impl LoadedProgram {
let decaying_for = std::cmp::min(63, now.saturating_sub(last_access));
self.tx_usage_counter.load(Ordering::Relaxed) >> decaying_for
}

pub fn account_owner(&self) -> Pubkey {
match self.account_owner {

Choose a reason for hiding this comment

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

nit: this is Into for LoadedProgramOwner

Some((*id, program.clone()))
LoadedProgramType::Loaded(_) => {
let include =
if let LoadedProgramOwner::LoaderV4 = program.account_owner {

Choose a reason for hiding this comment

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

nit: if you implement Eq for LoadedProgramOwner then I think this can become one
if with LoaderV4 && include_program_runtime_v2 { .. } else if
include_program_runtime_v1 { ... }

@Lichtso Lichtso dismissed stale reviews from alessandrod and pgarg66 via ff96903 April 17, 2024 07:21
@Lichtso Lichtso merged commit ac36dbb into anza-xyz:master Apr 17, 2024
38 checks passed
@Lichtso Lichtso deleted the refactor/remove_unloaded branch April 17, 2024 15:48
michaelschem pushed a commit to michaelschem/agave that referenced this pull request Apr 20, 2024
…-xyz#606)

* Adds LoadedProgram::account_owner() and LoadedProgramOwner.
Merges LoadedProgramTypes LegacyV0, LegacyV1, Typed and TestLoaded into Loaded.

* Review feedback.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants