Skip to content

Commit 2fa4747

Browse files
nickbridgechessNick Bridgenbridge-jumpYannickJadoul
authored
pythonbuf fix (#2675)
* Added test_thread testing for ostream_redirect segfault recreation * fix: scoped_ostream_redirect str created outside gil * Moved threading tests into test_iostream. Cleaned up some formatting. Deleted test_thread.{cpp,py} * CI: few formatting fixes * CI: yet another formatting fix * CI: more formatting fixes. Removed unecessary comment * Ignore 'warning C4702: unreachable code' in MSVC 2015 Co-authored-by: Nick Bridge <[email protected]> Co-authored-by: Nick Bridge <[email protected]> Co-authored-by: Yannick Jadoul <[email protected]>
1 parent 17c22b9 commit 2fa4747

File tree

4 files changed

+74
-4
lines changed

4 files changed

+74
-4
lines changed

include/pybind11/iostream.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@ class pythonbuf : public std::streambuf {
4343
// simplified to a fully qualified call.
4444
int _sync() {
4545
if (pbase() != pptr()) {
46-
// This subtraction cannot be negative, so dropping the sign
47-
str line(pbase(), static_cast<size_t>(pptr() - pbase()));
4846

4947
{
5048
gil_scoped_acquire tmp;
49+
50+
// This subtraction cannot be negative, so dropping the sign.
51+
str line(pbase(), static_cast<size_t>(pptr() - pbase()));
52+
5153
pywrite(line);
5254
pyflush();
5355
}

tests/test_iostream.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@
77
BSD-style license that can be found in the LICENSE file.
88
*/
99

10+
#if defined(_MSC_VER) && _MSC_VER < 1910 // VS 2015's MSVC
11+
# pragma warning(disable: 4702) // unreachable code in system header (xatomic.h(382))
12+
#endif
1013

1114
#include <pybind11/iostream.h>
1215
#include "pybind11_tests.h"
16+
#include <atomic>
1317
#include <iostream>
18+
#include <thread>
1419

1520

1621
void noisy_function(std::string msg, bool flush) {
@@ -25,6 +30,40 @@ void noisy_funct_dual(std::string msg, std::string emsg) {
2530
std::cerr << emsg;
2631
}
2732

33+
// object to manage C++ thread
34+
// simply repeatedly write to std::cerr until stopped
35+
// redirect is called at some point to test the safety of scoped_estream_redirect
36+
struct TestThread {
37+
TestThread() : t_{nullptr}, stop_{false} {
38+
auto thread_f = [this] {
39+
while (!stop_) {
40+
std::cout << "x" << std::flush;
41+
std::this_thread::sleep_for(std::chrono::microseconds(50));
42+
} };
43+
t_ = new std::thread(std::move(thread_f));
44+
}
45+
46+
~TestThread() {
47+
delete t_;
48+
}
49+
50+
void stop() { stop_ = true; }
51+
52+
void join() {
53+
py::gil_scoped_release gil_lock;
54+
t_->join();
55+
}
56+
57+
void sleep() {
58+
py::gil_scoped_release gil_lock;
59+
std::this_thread::sleep_for(std::chrono::milliseconds(50));
60+
}
61+
62+
std::thread * t_;
63+
std::atomic<bool> stop_;
64+
};
65+
66+
2867
TEST_SUBMODULE(iostream, m) {
2968

3069
add_ostream_redirect(m);
@@ -70,4 +109,10 @@ TEST_SUBMODULE(iostream, m) {
70109
std::cout << msg << std::flush;
71110
std::cerr << emsg << std::flush;
72111
});
112+
113+
py::class_<TestThread>(m, "TestThread")
114+
.def(py::init<>())
115+
.def("stop", &TestThread::stop)
116+
.def("join", &TestThread::join)
117+
.def("sleep", &TestThread::sleep);
73118
}

tests/test_iostream.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,26 @@ def test_redirect_both(capfd):
216216
assert stderr == ""
217217
assert stream.getvalue() == msg
218218
assert stream2.getvalue() == msg2
219+
220+
221+
def test_threading():
222+
with m.ostream_redirect(stdout=True, stderr=False):
223+
# start some threads
224+
threads = []
225+
226+
# start some threads
227+
for _j in range(20):
228+
threads.append(m.TestThread())
229+
230+
# give the threads some time to fail
231+
threads[0].sleep()
232+
233+
# stop all the threads
234+
for t in threads:
235+
t.stop()
236+
237+
for t in threads:
238+
t.join()
239+
240+
# if a thread segfaults, we don't get here
241+
assert True

tests/test_smart_ptr.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
BSD-style license that can be found in the LICENSE file.
99
*/
1010

11-
#if defined(_MSC_VER) && _MSC_VER < 1910
12-
# pragma warning(disable: 4702) // unreachable code in system header
11+
#if defined(_MSC_VER) && _MSC_VER < 1910 // VS 2015's MSVC
12+
# pragma warning(disable: 4702) // unreachable code in system header (xatomic.h(382))
1313
#endif
1414

1515
#include "pybind11_tests.h"

0 commit comments

Comments
 (0)