Skip to content

fix(st): add errdefer chain for epoch cache functions - #227

Merged
wemeetagain merged 4 commits into
mainfrom
GrapeBaBa/errdefer-epochcache
Mar 13, 2026
Merged

fix(st): add errdefer chain for epoch cache functions#227
wemeetagain merged 4 commits into
mainfrom
GrapeBaBa/errdefer-epochcache

Conversation

@GrapeBaBa

Copy link
Copy Markdown
Contributor

Motivation

Make createFromState release resources correctly when error thrown

Signed-off-by: Chen Kai <281165273grape@gmail.com>
Copilot AI review requested due to automatic review settings February 27, 2026 10:34
@GrapeBaBa
GrapeBaBa requested a review from a team as a code owner February 27, 2026 10:34
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the reliability of the EpochCache initialization process. By implementing a robust errdefer chain, it guarantees that all allocated resources are properly deallocated even when errors are encountered during the complex setup of the cache. This change improves the stability and prevents potential memory leaks in error scenarios.

Highlights

  • Improved Error Handling: Introduced a chain of errdefer statements within the createFromState function to ensure that resources are correctly released if an error occurs during the initialization of the EpochCache.
  • Refactored Resource Initialization: Extracted the initialization logic for EffectiveBalanceIncrementsRc, EpochShufflingRc, and SyncCommitteeCacheRc into dedicated private helper functions, each incorporating its own errdefer for robust cleanup.
  • Simplified createFromState Logic: The main createFromState function now calls these new helper functions, making its structure cleaner and more focused on orchestrating the cache creation process while delegating resource management to the helpers.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/state_transition/cache/epoch_cache.zig
    • Added initEffectiveBalanceIncrementsRc function to initialize EffectiveBalanceIncrementsRc with errdefer.
    • Added initEpochShufflingRc function to initialize EpochShufflingRc with errdefer.
    • Added initCurrentSyncCommitteeCacheRc function to initialize SyncCommitteeCacheRc for the current sync committee with errdefer.
    • Added initNextSyncCommitteeCacheRc function to initialize SyncCommitteeCacheRc for the next sync committee with errdefer.
    • Refactored createFromState to use the new init...Rc helper functions for effective_balance_increments, previous_shuffling, current_shuffling, next_shuffling, current_sync_committee_indexed, and next_sync_committee_indexed.
    • Updated access to active_indices from current_shuffling to current_shuffling_rc.get().
Activity
  • No specific activity has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors createFromState to improve resource management during error scenarios by introducing helper functions that encapsulate resource initialization and cleanup using errdefer. This is a solid improvement for safety and directly addresses the PR's motivation. My review includes suggestions to further enhance maintainability by reducing code duplication and to align the new code with the repository's style guide concerning assertions and code formatting for resource management.

Comment on lines +140 to +141
var effective_balance_increments = try effectiveBalanceIncrementsInit(allocator, validator_count);
errdefer effective_balance_increments.deinit();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The style guide recommends using newlines to visually group resource allocation and deallocation. Please add a blank line before the resource allocation on line 140 to adhere to this rule. This comment also applies to the other new helper functions in this file where resource allocation is the first statement.

References
  1. Use newlines to group resource allocation and deallocation, i.e. before the resource allocation and after the corresponding defer statement, to make leaks easier to spot. (link)

