-
Notifications
You must be signed in to change notification settings - Fork 6.1k
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
[core][1/N] Make executor to be a long-running Python thread #50644
[core][1/N] Make executor to be a long-running Python thread #50644
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
discussed offline:
setup.py
seems to always overwritePYTHON3_BIN_PATH
https://github.com/ray-project/ray/blob/master/python/setup.py#L537 , so it is probably something else that is missing the env var (rather than the wheel building). please investigate a bit more on why.- the env var, if needed, should be set in scripts like https://github.com/ray-project/ray/blob/master/python/build-wheel-manylinux2014.sh , rather than in the python wrapper. this will make sure that scripts like https://github.com/ray-project/ray/blob/master/python/README-building-wheels.md will keep working.
2b66d24
to
4218e5e
Compare
python/ray/_raylet.pyx
Outdated
cdef function[void()] callback; | ||
with gil: | ||
gstate = PyGILState_Ensure() | ||
callback = GetThreadReleaser(gstate) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetThreadReleaser
should be defined as another cdef
function in this file instead of in core worker cpp code (that is another abstraction leak similar to the original PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replied in #50644 (comment). I agree that it is the best case if we can avoid the C++ util function.
Would you mind sharing more details why it's an abstraction leak?
GetThreadReleaser
is exclusively used by Cython and is not included in the shared code path for any other language frontends.- It doesn't add any new dependencies.
- There is a directory
core_worker/lib/java
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
core_worker/lib/java
is just autogenerated bindings for the core worker API (it could/should be moved elsewhere)
inline std::function<void()> GetThreadReleaser(PyGILState_STATE gstate) { | ||
return [gstate]() { PyGILState_Release(gstate); }; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't need to exist -- you can define it in cython and pass it down
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To the best of my knowledge, this may not be possible in Cython. I haven't seen any documentation that explicitly states it's impossible. I tried looking for examples on GitHub, but I didn't find any way to capture gstate
and return a callable.
I tried multiple ways to capture gstate and return a callable, but I failed. In addition, this method is referenced in the unit tests for Cython's <functional>
. You can take a look at _get_function_ptr_from_name
in cython/cython@954f0ec.
I'm not familiar with Cython, but this is my current guess based on my investigation. It would be helpful if we could avoid defining this C++ function. Would you mind sharing more details if you have related experiences?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you should be able to use std::bind
and std::placeholder1
to bind the gstate
as an arg and then pass the lambda into the c++ layer
or alternatively (maybe easier?) you can wrap the gstate
as a unique_ptr<void*>
, pass it into c++, then cast it back to gstate
in the callback. we do something similar currently for the callback passed to c++ for async get:
Line 4717 in e0f7bbc
def set_get_async_callback(self, ObjectRef object_ref, user_callback: Callable): |
@israbbani has a follow-up to avoid the manual incref/decref on that codepath and use a unique_ptr
instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use std::bind
and it works! Initially, I tried using a lambda function to capture gstate. I didn't know that std::bind
existed. In addition, I haven't seen any CPython examples where users need to control the reference count of PyGILState_STATE
.
80f8f0e
to
1b63aa9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, thanks for figuring out the tricky cython bits! All style nits now.
Please add a test for concurrency_group_manager
that encodes the expected semantics of the initialize_thread_callback
(called before thread executes anything, the returned callback is called on shutdown).
Are we certain that the gstate
object does not need to be reference counted? Just want to make sure there is no concern of crashing during the shutdown sequence due to a memory corruption issue.
Test added. ee93309 |
I will conduct some experiments to verify it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work!
Let me know when ready for merge. DCO is failing btw. |
Signed-off-by: kaihsun <[email protected]>
Signed-off-by: kaihsun <[email protected]>
Signed-off-by: kaihsun <[email protected]>
Signed-off-by: kaihsun <[email protected]>
Signed-off-by: kaihsun <[email protected]>
Signed-off-by: kaihsun <[email protected]>
Signed-off-by: kaihsun <[email protected]>
Signed-off-by: kaihsun <[email protected]>
Signed-off-by: kaihsun <[email protected]>
Signed-off-by: kaihsun <[email protected]>
Signed-off-by: kaihsun <[email protected]>
Signed-off-by: kaihsun <[email protected]>
Signed-off-by: kaihsun <[email protected]>
Signed-off-by: kaihsun <[email protected]>
ee93309
to
de20eac
Compare
I followed the instructions on the DCO page to fix the DCO issue, but somehow it messed up the commits. I will open a new PR instead. |
Open a new PR: #51005 |
Open a new PR: #51016 |
Why are these changes needed?
Currently, tasks are sometimes executed on the main thread and sometimes on other threads which is created by C++. However, C++ threads are only considered Python threads when they execute Python functions. Hence, when a task finishes, the thread-local state will be garbage-collected. See #46336 (comment) for more details.
This PR uses the CPython API to treat C++ threads as Python threads, even if they do not execute Python functions.
https://docs.python.org/3/c-api/init.html#non-python-created-threads
This PR handles synchronous tasks. After this PR is merged, the follow-up tasks are:
Related issue number
Part of #46336
Checks
git commit -s
) in this PR.scripts/format.sh
to lint the changes in this PR.method in Tune, I've added it in
doc/source/tune/api/
under thecorresponding
.rst
file.