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

Lazy type objects without once_cell #758

Merged
merged 4 commits into from
Feb 8, 2020
Merged

Conversation

kngwyu
Copy link
Member

@kngwyu kngwyu commented Feb 6, 2020

The point is the use of our great GIL to make the global operation safe, and now we don't need Box::into_raw for type objects.
I'm sure this does not bring huge merits to users who have only a few #[pyclass]es in their crates... but somehow I enjoyed trying to remove Box. It was fun 🙂

@programmerjake
Copy link
Contributor

One problem with &'static ffi::PyTypeObject is that Rust assumes (and tells LLVM) that none of the fields are ever modifiable since ffi::PyTypeObject doesn't contain any UnsafeCells. Some of the fields are modified, the reference count, as well as probably others.

Copy link
Member

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

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

I like the idea of removing once_cell and using the GIL instead - makes total sense! I have a minor nitpick with the implementation to make it more bombproof.

I also have another concern with &'static - which is that I'm not sure it will play nicely with multiple sub-interpreters. According to PEP 554 and the docs for Py_NewInterpreter, even builtin type objects are separate between each sub-interpreter. So eventually we'd have to store instances of each type object for each sub-interpreter, and I don't think we can use &'static with that.

So if we want to one day have support for sub-interpreters, then we probably don't want type_object() to return &'static. We've had at least one request for sub-interpreters in the past (#576)

return value.clone();
}
// We have to get the GIL before setting the value to the global!!!
let gil = Python::acquire_gil();
Copy link
Member

Choose a reason for hiding this comment

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

It's unlikely to happen in practice, but there's nothing to prevent two or more threads reaching this point at the same time, which would then lead to value being initialized multiple times. Whether this is a problem, I'm not sure.

We could avoid this by checking again that value is still None after the GIL lock is acquired, and exiting early if some other thread initialized value while this thread was waiting for the GIL.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you.
I changed this to use AtomicBool

}
}
pub fn get<T: PyClass>(&self) -> &ffi::PyTypeObject {
if !self.initialized.get() {
let gil = Python::acquire_gil();
Copy link
Member

Choose a reason for hiding this comment

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

Same comment applies as for LazyHeapType.

@kngwyu
Copy link
Member Author

kngwyu commented Feb 8, 2020

Thank you for the reviews, but I think &static is OK here since the underlying data is in UnsafeCell or in FFI.
And for sub interpreters... I cannot imagine the ideal APIs now.

@kngwyu kngwyu merged commit 6069ee1 into PyO3:master Feb 8, 2020
@davidhewitt
Copy link
Member

Ok. I'll cross this hurdle if we ever get to sub-interpreter support :)

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