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

Thread-safe release pools #2

Merged
merged 1 commit into from
May 2, 2020

Conversation

davidhewitt
Copy link

@davidhewitt davidhewitt commented May 2, 2020

I managed to get thread_local! working using RefCell. If we wanted to be ambitious we could use UnsafeCell for performance, but I wanted to be conservative (I think I'd rather know if we got this wrong and take the light performance penalty for now?).

At the same time I also was able to clean up ReleasePool a lot, to not need ReleasePoolImpl.

I believe this fixes PyO3#756

@kngwyu
Copy link
Owner

kngwyu commented May 2, 2020

Thank you!
Since it's hard to review this PR at a glance(especially register_any), I'll check it by actually playing with it.

@kngwyu kngwyu merged commit f2b347a into kngwyu:new-nativetypes May 2, 2020
// Note: inside this closure we must be careful to not hold a borrow too long, because
// while calling Py_DECREF we may cause other callbacks to run which will need to
// register objects into the GILPool.
let len = owned_objects.borrow().len();
Copy link
Owner

Choose a reason for hiding this comment

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

Too inefficient?

Copy link
Owner

@kngwyu kngwyu May 2, 2020

Choose a reason for hiding this comment

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

Hmm 🤔
If we need this kind of hack, I think we should consider simply locking other threads.

Copy link
Author

@davidhewitt davidhewitt May 2, 2020

Choose a reason for hiding this comment

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

The problem isn't other threads, but that multiple GILPools can exist in the same thread. They might even be created & dropped while this GILPool's drop is running.

For example, when Py_DECREF causes a reference count to go to zero, then we may end up running a tp_dealloc callback, which might create a new GILPool, which might run other python code, which might run something else which creates another GILPool, ... (and so on). I think that all those GILPools should be cleaned up by the time the tp_dealloc callback ends, but I was feeling cautious.

If we are concerned about performance we should write some more benchmarks to compare PyO3#887 against pyo3 master? If there's a noticeable performance regression then we could consider using UnsafeCell instead of RefCell. I'd prefer avoid the unsafe unless we really need it though.

Copy link
Author

Choose a reason for hiding this comment

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

Another alternative is maybe for each GILPool to own its own Vec storage. We could even try using smallvec which might mean that most GILPools will never need an allocation?

Copy link
Author

Choose a reason for hiding this comment

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

EDIT: thinking about this more, we already use UnsafeCell in this file. So, maybe we should just proceed and use UnsafeCell here too.

Copy link
Author

Choose a reason for hiding this comment

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

(Do you want me to open another PR to change RefCell -> UnsafeCell?)

Copy link
Author

@davidhewitt davidhewitt May 2, 2020

Choose a reason for hiding this comment

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

Haha, one last thought, sorry :D

Actually I'm not sure that UnsafeCell is sound. Any register_owned call while the other callbacks are running might cause the Vec to resize, which could move the contents of the Vec storage. So we have to be very careful to make sure we're using the shared Vec correctly, else we're accessing invalid memory.

This problem also exists without thread_local, I guess. Just now that we're looking into this file we see the dangers of the existing implementation!

Copy link
Owner

Choose a reason for hiding this comment

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

If we are concerned about performance

I don't mind performance so much here, but now I don't feel this solution so reliable.

Copy link
Owner

Choose a reason for hiding this comment

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

My temporal solution: PyO3@75c807f

@davidhewitt davidhewitt deleted the new-nativetypes branch August 10, 2021 07:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants