Skip to content

Introduce PyAtomicRef and impl for PyObject.typ - #3828

Merged
qingshi163 merged 8 commits into
RustPython:mainfrom
qingshi163:main
Oct 15, 2022
Merged

qingshi163 merged 8 commits into
RustPython:mainfrom
qingshi163:main

Conversation

@qingshi163

Copy link
Copy Markdown
Contributor

@youknowone correctness and performance check?

@qingshi163
qingshi163 requested a review from youknowone June 29, 2022 15:38
@qingshi163
qingshi163 marked this pull request as ready for review June 29, 2022 15:38
@fanninpm

Copy link
Copy Markdown
Contributor

Feel free to ignore those Clippy failures. @youknowone likely missed them in #3817.

Comment thread vm/src/object/ext.rs Outdated
type>

fn deref(&self) -> &Self::Target {
unsafe { self.ptr.load().as_ref() }

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 is looking unsafe.

While thread 1 called deref() to obtain &Py<T>, inner can be swapped out in thread 2.
When the swapped out object is dropped, thread 1 will have a dangling pointer.

@qingshi163

Copy link
Copy Markdown
Contributor Author

#3970

@qingshi163

Copy link
Copy Markdown
Contributor Author

Is this approach safe as we manaully require the reference to the pytype cannot across between the frames. Most of the case we use the pytype is either using pytyperef or using the reference immediately.
@youknowone

@youknowone

Copy link
Copy Markdown
Member

Oh, I missed your last push. I will check the changes soon.

Comment thread vm/src/builtins/type.rs
}

