Skip to content

Commit fea3304

Browse files
laramielcopybara-github
authored andcommitted
Add a test case that exercises pybind11_protobuf use in parallel.
Tests both with and without the GIL held, and it uncovered an issue in pybind11 handling of std::string_view when the GIL is not held. BUG: pybind/pybind11#2765 The bug is that the string_view constructs an object which should be placed on life_support, but due to the underlying mechanism it is not retained in a multi-threaded environment. PiperOrigin-RevId: 394360131
1 parent 8c8d2f1 commit fea3304

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed

pybind11_protobuf/tests/BUILD

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,27 @@ py_test(
208208
"@com_google_absl_py//absl/testing:parameterized",
209209
],
210210
)
211+
212+
pybind_extension(
213+
name = "thread_module",
214+
srcs = ["thread_module.cc"],
215+
deps = [
216+
":test_cc_proto",
217+
"//pybind11_protobuf:native_proto_caster",
218+
"@com_google_absl//absl/time",
219+
"@com_google_protobuf//:protobuf",
220+
],
221+
)
222+
223+
py_test(
224+
name = "thread_module_test",
225+
srcs = ["thread_module_test.py"],
226+
data = [":thread_module.so"],
227+
python_version = "PY3",
228+
srcs_version = "PY3",
229+
deps = [
230+
":test_py_pb2",
231+
"@com_google_absl_py//absl/testing:absltest",
232+
"@com_google_absl_py//absl/testing:parameterized",
233+
],
234+
)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright (c) 2021 The Pybind Development Team. All rights reserved.
2+
//
3+
// All rights reserved. Use of this source code is governed by a
4+
// BSD-style license that can be found in the LICENSE file.
5+
6+
#include <pybind11/pybind11.h>
7+
8+
#include <functional>
9+
#include <memory>
10+
#include <stdexcept>
11+
12+
#include "absl/time/clock.h"
13+
#include "absl/time/time.h"
14+
#include "pybind11_protobuf/native_proto_caster.h"
15+
#include "pybind11_protobuf/tests/test.pb.h"
16+
17+
namespace py = ::pybind11;
18+
19+
using pybind11::test::TestMessage;
20+
21+
namespace {
22+
23+
PYBIND11_MODULE(thread_module, m) {
24+
m.def(
25+
"make_message",
26+
[](std::string text) -> TestMessage {
27+
TestMessage msg;
28+
msg.set_string_value(std::move(text));
29+
return msg;
30+
},
31+
py::arg("text") = "");
32+
33+
m.def(
34+
"make_message_string_view",
35+
[](std::string_view text) -> TestMessage {
36+
TestMessage msg;
37+
msg.set_string_value(std::string(text));
38+
return msg;
39+
},
40+
py::arg("text") = "");
41+
42+
m.def(
43+
"make_message_no_gil",
44+
[](std::string text) -> TestMessage {
45+
TestMessage msg;
46+
msg.set_string_value(std::move(text));
47+
return msg;
48+
},
49+
py::arg("text") = "", py::call_guard<py::gil_scoped_release>());
50+
51+
m.def(
52+
"make_message_string_view_no_gil",
53+
[](std::string_view text) -> TestMessage {
54+
TestMessage msg;
55+
msg.set_string_value(std::string(text));
56+
return msg;
57+
},
58+
py::arg("text") = "", py::call_guard<py::gil_scoped_release>());
59+
}
60+
61+
} // namespace
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""Test pybind11_protobuf with multiple threads.
2+
3+
Run with `blaze test :thread_test --config=tsan`.
4+
"""
5+
6+
import concurrent.futures
7+
8+
from absl.testing import absltest
9+
from absl.testing import parameterized
10+
from pybind11_protobuf.tests import thread_module
11+
12+
13+
def make_message(x):
14+
return thread_module.make_message(x)
15+
16+
17+
def make_message_string_view(x):
18+
return thread_module.make_message_string_view(x)
19+
20+
21+
def make_message_no_gil(x):
22+
return thread_module.make_message_no_gil(x)
23+
24+
25+
def make_message_string_view_no_gil(x):
26+
return thread_module.make_message_string_view_no_gil(x)
27+
28+
29+
class ThreadTest(parameterized.TestCase):
30+
31+
@parameterized.named_parameters(
32+
('make_message', make_message),
33+
('make_message_string_view', make_message_string_view),
34+
('make_message_no_gil', make_message_no_gil),
35+
# BUG: https://github.com/pybind/pybind11/issues/2765
36+
# The following fails due to std::string_view casting in pybind11
37+
# ('make_message_string_view_no_gil', make_message_string_view_no_gil),
38+
)
39+
def test_parallel(self, fn):
40+
fn('a')
41+
42+
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
43+
results = list(executor.map(fn, ['abc'] * 10))
44+
45+
for x in results:
46+
self.assertEqual('abc', x.string_value)
47+
48+
49+
if __name__ == '__main__':
50+
absltest.main()

0 commit comments

Comments
 (0)