Skip to content

Avoid potential deadlocks in host allocator #159352

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

Open
wants to merge 4 commits into
base: gh/guangyey/174/base
Choose a base branch
from

Conversation

guangyey
Copy link
Collaborator

@guangyey guangyey commented Jul 29, 2025

Stack from ghstack (oldest at bottom):

Motivation

This PR fixes a potential deadlock in the host allocator.
When calling event->record(stream), the record_stream implementation may acquire the Python GIL.
In places such as

void record_stream(
std::optional<std::vector<EventPool::Event>>& events,
CUDAStream stream) override {
auto event = create_event_internal(stream.device_index());
event->record(stream);
events->push_back(std::move(event));
}
, and
void record_stream(
std::optional<std::vector<XPUEvent>>& events,
XPUStream stream) override {
XPUEvent event;
event.record(stream);
events->push_back(std::move(event));
}
record_stream is invoked while holding the allocator lock.

To prevent deadlocks, we must ensure the locking order is:
GIL → Allocator Lock.
Reversing the order (Allocator Lock → GIL) can cause a deadlock.

Copy link

pytorch-bot bot commented Jul 29, 2025

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/159352

Note: Links to docs will display an error until the docs builds have been completed.

✅ You can merge normally! (1 Unrelated Failure)

As of commit 89aa54d with merge base 05c19d1 (image):

FLAKY - The following job failed but was likely due to flakiness present on trunk:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

block->event_count_ += events->size();
// Move out streams to avoid holding the mutex during event recording
streams = std::move(block->streams_);
block->streams_.clear();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Moved streams are used at the next line.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This actually fine per the spec, std::move moves puts it an unspecified, but valid state. Clear() should be safe to call.

@guangyey guangyey requested a review from albanD July 29, 2025 08:38
guangyey added 2 commits July 29, 2025 15:42
[ghstack-poisoned]
[ghstack-poisoned]
Skylion007
Skylion007 previously approved these changes Jul 29, 2025
block->event_count_ += events->size();
// Move out streams to avoid holding the mutex during event recording
streams = std::move(block->streams_);
block->streams_.clear();
Copy link
Collaborator

Choose a reason for hiding this comment

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

This actually fine per the spec, std::move moves puts it an unspecified, but valid state. Clear() should be safe to call.

@Skylion007 Skylion007 dismissed their stale review July 29, 2025 16:57

Didn't mean to approve, just comment

@EikanWang
Copy link
Collaborator

@guangyey , could you help rebase the stack to make the ci signal green?

@guangyey
Copy link
Collaborator Author

guangyey commented Aug 8, 2025

@guangyey , could you help rebase the stack to make the ci signal green?

OK.

[ghstack-poisoned]
guangyey added a commit that referenced this pull request Aug 11, 2025
ghstack-source-id: 17abfd7
Pull Request resolved: #159352
@guangyey guangyey requested a review from ezyang August 11, 2025 02:44
[ghstack-poisoned]
@albanD
Copy link
Collaborator

albanD commented Aug 11, 2025

Can you clarify when the GIL would get grabbed in this case?
I feel like event recording is a common thing to do in our backend and it is done without much care for python world.

@guangyey
Copy link
Collaborator Author

guangyey commented Aug 11, 2025

Can you clarify when the GIL would get grabbed in this case?
I feel like event recording is a common thing to do in our backend and it is done without much care for python world.

@albanD Take cuda for an example, here will grab the GIL at trace_gpu_event_record

const c10::impl::PyInterpreter* interp = c10::impl::GPUTrace::get_trace();
if (C10_UNLIKELY(interp)) {
(*interp)->trace_gpu_event_record(at::kCUDA,
reinterpret_cast<uintptr_t>(event_),
reinterpret_cast<uintptr_t>(stream.stream())
);
}

-> CONCRETE_GPU_TRACE
void trace_gpu_event_record(
at::DeviceType device_type,
uintptr_t event,
uintptr_t stream) const override {
CONCRETE_GPU_TRACE(device_type, "EventRecordCallbacks", event, stream);
}

and try to fetch GIL at line 26
#define CONCRETE_GPU_TRACE(device_type, func_name, ...) \
at::impl::MaybeSetTLSOnEntryGuard guard; \
if (Py_IsInitialized()) { \
pybind11::gil_scoped_acquire gil; \
try { \
/* Masquerade hip as cuda because hip uses `torch.cuda` module. */ \

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ciflow/trunk Trigger trunk jobs on your pull request ciflow/xpu Run XPU CI tasks open source topic: not user facing topic category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants