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

Prevent segfaults in ProcessWander when docking after flying around #298

Merged

Conversation

charles-m-knox
Copy link

@charles-m-knox charles-m-knox commented Oct 20, 2024

This PR prevents segfaults in SystemBubble::ProcessWander by untracking soon-to-be-destroyed (i.e. docking) SystemEntities from every bubble they were in recently, not just the bubble they currently reside within.


Explanation

After implementing the fixes in #295, a new bug was rearing its head. Every time I would dock after warping somewhere, the server would segfault within about 1 minute.

Turns out that SystemBubble::ProcessWander was what was triggering the segfault. It gets called every minute to clean up entities that are no longer present in each bubble.

ProcessWander iterates over a map, with each key/value pair containing an entity ID and the pointer to the underlying SystemEntity. It iterates over each entry in the map, attempting to ascertain whether or not the entity should be considered a "wanderer". Such examples of non-wanderers include certain celestials, wrecks, or wormholes, for example.

A player's ship is considered a wanderer frequently, because each tick during warp (introduced in #295) generates a new Bubble. In order to clean up the bubbles, ProcessWander needs to first determine who is still within it and remove the entities until each bubble has no entities in its map, which allows the bubble to be deleted later.

The segfault thus occurs when the player docks, but has also recently warped. When the player docks, BubbleManager::Remove is called on the player's SystemEntity, but unfortunately, it only removes the entity from its current bubble.

The player's SystemEntity pointer is safely deleted as part of this process, but the reason for the segfault is that all of the other bubbles that the ship recently warped through still contain pointers in their mappings of entities they each think they still have within their own bubble.


Resolution

The BubbleManager::Remove method has been updated to iterate over every single bubble, removing the SystemEntity before proceeding to remove its final Bubble. This is facilitated via the new SystemBubble::Untrack method that is identical to SystemBubble::Remove, but it omits the final call to set the SystemEntity's m_bubble pointer to a nullptr.


Testing

So far the fix seems stable. I'm able to fly around to various spots, dock, undock, etc.


Other changes

Any other changes are only either autoformatter changes or more logging statements.

Summary by Sourcery

Prevent segfaults in SystemBubble::ProcessWander by untracking SystemEntities from all bubbles they were recently in. Introduce a new method, SystemBubble::Untrack, to facilitate this process without unsetting the SystemEntity's bubble. Enhance logging for better traceability.

Bug Fixes:

  • Prevent segfaults in SystemBubble::ProcessWander by ensuring SystemEntities are untracked from all bubbles they were recently in, not just the current one.

Enhancements:

  • Add logging statements to SystemBubble::ProcessWander for better traceability and debugging.

Copy link

sourcery-ai bot commented Oct 20, 2024

Reviewer's Guide by Sourcery

This pull request addresses segfaults in the SystemBubble::ProcessWander function by improving the entity tracking and removal process across multiple bubbles. The main changes involve updating the BubbleManager::Remove method and introducing a new SystemBubble::Untrack method to ensure proper cleanup of entities when they dock after warping.

Sequence diagram for entity removal process

sequenceDiagram
    participant BubbleManager
    participant SystemBubble
    participant SystemEntity

    BubbleManager->>SystemBubble: Remove(SystemEntity)
    loop for each bubble
        SystemBubble->>SystemBubble: Untrack(SystemEntity)
    end
    SystemBubble->>SystemEntity: Set m_bubble to nullptr
Loading

Updated class diagram for SystemBubble and BubbleManager

classDiagram
    class SystemBubble {
        +void Add(SystemEntity* pSE)
        +void Remove(SystemEntity* pSE)
        +void Untrack(SystemEntity* pSE)
        +void ProcessWander(std::vector<SystemEntity*>& wanderers)
    }
    class BubbleManager {
        +void Remove(SystemEntity* ent)
    }
    class SystemEntity {
        +uint32 GetID()
        +bool HasPilot()
        +Client* GetPilot()
        +bool IsDroneSE()
        +SystemBubble* SysBubble()
    }
    SystemBubble --> SystemEntity
    BubbleManager --> SystemBubble
Loading

File-Level Changes

Change Details Files
Introduced a new SystemBubble::Untrack method for removing entities from bubble tracking without unsetting the entity's bubble
  • Added SystemBubble::Untrack method to remove entities from m_dynamicEntities map
  • Updated SystemBubble::Remove to call Untrack before setting the entity's bubble to nullptr
  • Added comments explaining the difference between Untrack and Remove
src/eve-server/system/SystemBubble.cpp
src/eve-server/system/SystemBubble.h
Updated BubbleManager::Remove to untrack entities from all bubbles
  • Added a loop to iterate through all bubbles and call Untrack on each
  • Improved logging for entity removal process
src/eve-server/system/BubbleManager.cpp
Enhanced logging and error handling in SystemBubble::ProcessWander
  • Added more detailed log messages throughout the ProcessWander method
  • Improved null checks and error handling for entity pointers
src/eve-server/system/SystemBubble.cpp
Refactored and improved code structure in SystemBubble class
  • Split long functions into smaller, more manageable blocks
  • Added more descriptive comments explaining the purpose of different code sections
  • Improved code formatting and consistency
src/eve-server/system/SystemBubble.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @charles-m-knox - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider optimizing the entity removal process by implementing a reverse lookup mechanism to quickly find all bubbles an entity is in, rather than iterating over all bubbles in the system. This could improve performance, especially in systems with many bubbles.
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

// verifies that each entity is still in this bubble.
// if any entity is no longer in the bubble, they are removed
// from the bubble and stuck into the vector for re-classification.
void SystemBubble::ProcessWander(std::vector<SystemEntity *> &wanderers) {
Copy link

Choose a reason for hiding this comment

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

suggestion: Consider refactoring ProcessWander into smaller functions

The ProcessWander method has become quite long and complex. Consider breaking it down into smaller, more focused functions to improve readability and maintainability.

void SystemBubble::ProcessWander(std::vector<SystemEntity *> &wanderers) {
    for (auto* wanderer : wanderers) {
        ProcessSingleWanderer(wanderer);
    }
}

void SystemBubble::ProcessSingleWanderer(SystemEntity* wanderer) {
    // Implementation details for processing a single wanderer
}


// iterate through all other bubbles and determine if the entity is
// in them.
std::list<SystemBubble *>::iterator itr = m_bubbles.begin();
Copy link

Choose a reason for hiding this comment

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

suggestion: Consider using range-based for loop for better readability

The iteration through m_bubbles could be simplified using a range-based for loop, which would improve readability and reduce the chance of iterator-related errors.

for (SystemBubble* bubble : m_bubbles) {
    if (bubble == nullptr) {

@@ -196,10 +197,37 @@ void BubbleManager::NewBubbleCenter(GVector shipVelocity, GPoint &newCenter) {

void BubbleManager::Remove(SystemEntity *ent) {
// suns, planets and moons arent in bubbles
//if (ent->IsStaticEntity())
// if (ent->IsStaticEntity())
// return;
if (ent->SysBubble() != nullptr) {
Copy link

Choose a reason for hiding this comment

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

issue (complexity): Consider implementing a reverse lookup mechanism for entity-bubble associations.

The changes to the Remove function in BubbleManager introduce unnecessary complexity and potential performance issues. While the new code ensures an entity is untracked from all bubbles, iterating through all bubbles for each removal operation is inefficient.

Consider optimizing this approach by maintaining a reverse lookup of which bubbles an entity is in. This would allow direct access to relevant bubbles without iterating over all of them. Here's a suggested implementation:

class SystemEntity {
    // ... existing code ...
    std::set<SystemBubble*> m_bubbles;  // Set of bubbles this entity is in
public:
    void AddBubble(SystemBubble* bubble) { m_bubbles.insert(bubble); }
    void RemoveBubble(SystemBubble* bubble) { m_bubbles.erase(bubble); }
    const std::set<SystemBubble*>& GetBubbles() const { return m_bubbles; }
};

void BubbleManager::Remove(SystemEntity *ent) {
    if (ent->SysBubble() != nullptr) {
        _log(DESTINY__BUBBLE_DEBUG, "BubbleManager::Remove(): Entity %s(%u) being removed from Bubble %u",
             ent->GetName(), ent->GetID(), ent->SysBubble()->GetID());

        // Untrack from all bubbles the entity is in
        for (SystemBubble* bubble : ent->GetBubbles()) {
            _log(DESTINY__BUBBLE_DEBUG, "BubbleManager::Remove(): Entity %s(%u) being untracked from Bubble %u",
                 ent->GetName(), ent->GetID(), bubble->GetID());
            bubble->Untrack(ent);
        }

        ent->SysBubble()->Remove(ent);
    }
}

This approach maintains the correctness of untracking from all relevant bubbles while avoiding the performance penalty of iterating over all bubbles in the system. Remember to update other parts of the code (like SystemBubble::Add) to maintain this new data structure.

@charles-m-knox
Copy link
Author

Need to close this for now. Seeing something in the logs that I want to investigate a little more.

@jdhirst jdhirst merged commit 47980f6 into EvEmu-Project:staging Nov 12, 2024
3 checks passed
@charles-m-knox charles-m-knox deleted the process-wander-segfault-fixes branch November 13, 2024 00:06
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.

2 participants