Comment on lines +139 to +144
fn initEffectiveBalanceIncrementsRc(allocator: Allocator, validator_count: usize) !*EffectiveBalanceIncrementsRc {
var effective_balance_increments = try effectiveBalanceIncrementsInit(allocator, validator_count);
errdefer effective_balance_increments.deinit();

return try EffectiveBalanceIncrementsRc.init(allocator, effective_balance_increments);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The style guide requires asserting all function arguments. This function is missing assertions for its arguments, such as allocator and validator_count. Please add assertions to ensure correctness and adherence to the style guide. This also applies to the other new helper functions in this file.

References
  1. Assert all function arguments and return values, pre/postconditions and invariants. A function must not operate blindly on data it has not checked. (link)

Comment on lines +163 to +197
fn initCurrentSyncCommitteeCacheRc(
allocator: Allocator,
state: *AnyBeaconState,
pubkey_to_index: *const PubkeyIndexMap,
skip_sync_committee_cache: bool,
) !*SyncCommitteeCacheRc {
var sync_committee_cache = blk: {
if (skip_sync_committee_cache) break :blk SyncCommitteeCacheAllForks.initEmpty();
var sync_committee_view = try state.currentSyncCommittee();
var sync_committee: types.altair.SyncCommittee.Type = undefined;
try sync_committee_view.toValue(allocator, &sync_committee);
break :blk try SyncCommitteeCacheAllForks.initSyncCommittee(allocator, &sync_committee, pubkey_to_index);
};
errdefer sync_committee_cache.deinit();

return try SyncCommitteeCacheRc.init(allocator, sync_committee_cache);
}

fn initNextSyncCommitteeCacheRc(
allocator: Allocator,
state: *AnyBeaconState,
pubkey_to_index: *const PubkeyIndexMap,
skip_sync_committee_cache: bool,
) !*SyncCommitteeCacheRc {
var sync_committee_cache = blk: {
if (skip_sync_committee_cache) break :blk SyncCommitteeCacheAllForks.initEmpty();
var sync_committee_view = try state.nextSyncCommittee();
var sync_committee: types.altair.SyncCommittee.Type = undefined;
try sync_committee_view.toValue(allocator, &sync_committee);
break :blk try SyncCommitteeCacheAllForks.initSyncCommittee(allocator, &sync_committee, pubkey_to_index);
};
errdefer sync_committee_cache.deinit();

return try SyncCommitteeCacheRc.init(allocator, sync_committee_cache);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The functions initCurrentSyncCommitteeCacheRc and initNextSyncCommitteeCacheRc are nearly identical, with the only difference being the call to state.currentSyncCommittee() versus state.nextSyncCommittee(). To improve maintainability and reduce code duplication, consider refactoring this logic into a single helper function. This function could accept a function pointer to either currentSyncCommittee or nextSyncCommittee to handle both cases.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR improves error-path resource cleanup in EpochCache.createFromState by wrapping several intermediate allocations in reference-counted helpers and adding errdefer-based release chaining, so partially-initialized caches don’t leak when an error is thrown.

Changes:

  • Added helper initializers to build EffectiveBalanceIncrementsRc, EpochShufflingRc, and SyncCommitteeCacheRc with consistent errdefer cleanup.
  • Updated createFromState to use the new RC helpers and errdefer ...release() for safe unwinding on failure.
  • Simplified the final EpochCache struct initialization by directly storing the RC pointers created earlier.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/state_transition/cache/epoch_cache.zig Outdated
@GrapeBaBa

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ff33933ead

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/state_transition/cache/epoch_cache.zig Outdated
Signed-off-by: Chen Kai <281165273grape@gmail.com>
Signed-off-by: Chen Kai <281165273grape@gmail.com>
@GrapeBaBa GrapeBaBa changed the title fix(st): add errdefer chain for epoch cache init fix(st): add errdefer chain for epoch cache functions Feb 27, 2026
spiral-ladder
spiral-ladder previously approved these changes Mar 3, 2026

@spiral-ladder spiral-ladder left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This PR LGTM, but re-reading the implementation led me to a question in a comment

@@ -214,19 +279,31 @@ pub const EpochCache = struct {
}

// ownership of the active indices is transferred to EpochShuffling

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Re-reading this comment and thinking about how memory ownership works here again, but isn't this inaccurate if we're copying the items of the ArrayList into the EpochShuffling? Shouldn't we just use toOwnedSlice() here to pass into initEpochShufflingRc and take ownership of the items?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Re-reading the code, I feel you are right

@spiral-ladder spiral-ladder Mar 4, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

#229 fixing this here, but i think we can merge this current PR as is :shipit:

there was a misleading comment saying that `EpochShuffling` took
ownership of the `active_indices` even though the implementation copies.

This PR fixes that

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +843 to +851
errdefer next_sync_committee_indexed.deinit();

const next_sync_committee_indexed_rc = try SyncCommitteeCacheRc.init(self.allocator, next_sync_committee_indexed);
errdefer next_sync_committee_indexed_rc.release();

var current_sync_committee_indexed = try SyncCommitteeCacheAllForks.initValidatorIndices(self.allocator, next_sync_committee_indices);
errdefer current_sync_committee_indexed.deinit();

const current_sync_committee_indexed_rc = try SyncCommitteeCacheRc.init(self.allocator, current_sync_committee_indexed);

Copilot AI Mar 5, 2026

Copy link

Choose a reason for hiding this comment

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

In setSyncCommitteesIndexed, next_sync_committee_indexed and current_sync_committee_indexed each have errdefer ...deinit() set before being wrapped into SyncCommitteeCacheRc. If a later try fails after SyncCommitteeCacheRc.init succeeds (e.g., allocating the second cache), both the value deinit() and the RC release() will run on the same underlying allocation (the union copy still points at the same *SyncCommitteeCacheAltair), causing a double-free. Consider moving the value->RC wrapping into a small helper (similar to initCurrentSyncCommitteeCacheRc) so ownership transfer and cleanup are handled in one place, or explicitly disarm the earlier errdefer after successful RC init (e.g., by resetting the union to initEmpty() before any subsequent fallible operations).

Suggested change
errdefer next_sync_committee_indexed.deinit();
const next_sync_committee_indexed_rc = try SyncCommitteeCacheRc.init(self.allocator, next_sync_committee_indexed);
errdefer next_sync_committee_indexed_rc.release();
var current_sync_committee_indexed = try SyncCommitteeCacheAllForks.initValidatorIndices(self.allocator, next_sync_committee_indices);
errdefer current_sync_committee_indexed.deinit();
const current_sync_committee_indexed_rc = try SyncCommitteeCacheRc.init(self.allocator, current_sync_committee_indexed);
const next_sync_committee_indexed_rc = SyncCommitteeCacheRc.init(self.allocator, next_sync_committee_indexed) catch |e| {
next_sync_committee_indexed.deinit();
return e;
};
errdefer next_sync_committee_indexed_rc.release();
var current_sync_committee_indexed = try SyncCommitteeCacheAllForks.initValidatorIndices(self.allocator, next_sync_committee_indices);
const current_sync_committee_indexed_rc = SyncCommitteeCacheRc.init(self.allocator, current_sync_committee_indexed) catch |e| {
current_sync_committee_indexed.deinit();
return e;
};

Copilot uses AI. Check for mistakes.
@wemeetagain
wemeetagain merged commit 19745d4 into main Mar 13, 2026
14 checks passed
@wemeetagain
wemeetagain deleted the GrapeBaBa/errdefer-epochcache branch August 17, 2026 19:48
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.

4 participants