Skip to content

Commit dcc3f63

Browse files
committed
Restoring TestThread code with added std::lock_guard<std::mutex>.
1 parent f443f9f commit dcc3f63

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

tests/test_iostream.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313

1414
#include <pybind11/iostream.h>
1515
#include "pybind11_tests.h"
16+
#include <atomic>
1617
#include <iostream>
18+
#include <mutex>
1719
#include <string>
20+
#include <thread>
1821

1922
void noisy_function(const std::string &msg, bool flush) {
2023

@@ -28,6 +31,44 @@ void noisy_funct_dual(const std::string &msg, const std::string &emsg) {
2831
std::cerr << emsg;
2932
}
3033

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

3374
add_ostream_redirect(m);
@@ -69,4 +110,10 @@ TEST_SUBMODULE(iostream, m) {
69110
std::cout << msg << std::flush;
70111
std::cerr << emsg << std::flush;
71112
});
113+
114+
py::class_<TestThread>(m, "TestThread")
115+
.def(py::init<>())
116+
.def("stop", &TestThread::stop)
117+
.def("join", &TestThread::join)
118+
.def("sleep", &TestThread::sleep);
72119
}

tests/test_iostream.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,3 +306,26 @@ def test_redirect_both(capfd):
306306
assert stderr == ""
307307
assert stream.getvalue() == msg
308308
assert stream2.getvalue() == msg2
309+
310+
311+
def test_threading():
312+
with m.ostream_redirect(stdout=True, stderr=False):
313+
# start some threads
314+
threads = []
315+
316+
# start some threads
317+
for _j in range(20):
318+
threads.append(m.TestThread())
319+
320+
# give the threads some time to fail
321+
threads[0].sleep()
322+
323+
# stop all the threads
324+
for t in threads:
325+
t.stop()
326+
327+
for t in threads:
328+
t.join()
329+
330+
# if a thread segfaults, we don't get here
331+
assert True

0 commit comments

Comments
 (0)