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

Fix potential panics caused by Garbage Collector #855

Merged
merged 4 commits into from
Apr 9, 2020
Merged

Fix potential panics caused by Garbage Collector #855

merged 4 commits into from
Apr 9, 2020

Conversation

althonos
Copy link
Member

@althonos althonos commented Apr 8, 2020

As discovered in #854, having a mutable borrow at any point on a PyClass that also implements the Garbage Collection protocol leads to unexpected panics, since the garbage collector can be run at any time, including when our class is already mutably borrowed.

As a solution, the wrapper for the garbage collector now calls PyCell::try_borrow instead of PyCell::borrow, and simply returns if it could not acquire the cell. I'm not sure about the expected behaviour here though, feedback is welcome.

Maintenance

  • Run cargo fmt
  • Run cargo clippy and check there are no hard errors (There are a bunch of existing warnings; This is also checked by travis) ✅
  • If applicable, add an entry in the changelog. ✅
  • If applicable, add documentation to all new items and extend the guide.
  • If applicable, add tests for all new or fixed functions ✅
  • If you changed any python code, run black .. You can install black with pip install black)

@kngwyu
Copy link
Member

kngwyu commented Apr 8, 2020

Really good catch, thank you!

simply returns if it could not acquire the cell.

I think it's OK, though it's really difficult to test. Maybe it's sufficient to check the else branch(=in case try_borrow fails) works correctly?

@althonos
Copy link
Member Author

althonos commented Apr 8, 2020

I'm trying to craft something with a method that blocks while borrowing, and a background thread that calls the GC, but I have no luck currently. I can confirm this test works on the codebase I'm using but since the test case (which comes from the CPython test suite) is very cryptic I can't port it as is.

@davidhewitt
Copy link
Member

Looks good to me, thanks 👍

I think that doing nothing if already borrowed is probably fine for tp_traverse - the worst case is that the GC won't detect a cycle, and will do nothing.

I think we might have to look carefully in the future at tp_clear, which I think will only get called once and will need to succeed right away.

@althonos
Copy link
Member Author

althonos commented Apr 8, 2020

Okay, I have a test case where I'm manually invoking the tp_traverse method as-if I were the GC; once while the instance is not borrowed, once when it is mutably borrowed. Without the fix the test panics, with the fix it doesn't. Both branches are covered according to tarpaulin.

@althonos althonos requested a review from kngwyu April 9, 2020 07:39
Copy link
Member

@kngwyu kngwyu left a comment

Choose a reason for hiding this comment

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

Awesome 💯 , thank you!

let ty = TraversableClass::type_object().as_ref(py).as_type_ptr();
let traverse = (*ty).tp_traverse.unwrap();

// create an object and check that traversing it works normally
Copy link
Member

Choose a reason for hiding this comment

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

Good test 👍

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.

3 participants