Skip to content

Commit 6d02066

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: 394314149
1 parent 8c8d2f1 commit 6d02066

File tree

3 files changed

+123
-0
lines changed

3 files changed

+123
-0
lines changed

pybind11_protobuf/tests/BUILD

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,26 @@ 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+
"@com_google_absl_py//absl/testing:absltest",
231+
"@com_google_absl_py//absl/testing:parameterized",
232+
],
233+
)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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 "google/protobuf/dynamic_message.h"
13+
#include "absl/time/clock.h"
14+
#include "absl/time/time.h"
15+
#include "pybind11_protobuf/native_proto_caster.h"
16+
#include "pybind11_protobuf/tests/test.pb.h"
17+
18+
namespace py = ::pybind11;
19+
20+
using pybind11::test::TestMessage;
21+
22+
namespace {
23+
24+
PYBIND11_MODULE(thread_module, m) {
25+
m.def(
26+
"make_message",
27+
[](std::string text) -> TestMessage {
28+
absl::SleepFor(absl::Milliseconds(1));
29+
TestMessage msg;
30+
msg.set_string_value(std::move(text));
31+
return msg;
32+
},
33+
py::arg("text") = "");
34+
35+
m.def(
36+
"make_message_string_view",
37+
[](std::string_view text) -> TestMessage {
38+
absl::SleepFor(absl::Milliseconds(1));
39+
TestMessage msg;
40+
msg.set_string_value(std::string(text));
41+
return msg;
42+
},
43+
py::arg("text") = "");
44+
45+
m.def(
46+
"make_message_no_gil",
47+
[](std::string text) -> TestMessage {
48+
absl::SleepFor(absl::Milliseconds(1));
49+
TestMessage msg;
50+
msg.set_string_value(std::move(text));
51+
return msg;
52+
},
53+
py::arg("text") = "", py::call_guard<py::gil_scoped_release>());
54+
55+
m.def(
56+
"make_message_string_view_no_gil",
57+
[](std::string_view text) -> TestMessage {
58+
absl::SleepFor(absl::Milliseconds(1));
59+
TestMessage msg;
60+
msg.set_string_value(std::string(text));
61+
return msg;
62+
},
63+
py::arg("text") = "", py::call_guard<py::gil_scoped_release>());
64+
}
65+
66+
} // namespace
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""Test pybind11_protobuf with multiple threads.
2+
3+
Run with `blaze test :thread_test --config=tsan`.
4+
"""
5+
6+
import multiprocessing.pool
7+
8+
from absl.testing import absltest
9+
from absl.testing import parameterized
10+
from pybind11_protobuf.tests import thread_module as m
11+
12+
13+
class ThreadTest(parameterized.TestCase):
14+
15+
@parameterized.named_parameters(
16+
('make_message', m.make_message),
17+
('make_message_string_view', m.make_message_string_view),
18+
('make_message_no_gil', m.make_message_no_gil),
19+
# BUG: https://github.com/pybind/pybind11/issues/2765
20+
# The following fails due to std::string_view casting in pybind11
21+
# ('make_message_string_view_no_gil', m.make_message_string_view_no_gil),
22+
)
23+
def test_parallel(self, fn):
24+
data = ['abc'] * 10
25+
with multiprocessing.pool.ThreadPool(4) as pool:
26+
result = list(pool.map(fn, data))
27+
pool.close()
28+
pool.join()
29+
for x in result:
30+
self.assertEqual('abc', x.string_value)
31+
32+
33+
if __name__ == '__main__':
34+
absltest.main()

0 commit comments

Comments
 (0)