Make pycritical_section non-copyable and non-movable

The pycritical_section class is a RAII wrapper that manages a Python
critical section lifecycle:
- Acquires the critical section in the constructor via
  PyCriticalSection_BeginMutex
- Releases it in the destructor via PyCriticalSection_End
- Holds a reference to a pymutex

Allowing copy or move operations would be dangerous:

1. Copy: Both the original and copied objects would call
   PyCriticalSection_End on the same PyCriticalSection object in their
   destructors, leading to double-unlock and undefined behavior.

2. Move: The moved-from object's destructor would still run and attempt
   to end the critical section, while the moved-to object would also try
   to end it, again causing double-unlock.

This follows the same pattern used by other RAII lock guards in the
codebase, such as gil_scoped_acquire and gil_scoped_release, which also
explicitly delete copy/move operations to prevent similar issues.

By explicitly deleting these operations, we prevent accidental misuse
and ensure the critical section is properly managed by a single RAII
object throughout its lifetime.
This commit is contained in:
Ralf W. Grosse-Kunstleve
2026-01-24 10:23:24 -08:00
parent 8690dd71be
commit be01d1ebe8

View File

@@ -248,6 +248,12 @@ public:
PyCriticalSection_BeginMutex(&cs, &mutex.mutex);
}
~pycritical_section() { PyCriticalSection_End(&cs); }
// Non-copyable and non-movable to prevent double-unlock
pycritical_section(const pycritical_section &) = delete;
pycritical_section &operator=(const pycritical_section &) = delete;
pycritical_section(pycritical_section &&) = delete;
pycritical_section &operator=(pycritical_section &&) = delete;
};
// Instance map shards are used to reduce mutex contention in free-threaded Python.