mirror of
https://github.com/protocolbuffers/protobuf
synced 2026-08-26 02:23:14 -04:00
This change modifies all remaining `Dealloc()` functions to use `EraseIfEqual` if they were not already. This prevents the same race that was fixed for descriptors in cl/874084218. PiperOrigin-RevId: 952273589
111 lines
3 KiB
C++
111 lines
3 KiB
C++
#include "google/protobuf/pyext/weak_value_map.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "absl/log/absl_check.h"
|
|
|
|
#define PY_SSIZE_T_CLEAN
|
|
#include <Python.h>
|
|
|
|
#ifdef Py_GIL_DISABLED
|
|
|
|
// In a free threaded build, we have to always be prepared for the possibility
|
|
// that an object is being deallocated, but has not yet been removed from the
|
|
// map. We accomplish this by using the unstable PyUnstable_TryIncRef() function
|
|
// which returns false if the object is dying.
|
|
|
|
PyObject* PyWeakValueMap::Get(const void* key, const PyTypeObject* type) {
|
|
absl::MutexLock lock(&mutex_);
|
|
auto it = cache_.find(key);
|
|
if (it != cache_.end()) {
|
|
ABSL_DCHECK(type == nullptr || Py_TYPE(it->second) == type);
|
|
if (PyUnstable_TryIncRef(it->second)) {
|
|
return it->second;
|
|
}
|
|
// Object is deallocating, remove it from the map.
|
|
cache_.erase(it);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool PyWeakValueMap::TrySet(const void* key, PyObject*& value) {
|
|
PyTypeObject* type = Py_TYPE(value);
|
|
PyObject* decref;
|
|
PyUnstable_EnableTryIncRef(value);
|
|
|
|
{
|
|
absl::MutexLock lock(&mutex_);
|
|
auto [it, inserted] = cache_.insert(std::make_pair(key, value));
|
|
if (inserted) return true;
|
|
|
|
// The object is already in the map. Try to use the existing object.
|
|
ABSL_DCHECK(type == nullptr || Py_TYPE(it->second) == type);
|
|
|
|
if (PyUnstable_TryIncRef(it->second)) {
|
|
// The existing object is valid, so we can deallocate our copy, but we
|
|
// should drop the lock first.
|
|
decref = value;
|
|
value = it->second;
|
|
// Fall through to the end of the function.
|
|
} else {
|
|
// The existing object is dying, replace it.
|
|
it->second = value;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Py_DECREF(decref);
|
|
return false;
|
|
}
|
|
|
|
bool PyWeakValueMap::EraseIfEqualImpl(const void* key, PyObject* value) {
|
|
absl::MutexLock lock(&mutex_);
|
|
auto it = cache_.find(key);
|
|
if (it != cache_.end() && it->second == value) {
|
|
cache_.erase(it);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PyWeakValueMap::IsEmpty() const {
|
|
absl::MutexLock lock(&mutex_);
|
|
return cache_.empty();
|
|
}
|
|
|
|
void PyWeakValueMap::Clear() {
|
|
absl::MutexLock lock(&mutex_);
|
|
cache_.clear();
|
|
}
|
|
|
|
#else // !Py_GIL_DISABLED
|
|
|
|
bool PyWeakValueMap::TrySet(const void* key, PyObject*& value) {
|
|
auto [it, inserted] = cache_.insert(std::make_pair(key, value));
|
|
if (inserted) return true;
|
|
Py_DECREF(value);
|
|
Py_INCREF(it->second);
|
|
value = it->second;
|
|
return false;
|
|
}
|
|
|
|
PyObject* PyWeakValueMap::Get(const void* key, const PyTypeObject* type) {
|
|
auto it = cache_.find(key);
|
|
if (it == cache_.end()) return nullptr;
|
|
Py_INCREF(it->second);
|
|
return it->second;
|
|
}
|
|
|
|
bool PyWeakValueMap::EraseIfEqualImpl(const void* key, PyObject* value) {
|
|
auto it = cache_.find(key);
|
|
// In a single-threaded build, the object is guaranteed to be in the map.
|
|
ABSL_CHECK(it != cache_.end() && it->second == value);
|
|
cache_.erase(it);
|
|
return true;
|
|
}
|
|
|
|
bool PyWeakValueMap::IsEmpty() const { return cache_.empty(); }
|
|
|
|
void PyWeakValueMap::Clear() { cache_.clear(); }
|
|
|
|
#endif
|