1919#include < string>
2020
2121#include " google/protobuf/descriptor.pb.h"
22+ #include " absl/base/attributes.h"
2223#include " absl/base/const_init.h"
24+ #include " absl/base/thread_annotations.h"
2325#include " absl/container/flat_hash_map.h"
2426#include " absl/log/absl_check.h"
2527#include " absl/strings/string_view.h"
@@ -78,7 +80,7 @@ namespace python {
7880
7981// Zero-cost mutex wrapper that compiles away to nothing in GIL-enabled builds.
8082// Similar to nanobind's ft_mutex pattern.
81- class FreeThreadingMutex {
83+ class ABSL_LOCKABLE ABSL_ATTRIBUTE_WARN_UNUSED FreeThreadingMutex {
8284 public:
8385 FreeThreadingMutex () = default ;
8486 explicit constexpr FreeThreadingMutex (absl::ConstInitType)
@@ -96,21 +98,23 @@ class FreeThreadingMutex {
9698 void Unlock () {}
9799#else
98100 // Free-threaded build: real mutex
99- void Lock () { mutex_.Lock (); }
100- void Unlock () { mutex_.Unlock (); }
101+ void Lock () ABSL_EXCLUSIVE_LOCK_FUNCTION() { mutex_.Lock (); }
102+ void Unlock () ABSL_UNLOCK_FUNCTION() { mutex_.Unlock (); }
101103
102104 private:
103105 absl::Mutex mutex_;
104106#endif
105107};
106108
107109// RAII lock guard for FreeThreadingMutex
108- class FreeThreadingLockGuard {
110+ class ABSL_SCOPED_LOCKABLE FreeThreadingLockGuard {
109111 public:
110- explicit FreeThreadingLockGuard (FreeThreadingMutex& mutex) : mutex_(mutex) {
112+ explicit FreeThreadingLockGuard (FreeThreadingMutex& mutex)
113+ ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex)
114+ : mutex_(mutex) {
111115 mutex_.Lock ();
112116 }
113- ~FreeThreadingLockGuard () { mutex_.Unlock (); }
117+ ~FreeThreadingLockGuard () ABSL_UNLOCK_FUNCTION() { mutex_.Unlock (); }
114118
115119 FreeThreadingLockGuard (const FreeThreadingLockGuard&) = delete ;
116120 FreeThreadingLockGuard& operator =(const FreeThreadingLockGuard&) = delete ;
@@ -119,18 +123,19 @@ class FreeThreadingLockGuard {
119123 FreeThreadingMutex& mutex_;
120124};
121125
126+ // Mutex to protect interned_descriptors from concurrent access in
127+ // free-threading Python builds. Zero-cost in GIL-enabled builds.
128+ // NOTE: Free-threading support is still experimental.
129+ FreeThreadingMutex interned_descriptors_mutex (absl::kConstInit );
130+
122131// Store interned descriptors, so that the same C++ descriptor yields the same
123132// Python object. Objects are not immortal: this map does not own the
124133// references, and items are deleted when the last reference to the object is
125134// released.
126135// This is enough to support the "is" operator on live objects.
127136// All descriptors are stored here.
128- absl::flat_hash_map<const void *, PyObject*>* interned_descriptors;
129-
130- // Mutex to protect interned_descriptors from concurrent access in
131- // free-threading Python builds. Zero-cost in GIL-enabled builds.
132- // NOTE: Free-threading support is still experimental.
133- FreeThreadingMutex interned_descriptors_mutex (absl::kConstInit );
137+ absl::flat_hash_map<const void *, PyObject*>* interned_descriptors
138+ ABSL_PT_GUARDED_BY (interned_descriptors_mutex);
134139
135140PyObject* PyString_FromCppString (absl::string_view str) {
136141 return PyUnicode_FromStringAndSize (str.data (),
0 commit comments