impl PyTypeRef {
impl Py<PyType> {

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 is a great idea.

youknowone
youknowone previously approved these changes Aug 28, 2022

@youknowone youknowone 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.

Logically looking good to me.

Comment thread vm/src/frame.rs Outdated
/// tracer function for this frame (usually is None)
pub trace: PyMutex<PyObjectRef>,
state: PyMutex<FrameState>,
pub temperary_refs: PyMutex<Vec<PyObjectRef>>,

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.

Maybe we can reuse this for del.
I didn't know many fields in Frame are mutex. I thought they are single-thread accessible.

typo

Suggested change
pub temperary_refs: PyMutex<Vec<PyObjectRef>>,
pub temporary_refs: PyMutex<Vec<PyObjectRef>>,

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.

If frame itself is executed single-threaded, we could replace mutex with an atomic increasing vec, do we have any crates doing that?

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.

I don't think we have those crate. When we create safety by human, UnsafeCell is the common tool

Comment thread vm/src/object/core.rs Outdated
Comment thread vm/src/object/ext.rs Outdated
Comment thread vm/src/object/ext.rs Outdated
#[must_use]
pub unsafe fn swap(&self, pyref: PyRef<T>) -> PyRef<T> {
let py = PyRef::leak(pyref);
let old = self.0.swap(NonNull::from(py));

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.

Suggested change
let old = self.0.swap(NonNull::from(py));
let old = self.0.swap(NonNull::new_unchecked(py));

we already know py is not a null.

I am not sure possible but retaining &'static mut T will be good when available.

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.

these two are equal. no check will be made as NonNull::from only accept valid value and it is calling new_unchecked inside the function

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.

what do you mean about &'static mut T

@youknowone youknowone Aug 29, 2022 •

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.

oh, got it. py is a reference, not a pointer.

I was thinking if this is possible.

pub struct PyAtomicRef<T: PyObjectPayload>(AtomicCell<&'static mut Py<T>>);

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.

I think I've found over the past while, mainly from looking at crossbeam's issue list, that atomics aren't actually able to be hidden away enough such that AtomicCell would be a good abstraction. I'd honestly prefer a PyAtomic<*mut Py<T>>, which is a type alias that resolves to std::sync::atomic::AtomicPtr when threading is enabled, and Cell<*mut _> when it's not. I meant to go and replace all the AtomicCell uses with PyAtomic after I added PyAtomic, but I never got around to it.

Comment thread vm/src/object/ext.rs Outdated
}
}

pub struct PyAtomicRef<T: PyObjectPayload>(AtomicCell<NonNull<Py<T>>>);

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.

Will using AtomicCell<NonNull> spend same cost as AtomicPtr?

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.

AtomicCell::::is_lock_free() should be true for the platforms that support AtomicPtr. But I am not sure about any overhead that AtomicCell does.

Comment thread vm/src/object/ext.rs
pub fn swap_to_temporary_refs(&self, pyref: PyRef<T>, vm: &VirtualMachine) {
let old = unsafe { self.swap(pyref) };
if let Some(frame) = vm.current_frame() {
frame.temporary_refs.lock().push(old.into());

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.

Does temporary_refs ever get cleared? Like I guess it doesn't need to be, necessarily, but I don't like the idea of

class A: pass
class B: pass
B().__class__ = A

meaning that A will never ever ever be deallocated.

What if instead class() returned a guard.... no that wouldn't work. I'll try and think of something by the morning, maybe.

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.

I guess when the frame is popped

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.

Ohhh, I missed that its per-frame, yeah I guess that does make sense. Nevermind!

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.

I guess when the frame is popped

Yes, we can clear the refs for each instruction, but I don't think it is worth.

@youknowone
youknowone dismissed their stale review August 29, 2022 10:03

The errors were not rebase problem

@coolreader18

Copy link
Copy Markdown
Member

(using PyAtomic instead of AtomicCell would fix the provenance errors in miri)

@qingshi163

Copy link
Copy Markdown
Contributor Author

(using PyAtomic instead of AtomicCell would fix the provenance errors in miri)

let me try

@coolreader18

Copy link
Copy Markdown
Member

Also, vm.frames should definitely be made private now, since it's now a safety thing that a frame cannot be popped off the frame stack + dropped before it's fully done running.

@qingshi163

Copy link
Copy Markdown
Contributor Author

(using PyAtomic instead of AtomicCell would fix the provenance errors in miri)

let me try

Don't know why but seem like failing the compile when threading is off, any mistake about how to use PyAtomic?
@coolreader18

@qingshi163

Copy link
Copy Markdown
Contributor Author

(using PyAtomic instead of AtomicCell would fix the provenance errors in miri)

let me try

Don't know why but seem like failing the compile when threading is off, any mistake about how to use PyAtomic? @coolreader18

nevermind, it just the name conflict

@qingshi163

Copy link
Copy Markdown
Contributor Author

@coolreader18 @youknowone review?

@youknowone youknowone 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.

@qingshi163 looks good to me.
If @coolreader18 is too busy to review this PR, please feel free to merge it yourself.

Thank you for the awesome work!

@qingshi163
qingshi163 merged commit 30aeb8b into RustPython:main Oct 15, 2022
@qingshi163
qingshi163 deleted the main branch October 15, 2022 19:53
@qingshi163
qingshi163 restored the main branch October 15, 2022 19:53
@qingshi163
qingshi163 deleted the main branch November 13, 2022 08:12
youknowone added a commit that referenced this pull request Sep 26, 2026
* Remove unused PyLease

PyLease wrapped a read guard over the object's type when `typ` was a
`PyRwLock<PyTypeRef>`. Its last users, `class()` and `lease_class()`,
went away when `typ` became a `PyAtomicRef<PyType>` (#3828). Its field
is private and it has no constructor, so it cannot be built anywhere.

Assisted-by: Claude Code:claude-opus-5-5

* Skip test_large_content_length_truncated on macOS

The test sets the CGI handler timeout to 1ms. That timeout also bounds
reading the request line, so when the client has not sent the request
within 1ms the server logs "Request timed out" and closes the
connection. The client then gets ConnectionResetError or
BrokenPipeError. This reproduces on macOS with both RustPython and
CPython 3.14.6. The skip was removed in #8759 after Linux-only
repeats.

Assisted-by: Claude Code:claude-opus-5-5
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