Skip to content

Commit 8b36073

Browse files
laramielcopybara-github
authored andcommitted
Add locking for python free-threading
Python free-threading requires object locking around any mutable objects. Locks such as absl::Mutex may be used, or a python object's internal mutex may be used. pybind11, unfortunately, does not have a built-in capability to ensure these locks are acquired. This change adds the following types which aid in locking. * ScopedPyCriticalSection and the ScopedPyCriticalSection2. These scoped locks acquire a python object's internal mutex. * with_handle<T&> which provide access to the py::handle along with a type reference (which can be used to call ScopedPyCriticalSection) * locking_type_caster, which acquires python locks when making a copy of an object into C++ (via T / T&& casts from the pybind11 type_caster). * Additional concurrent tests. The most common pattern for locked use is the following: [](with_handle<T&> self, ...) { ScopedPyCriticalSection cs(self.handle.ptr()); ... use self.value as needed ... } Additionally, most call sites have been audited to try and ensure correct mutex use in the free threaded python environment. This has led to an added template parameter, WithLocking, to several template functions. Note that mutable types passed using by-value semantics are the likely to require extra scrutiny, such as the use of locking_type_caster<>, or other changes to ensure concurrent access is properly guarded. See: #218 See: #254 PiperOrigin-RevId: 827716276 Change-Id: Id28d99b0a0ffb978988155892f8602c3c0d127f7
1 parent 93086e2 commit 8b36073

33 files changed

+2364
-1147
lines changed

python/tensorstore/BUILD

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ pybind11_cc_library(
228228
srcs = ["json_type_caster.cc"],
229229
hdrs = ["json_type_caster.h"],
230230
deps = [
231+
":critical_section",
231232
"@com_github_pybind_pybind11//:pybind11",
232233
"@nlohmann_json//:json",
233234
],
@@ -296,6 +297,7 @@ pybind11_cc_library(
296297
srcs = ["dim_expression.cc"],
297298
hdrs = ["dim_expression.h"],
298299
deps = [
300+
":critical_section",
299301
":index",
300302
":numpy_indexing_spec",
301303
":sequence_parameter",
@@ -343,11 +345,13 @@ pybind11_cc_library(
343345
hdrs = ["index_space.h"],
344346
deps = [
345347
":array_type_caster",
348+
":critical_section",
346349
":dim_expression",
347350
":gil_safe",
348351
":homogeneous_tuple",
349352
":index",
350353
":json_type_caster",
354+
":locking_type_casters",
351355
":numpy",
352356
":numpy_indexing_spec",
353357
":python_imports",
@@ -440,19 +444,21 @@ pybind11_cc_library(
440444
deps = [
441445
":array_type_caster",
442446
":context",
447+
":critical_section",
443448
":data_type",
444449
":define_heap_type",
445450
":garbage_collection",
446451
":homogeneous_tuple",
447-
":index",
448452
":index_space",
449453
":intrusive_ptr_holder",
450454
":json_type_caster",
451455
":keyword_arguments",
452456
":kvstore",
457+
":locking_type_casters",
453458
":result_type_caster",
454459
":sequence_parameter",
455460
":serialization",
461+
":status",
456462
":tensorstore_module_components",
457463
":unit",
458464
"//tensorstore:array",
@@ -462,7 +468,9 @@ pybind11_cc_library(
462468
"//tensorstore:data_type",
463469
"//tensorstore:index",
464470
"//tensorstore:json_serialization_options",
471+
"//tensorstore:json_serialization_options_base",
465472
"//tensorstore:open_mode",
473+
"//tensorstore:open_options",
466474
"//tensorstore:rank",
467475
"//tensorstore:schema",
468476
"//tensorstore:spec",
@@ -472,9 +480,10 @@ pybind11_cc_library(
472480
"//tensorstore/internal:global_initializer",
473481
"//tensorstore/internal:intrusive_ptr",
474482
"//tensorstore/internal/json:pprint_python",
483+
"//tensorstore/internal/meta:type_traits",
484+
"//tensorstore/kvstore",
475485
"//tensorstore/util:executor",
476-
"//tensorstore/util:option",
477-
"//tensorstore/util:result",
486+
"//tensorstore/util:span",
478487
"//tensorstore/util:str_cat",
479488
"//tensorstore/util:unit",
480489
"@abseil-cpp//absl/status",
@@ -487,12 +496,8 @@ pybind11_cc_library(
487496

488497
pybind11_cc_library(
489498
name = "data_type",
490-
srcs = [
491-
"data_type.cc",
492-
],
493-
hdrs = [
494-
"data_type.h",
495-
],
499+
srcs = ["data_type.cc"],
500+
hdrs = ["data_type.h"],
496501
deps = [
497502
":json_type_caster",
498503
":numpy",
@@ -520,6 +525,7 @@ pybind11_cc_library(
520525
":array_type_caster",
521526
":batch",
522527
":context",
528+
":critical_section",
523529
":data_type",
524530
":define_heap_type",
525531
":future",
@@ -531,6 +537,7 @@ pybind11_cc_library(
531537
":json_type_caster",
532538
":keyword_arguments",
533539
":kvstore",
540+
":locking_type_casters",
534541
":result_type_caster",
535542
":sequence_parameter",
536543
":serialization",
@@ -599,6 +606,7 @@ pybind11_cc_library(
599606
"//tensorstore/serialization",
600607
"//tensorstore/util:executor",
601608
"@com_github_pybind_pybind11//:pybind11",
609+
"@nlohmann_json//:json",
602610
],
603611
alwayslink = True,
604612
)
@@ -618,13 +626,38 @@ pybind11_cc_library(
618626
],
619627
)
620628

629+
pybind11_cc_library(
630+
name = "critical_section",
631+
hdrs = [
632+
"critical_section.h",
633+
"with_handle.h",
634+
],
635+
deps = ["@com_github_pybind_pybind11//:pybind11"],
636+
)
637+
638+
pybind11_cc_library(
639+
name = "locking_type_casters",
640+
hdrs = ["locking_type_casters.h"],
641+
deps = [
642+
":critical_section",
643+
"//tensorstore:array_storage_statistics",
644+
"//tensorstore:batch",
645+
"//tensorstore:chunk_layout",
646+
"//tensorstore:index_interval",
647+
"//tensorstore:schema",
648+
"//tensorstore/kvstore:generation",
649+
"//tensorstore/kvstore:key_range",
650+
"@com_github_pybind_pybind11//:pybind11",
651+
],
652+
)
653+
621654
pybind11_cc_library(
622655
name = "garbage_collection",
623656
srcs = ["garbage_collection.cc"],
624657
hdrs = ["garbage_collection.h"],
625658
deps = [
659+
":critical_section",
626660
":define_heap_type",
627-
":gil_safe",
628661
":tensorstore_module_components",
629662
"//tensorstore/internal:global_initializer",
630663
"//tensorstore/internal:intrusive_ptr",
@@ -642,6 +675,7 @@ pybind11_cc_library(
642675
srcs = ["future.cc"],
643676
hdrs = ["future.h"],
644677
deps = [
678+
":critical_section",
645679
":define_heap_type",
646680
":garbage_collection",
647681
":gil_safe",
@@ -658,8 +692,10 @@ pybind11_cc_library(
658692
"//tensorstore/util:executor",
659693
"//tensorstore/util:future",
660694
"//tensorstore/util:result",
695+
"@abseil-cpp//absl/base:core_headers",
661696
"@abseil-cpp//absl/functional:function_ref",
662697
"@abseil-cpp//absl/status",
698+
"@abseil-cpp//absl/synchronization",
663699
"@abseil-cpp//absl/time",
664700
"@com_github_pybind_pybind11//:pybind11",
665701
],
@@ -710,6 +746,7 @@ pybind11_cc_library(
710746
hdrs = ["numpy_indexing_spec.h"],
711747
deps = [
712748
":array_type_caster",
749+
":critical_section",
713750
":data_type",
714751
":index",
715752
":numpy",
@@ -723,15 +760,13 @@ pybind11_cc_library(
723760
"//tensorstore:index",
724761
"//tensorstore:rank",
725762
"//tensorstore:static_cast",
726-
"//tensorstore/index_space:dimension_identifier",
727-
"//tensorstore/index_space:dimension_index_buffer",
728-
"//tensorstore/index_space:index_transform",
729763
"//tensorstore/index_space:numpy_indexing_spec",
730764
"//tensorstore/util:byte_strided_pointer",
731765
"//tensorstore/util:iterate",
732766
"//tensorstore/util:span",
733767
"//tensorstore/util:str_cat",
734768
"@abseil-cpp//absl/base:core_headers",
769+
"@abseil-cpp//absl/meta:type_traits",
735770
"@abseil-cpp//absl/status",
736771
"@com_github_pybind_pybind11//:pybind11",
737772
],
@@ -808,7 +843,7 @@ tensorstore_pytest_test(
808843
name = "future_test",
809844
size = "medium",
810845
srcs = ["tests/future_test.py"],
811-
tags = ["cpu:2"],
846+
tags = ["cpu:4"],
812847
deps = [
813848
":conftest",
814849
":tensorstore",
@@ -819,6 +854,7 @@ tensorstore_pytest_test(
819854
name = "chunk_layout_test",
820855
size = "small",
821856
srcs = ["tests/chunk_layout_test.py"],
857+
tags = ["cpu:2"],
822858
deps = [
823859
":conftest",
824860
":tensorstore",
@@ -870,6 +906,8 @@ pybind11_cc_library(
870906
srcs = ["batch.cc"],
871907
hdrs = ["batch.h"],
872908
deps = [
909+
":critical_section",
910+
":locking_type_casters",
873911
":tensorstore_module_components",
874912
"//tensorstore:batch",
875913
"//tensorstore/internal:global_initializer",
@@ -955,11 +993,13 @@ pybind11_cc_library(
955993
hdrs = ["chunk_layout.h"],
956994
deps = [
957995
":array_type_caster",
996+
":critical_section",
958997
":homogeneous_tuple",
959998
":index",
960999
":index_space",
9611000
":json_type_caster",
9621001
":keyword_arguments",
1002+
":locking_type_casters",
9631003
":result_type_caster",
9641004
":sequence_parameter",
9651005
":serialization",
@@ -979,6 +1019,7 @@ pybind11_cc_library(
9791019
"//tensorstore/util:str_cat",
9801020
"@abseil-cpp//absl/status",
9811021
"@com_github_pybind_pybind11//:pybind11",
1022+
"@nlohmann_json//:json",
9821023
],
9831024
alwayslink = True,
9841025
)
@@ -1041,6 +1082,7 @@ pybind11_cc_library(
10411082
srcs = ["serialization.cc"],
10421083
hdrs = ["serialization.h"],
10431084
deps = [
1085+
":critical_section",
10441086
":garbage_collection",
10451087
":gil_safe",
10461088
":result_type_caster",
@@ -1059,6 +1101,7 @@ pybind11_cc_library(
10591101
"@abseil-cpp//absl/functional:function_ref",
10601102
"@abseil-cpp//absl/status",
10611103
"@abseil-cpp//absl/strings:cord",
1104+
"@abseil-cpp//absl/synchronization",
10621105
"@com_github_pybind_pybind11//:pybind11",
10631106
"@riegeli//riegeli/bytes:cord_writer",
10641107
"@riegeli//riegeli/bytes:string_reader",
@@ -1075,11 +1118,13 @@ pybind11_cc_library(
10751118
deps = [
10761119
":batch",
10771120
":context",
1121+
":critical_section",
10781122
":define_heap_type",
10791123
":future",
10801124
":garbage_collection",
10811125
":json_type_caster",
10821126
":keyword_arguments",
1127+
":locking_type_casters",
10831128
":result_type_caster",
10841129
":serialization",
10851130
":status",
@@ -1100,6 +1145,7 @@ pybind11_cc_library(
11001145
"//tensorstore/kvstore:key_range",
11011146
"//tensorstore/util:executor",
11021147
"//tensorstore/util:future",
1148+
"//tensorstore/util:result",
11031149
"//tensorstore/util:str_cat",
11041150
"//tensorstore/util/garbage_collection",
11051151
"@abseil-cpp//absl/status",
@@ -1165,6 +1211,7 @@ pybind11_cc_library(
11651211
":garbage_collection",
11661212
":gil_safe",
11671213
":keyword_arguments",
1214+
":locking_type_casters",
11681215
":result_type_caster",
11691216
":serialization",
11701217
":spec",

python/tensorstore/batch.cc

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
#include <optional>
2323
#include <utility>
2424

25+
#include "python/tensorstore/critical_section.h"
2526
#include "python/tensorstore/tensorstore_module_components.h"
27+
#include "python/tensorstore/with_handle.h"
2628
#include "tensorstore/batch.h"
2729
#include "tensorstore/internal/global_initializer.h"
2830
#include "tensorstore/util/executor.h"
@@ -135,12 +137,15 @@ Operations
135137
}
136138

137139
void DefineBatchAttributes(BatchCls& cls) {
138-
using Self = Batch;
139140
cls.def(py::init([]() { return Batch::New(); }), R"(
140141
Creates a new batch.
141142
)");
142143
cls.def(
143-
"submit", [](Self& self) { self.Release(); },
144+
"submit",
145+
[](with_handle<Batch&> self) {
146+
ScopedPyCriticalSection cs(self.handle.ptr());
147+
self.value.Release();
148+
},
144149
R"(
145150
Submits the batch.
146151
@@ -153,9 +158,15 @@ will result in an error.
153158
Group:
154159
Operations
155160
)");
156-
cls.def("__enter__", [](const Self& self) { return &self; });
157-
cls.def("__exit__", [](Self& self, py::object exc_type, py::object exc_value,
158-
py::object traceback) { self.Release(); });
161+
cls.def("__enter__", [](with_handle<const Batch&> self) {
162+
ScopedPyCriticalSection cs(self.handle.ptr());
163+
return &self.value;
164+
});
165+
cls.def("__exit__", [](with_handle<Batch&> self, py::object exc_type,
166+
py::object exc_value, py::object traceback) {
167+
ScopedPyCriticalSection cs(self.handle.ptr());
168+
self.value.Release();
169+
});
159170
}
160171

161172
void RegisterBatchBindings(pybind11::module m, Executor defer) {

python/tensorstore/batch.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include <optional>
2323

24+
#include "python/tensorstore/locking_type_casters.h" // IWYU pragma: keep
2425
#include "tensorstore/batch.h"
2526

2627
namespace tensorstore {

0 commit comments

Comments
 (0)