Commit 44737253 authored by Kirill Smelkov's avatar Kirill Smelkov

libgolang: tests: It is not safe to capture by reference in go(<lambda>)

When lambda captures stack-resided variable by reference, it actually
remembers address of that variable and inside lambda code dereferences
that address on variable use. The lifetime of all spawned goroutines in
libgolang_test is subset of test function driver lifetime, so capturing
by reference should be safe in that situation on the first glance.
However for that to work, it is required that stacks of both goroutines
- the main goroutine and spawned goroutine - must be live at the same
time, so that spawned goroutine could safely retrieve a
reference-captured variable located on the main goroutine stack.

This works for thread runtime, but is known not to work for gevent
runtime, where inactive goroutine stack is swapped onto heap and is
generally considered "dead" while that goroutine is parked (see
"Implementation note" in 3b241983 "Port/move channels to C/C++/Pyx" for
details about this).

-> Fix the test by capturing by value in lambdas. What we capture is
usually chan<T> object, which itself is a pointer, so it should not make
a big difference in efficiency. It is also more safe to capture channels
by value, since that automatically incref/decref them and adds extra
protection wrt lifetime management bugs.

NOTE sending/receiving via channels from/to stack-based variables is
always safe - for both thread and gevent runtimes, as channels
implementation explicitly cares for this to work. Once again
"Implementation note" in 3b241983 has the details.
parent f2b77c94
......@@ -206,13 +206,13 @@ void _test_chan_vs_stackdeadwhileparked() {
// recv
auto ch = makechan<int>();
go([&]() {
go([ch]() {
waitBlocked_RX(ch);
usestack_and_call([&]() {
usestack_and_call([ch]() {
ch.send(111);
});
});
usestack_and_call([&]() {
usestack_and_call([ch]() {
int rx = ch.recv();
if (rx != 111)
panic("recv(111) != 111");
......@@ -220,28 +220,28 @@ void _test_chan_vs_stackdeadwhileparked() {
// send
auto done = makechan<structZ>();
go([&]() {
go([ch, done]() {
waitBlocked_TX(ch);
usestack_and_call([&]() {
usestack_and_call([ch]() {
int rx = ch.recv();
if (rx != 222)
panic("recv(222) != 222");
});
done.close();
});
usestack_and_call([&]() {
usestack_and_call([ch]() {
ch.send(222);
});
done.recv();
// select(recv)
go([&]() {
go([ch]() {
waitBlocked_RX(ch);
usestack_and_call([&]() {
usestack_and_call([ch]() {
ch.send(333);
});
});
usestack_and_call([&]() {
usestack_and_call([ch]() {
int rx = 0;
int _ = select({ch.recvs(&rx)});
if (_ != 0)
......@@ -252,16 +252,16 @@ void _test_chan_vs_stackdeadwhileparked() {
// select(send)
done = makechan<structZ>();
go([&]() {
go([ch, done]() {
waitBlocked_TX(ch);
usestack_and_call([&]() {
usestack_and_call([ch]() {
int rx = ch.recv();
if (rx != 444)
panic("recv(444) != 444");
});
done.close();
});
usestack_and_call([&]() {
usestack_and_call([ch]() {
int tx = 444;
int _ = select({ch.sends(&tx)});
if (_ != 0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment