Simple coroutine leaking with GCC 10 - c++

Consider the following simple coroutine, which tracks its construction and destruction:
#include <coroutine>
#include <iostream>
struct simple {
static inline int x = 0;
int id = 0;
simple() : id{ x++ } { std::cout << id << " constructed\n"; }
simple(simple&&) : id{ x++ } { std::cout << id << " move constructed\n"; }
~simple() { std::cout << id << " destructed\n"; }
struct promise_type {
simple get_return_object() { return {}; }
void return_void() {}
void unhandled_exception() { std::terminate(); }
auto initial_suspend() noexcept { return std::suspend_never{}; }
auto final_suspend() noexcept { return std::suspend_never{}; }
};
};
simple f() { co_return; }
int main() {
f();
}
When compiling with GCC 10.1, the output is:
0 constructed
1 move constructed
1 destructed
—notably, id 0 is never destructed. This is true even with GCC trunk.
Is there something I've done wrong here, some hidden UB or the like? Or is GCC at fault here?
As a comparison, I tried using Clang's experimental coroutine support, and got
0 constructed
0 destructed
which is closer to what I was expecting. I also get this "correct" output even in GCC if I comment out the move constructor in the coroutine object.
Live example: https://godbolt.org/z/UQVURi

As Oliv mentioned, it appears this is just a bug with GCC's experimental coroutine support. A ticket has been raised here.

Related

Calling destroy() from final_suspend() results in a crash

I call h.destroy() in final_suspend to destroy the coroutine automatically when it finishes execution and then I resume awaiting coroutine (that awaits the task to complete). I found a question about this technique and an answer explaining why it should work.
As far as I can see, this technique really works, but not with MSVC 2022 that calls task destructor twice, see the code below:
#include <coroutine>
#include <optional>
#include <iostream>
#include <thread>
#include <chrono>
#include <queue>
#include <vector>
// simple timers
// stored timer tasks
struct timer_task
{
std::chrono::steady_clock::time_point target_time;
std::coroutine_handle<> handle;
};
// comparator
struct timer_task_before_cmp
{
bool operator()(const timer_task& left, const timer_task& right) const
{
return left.target_time > right.target_time;
}
};
std::priority_queue<timer_task, std::vector<timer_task>, timer_task_before_cmp> timers;
inline void submit_timer_task(std::coroutine_handle<> handle, std::chrono::nanoseconds timeout)
{
timers.push(timer_task{ std::chrono::steady_clock::now() + timeout, handle });
}
//template <bool owning>
struct UpdatePromise;
//template <bool owning>
struct UpdateTask
{
// declare promise type
using promise_type = UpdatePromise;
UpdateTask(std::coroutine_handle<promise_type> handle) :
handle(handle)
{
std::cout << "UpdateTask constructor." << std::endl;
}
UpdateTask(const UpdateTask&) = delete;
UpdateTask(UpdateTask&& other) : handle(other.handle)
{
std::cout << "UpdateTask move constructor." << std::endl;
}
UpdateTask& operator = (const UpdateTask&) = delete;
UpdateTask& operator = (const UpdateTask&& other)
{
handle = other.handle;
std::cout << "UpdateTask move assignment." << std::endl;
return *this;
}
~UpdateTask()
{
std::cout << "UpdateTask destructor." << std::endl;
}
std::coroutine_handle<promise_type> handle;
};
struct UpdatePromise
{
std::coroutine_handle<> awaiting_coroutine;
UpdateTask get_return_object();
std::suspend_never initial_suspend()
{
return {};
}
void unhandled_exception()
{
std::terminate();
}
auto final_suspend() noexcept
{
// if there is a coroutine that is awaiting on this coroutine resume it
struct transfer_awaitable
{
std::coroutine_handle<> awaiting_coroutine;
// always stop at final suspend
bool await_ready() noexcept
{
return false;
}
std::coroutine_handle<> await_suspend(std::coroutine_handle<UpdatePromise> h) noexcept
{
// resume awaiting coroutine or if there is no coroutine to resume return special coroutine that do
// nothing
std::coroutine_handle<> val = awaiting_coroutine ? awaiting_coroutine : std::noop_coroutine();
h.destroy();
return val;
}
void await_resume() noexcept {}
};
return transfer_awaitable{ awaiting_coroutine };
}
void return_void() {}
// use `co_await std::chrono::seconds{n}` to wait specified amount of time
auto await_transform(std::chrono::milliseconds d)
{
struct timer_awaitable
{
std::chrono::milliseconds m_d;
// always suspend
bool await_ready()
{
return m_d <= std::chrono::milliseconds(0);
}
// h is a handler for current coroutine which is suspended
void await_suspend(std::coroutine_handle<> h)
{
// submit suspended coroutine to be resumed after timeout
submit_timer_task(h, m_d);
}
void await_resume() {}
};
return timer_awaitable{ d };
}
// also we can await other UpdateTask<T>
auto await_transform(UpdateTask& update_task)
{
if (!update_task.handle)
{
throw std::runtime_error("coroutine without promise awaited");
}
if (update_task.handle.promise().awaiting_coroutine)
{
throw std::runtime_error("coroutine already awaited");
}
struct task_awaitable
{
std::coroutine_handle<UpdatePromise> handle;
// check if this UpdateTask already has value computed
bool await_ready()
{
return handle.done();
}
// h - is a handle to coroutine that calls co_await
// store coroutine handle to be resumed after computing UpdateTask value
void await_suspend(std::coroutine_handle<> h)
{
handle.promise().awaiting_coroutine = h;
}
// when ready return value to a consumer
auto await_resume()
{
}
};
return task_awaitable{ update_task.handle };
}
};
inline UpdateTask UpdatePromise::get_return_object()
{
return { std::coroutine_handle<UpdatePromise>::from_promise(*this) };
}
// timer loop
void loop()
{
while (!timers.empty())
{
auto& timer = timers.top();
// if it is time to run a coroutine
if (timer.target_time < std::chrono::steady_clock::now())
{
auto handle = timer.handle;
timers.pop();
handle.resume();
}
else
{
std::this_thread::sleep_until(timer.target_time);
}
}
}
// example
using namespace std::chrono_literals;
UpdateTask TestTimerAwait()
{
using namespace std::chrono_literals;
std::cout << "testTimerAwait started." << std::endl;
co_await 1s;
std::cout << "testTimerAwait finished." << std::endl;
}
UpdateTask TestNestedTimerAwait()
{
using namespace std::chrono_literals;
std::cout << "testNestedTimerAwait started." << std::endl;
auto task = TestTimerAwait();
co_await 2s;
//co_await task;
std::cout << "testNestedTimerAwait finished." << std::endl;
}
// main can't be a coroutine and usually need some sort of looper (io_service or timer loop in this example)
int main()
{
auto task = TestNestedTimerAwait();
// execute deferred coroutines
loop();
}
the output with MSVC 2022 is:
UpdateTask constructor.
testNestedTimerAwait started.
UpdateTask constructor.
testTimerAwait started.
testTimerAwait finished.
testNestedTimerAwait finished.
UpdateTask destructor.
UpdateTask destructor.
UpdateTask destructor.
but the output with GCC 11.1.0 is:
UpdateTask constructor.
testNestedTimerAwait started.
UpdateTask constructor.
testTimerAwait started.
testTimerAwait finished.
testNestedTimerAwait finished.
UpdateTask destructor.
UpdateTask destructor.
as you can see there is one extra destructor call with MSVC 2022, so the behaviour of the code generated with MSVC 2022 is undefined and it can potentially format your hard drive.
MSVC 2022 version: Microsoft (R) C/C++ Optimizing Compiler Version 19.30.30709 for x86
EDIT9:
Figured out what happens. The destructor of UpdateTask is called twice with MSVC 2022, see updated code.
EDIT10:
From docs: The coroutine is suspended (its coroutine state is populated with local variables and current suspension point).
awaiter.await_suspend(handle) is called, where handle is the coroutine handle representing the current coroutine. Inside that function, the suspended coroutine state is observable via that handle, and it's this function's responsibility to schedule it to resume on some executor, or to be destroyed (returning false counts as scheduling)
Looks like it was a compiler bug, that is probably fixed in Microsoft (R) C/C++ Optimizing Compiler Version 19.31.31106.2 for x86, at least now the output is:
UpdateTask constructor.
testNestedTimerAwait started.
UpdateTask constructor.
testTimerAwait started.
testTimerAwait finished.
testNestedTimerAwait finished.
UpdateTask destructor.
UpdateTask destructor.

Cppcoro not accepting result from an constom awaitable struct

I decided to write my own awaitable in order to loader to learn how C++ coroutine works. For now, I want to build my own struct that is equivalent to this:
cppcoro::task<int> bar()
{
co_yield 42;
}
This is what I came up after reading CppReference's coroutine page. Which states Finally, awaiter.await_resume() is called, and its result is the result of the whole co_await expr expression. I assume that changing the return type of await_resume() is enough to gave both bar and make_awaitable the same functionality.
#include <iostream>
#include <coroutine>
#include <cppcoro/task.hpp>
#include <cppcoro/sync_wait.hpp>
auto make_awaitable() {
using return_type = int;
struct awaitable {
bool await_ready() {return false;}
void await_suspend(std::coroutine_handle<> h) {
std::cout << "In await_suspend()" << std::endl;
h.resume();
}
int await_resume() {return 42;};
};
return awaitable{};
}
cppcoro::task<int> foo()
{
int n = co_await make_awaitable();
std::cout << n << std::endl;
}
int main()
{
cppcoro::sync_wait(foo());
std::cout << "Called coro" << std::endl;
return 0;
}
But running the code generates an assertion error.
coro_test: /usr/include/cppcoro/task.hpp:187: cppcoro::detail::task_promise<T>::rvalue_type cppcoro::detail::task_promise<T>::result() && [with T = int; cppcoro::detail::task_promise<T>::rvalue_type = int]: Assertion `m_resultType == result_type::value' failed.
What am I doing wrong?
Compiler: GCC 10
The problem is, I think, that you declared your return type as task<int> but you don't actually co_return any int.
Does the problem go away if you co_return n?

Use std::function as member function, which capture `this`, and access it from the copied lambda after destructor

Flex Ferrum post a code sample here (I think it is Minimal, Complete, and Verifiable enough):
#include <iostream>
#include <functional>
using namespace std;
class Bang
{
public:
Bang(int i = 0) : m_val(i)
{
m_foo = [this] {std::cout << m_val << std::endl;};
}
~Bang()
{
m_val = -1;
}
void Foo()
{
m_foo();
}
private:
int m_val;
std::function<void ()> m_foo;
};
Bang GetBang()
{
return Bang(100500);
}
int main() {
Bang b(100500);
b.Foo();
b = GetBang();
b.Foo();
return 0;
}
Our nice Flex also offer a live demo
After a rough look, I thought it will output 100500, but the real output is:
-1
Why? What's behind it?
How to fix it?(output 100500, not -1)
I have written some my own reasonings in the ask box, but found it is more fit to be posted as an answer(will make the question too long). If my answer is wrong, please correct it and more answers are welcome
Ah, it should blame the destructor of temporary - Bang(100500), which returns form GetBang, is prvalue, and has temporary object lifetime.
[this] will be stored as reference of *this, like this:
class Lambda
{
public:
void operator()() const
{
//output
}
private:
Bang& bang;
public:
Lambda(Bang& bang) : bang{bang}
{
}
} lambda{*this};
...
m_foo = lambda;
Because here is no RVO, so, the temporary Bang(100500) will first be assigned to b, then be destroyed.
Custorm operator(), constructor, and destructor to output some information:
#include <iostream>
#include <functional>
using namespace std;
class Bang
{
public:
Bang(int i = 0) : m_val(i)
{
std::cout << "Bang(int i = 0) m_val address is " << &m_val << '\n';
class Lambda
{
public:
void operator()() const
{
std::cout << "operator() m_val address is " << &bang.m_val << '\n';
std::cout << bang.m_val << std::endl;
}
private:
Bang &bang;
public:
Lambda(Bang &bang) : bang{bang}
{
}
} lambda{*this};
m_foo = lambda;
}
~Bang()
{
std::cout << "~Bang()\n";
m_val = -1;
}
void Foo()
{
m_foo();
}
private:
int m_val;
std::function<void()> m_foo;
};
Bang GetBang()
{
return Bang(100500);
}
int main()
{
Bang b;
b = GetBang();
b.Foo();
return 0;
}
live demo
Output:
Bang(int i = 0) m_val address is 0x7ffd202c48b0
Bang(int i = 0) m_val address is 0x7ffd202c48e0
~Bang()
operator() m_val address is 0x7ffd202c48e0
-1
~Bang()
shows:
dtor will be called before output, That means that the temporary object has been destroyed.
m_value's address doesn't change.
The two guaranteed we still access the temporary's m_value from the b's m_foo().
It should be Undefined Behaviour to access an object which has be destroyed, but no warning and errors required.
Update
To solve the problem, there two solutions:
Like #Killzone Kid points out, capture with an initializer: [bang = *this]. This requires c++14.
More simpler way to capture of the current object by-copy: [*this]. This requires c++17. live demo
You probably want to pass current object to lambda by value *this so that it can be stored and copied when you copy assign Bang. Passing pointer this will store and copy pointer to the temp object that has been destroyed when you copy assign Bang.
This works as it should:
#include <iostream>
#include <functional>
class Bang
{
public:
Bang(int i = 0) : m_val(i)
{
m_foo = [bang = *this] { std::cout << bang.m_val << std::endl; };
}
~Bang()
{
m_val = -1;
}
void Foo()
{
m_foo();
}
private:
int m_val;
std::function<void()> m_foo;
};
Bang GetBang()
{
return Bang(100500);
}
int main()
{
Bang b;
b = GetBang();
b.Foo();
return 0;
}
Demo: https://ideone.com/LUDrBb

std::lock_guard causing undefined behavior

Edit: As it seems, the problem was me not actually creating a local instance of a lock_guard, but merely an anonymous temporary, which got destroyed again immediately, as pointed out by the comments below.
Edit2: Enabling clang's thread sanitizer helps to pinpoint these kinds of problems at run-time. It can be enabled via
clang++ -std=c++14 -stdlib=libc++ -fsanitize=thread *.cpp -pthread
This is probably in some way a duplicate question, but I couldn't find anything, so if it really is duplicate I'm sorry. This should be a beginner question anyway.
I was playing around with a simple "Counter" class, say inline in file
Counter.hpp:
#ifndef CLASS_COUNTER_HPP_
#define CLASS_COUNTER_HPP_
#include <mutex>
#include <string>
#include <exception>
class Counter
{
public:
explicit Counter(std::size_t v = 0) : value_{v} {}
std::size_t value() const noexcept { return value_; }
// void increment() { ++value_; } // not an atomic operation : ++value_ equals value_ = value_ + 1
// --> 3 operations: read, add, assign
void increment() noexcept
{
mutex_.lock();
++value_;
mutex_.unlock();
}
// void decrement() noexcept
// {
// mutex_.lock();
// --value_; // possible underflow
// mutex_.unlock();
// }
void decrement()
{
std::lock_guard<std::mutex>{mutex_};
if (value_ == 0)
{
std::string message{"New Value ("+std::to_string(value_-1)+") too low, must be at least 0"};
throw std::logic_error{message};
}
--value_;
}
private:
std::size_t value_;
std::mutex mutex_;
};
#endif
In main.cpp a Counter instance is supposed to be incremented and decremented
concurrently:
main.cpp:
#include <iostream>
#include <iomanip>
#include <array>
#include <thread>
#include <exception>
#include "Counter.hpp"
int
main ()
{
Counter counter{};
std::array<std::thread,4> threads;
auto operation = [&counter]()
{
for (std::size_t i = 0; i < 125; ++i)
counter.increment();
};
// std::for_each(begin(threads),end(threads),[&operation](auto& val) { val = std::thread{operation}; });
std::cout << "Incrementing Counter (" << std::setw(3) << counter.value() << ") concurrently...";
for (auto& t : threads)
{
t = std::thread{operation};
}
for (auto& t : threads)
t.join();
std::cout << " new value == " << counter.value() << '\n';
auto second_operation = [&counter]()
{
for (std::size_t i = 0; i < 125; ++i)
{
try
{
counter.decrement();
}
catch(const std::exception& e)
{
std::cerr << "\n***Exception while trying to decrement : " << e.what() << "***\n";
}
}
};
std::cout << "Decrementing Counter (" << std::setw(3) << counter.value() << ") concurrently...";
for (auto& t : threads)
t = std::thread{second_operation};
for (auto& t : threads)
t.join();
std::cout << " new value == " << counter.value() << '\n';
return 0;
The exception handling seems to work as it's supposed to, and the way I understand it std::lock_guard is supposed to guarantee unlocking a mutex once the lock_guard goes out of scope.
However it seems to be more complicated than that. While the incrementation correctly results in a final value of "500", the decrementation - which is supposed to result in "0" - doesn't work out. The result will be something between "0" and "16" or so.
If the timing is changed, for instance by using valgrind, it seems to work correctly every time.
I was able to pinpoint the problem to the use of std::lock_guard. If I define the decrement() function as this :
void decrement() noexcept
{
mutex_.lock();
--value_; // possible underflow
mutex_.unlock();
}
everything works out fine ( as long as there is no underflow).
But once I make a simple change to:
void decrement() noexcept
{
std::lock_guard<std::mutex>{mutex_};
--value_; // possible underflow
}
the behavior is like I described above. I presume I did not really understand the behavior and use cases of std::lock_guard. I would really appreciate it if you could point me into the right direction!
The program compiles via clang++ -std=c++14 -stdlib=libc++ *.cpp -pthread.
std::lock_guard<std::mutex>{mutex_}; Does not create a local. It creates a temporary which is destroyed at the end of the statement. This means your value is not protected by the lock. The lock guard must be a local:
void decrement() noexcept
{
std::lock_guard<std::mutex> guard {mutex_};
--value_; // possible underflow
}
The problem is that the line
std::lock_guard<std::mutex>{mutex_};
does not create a variable, but rather creates a temporary lock_guard object which gets destroyed again immediately. What you probably meant to write was:
std::lock_guard<std::mutex> guard{mutex_};
This creates a variable of type lock_guard, named guard, which gets destroyed when it leaves the scope (i.e. at the end of the function. Essentially, you forgot to name your variable.

C++14 Calling a constructor [with different arguments] from a different constructor (same class)

I wrote a program today which was giving me a real headache when it didn't work how I expected it to.
So I've written a short example which (almost)* reproduces the problem.
I hope this is fairly self-explanatory in what it is supposed to do.
#include <iostream>
class A
{
public:
enum class defaults
{
DEFAULT_A
};
A(defaults default_val)
{
if(default_val == defaults::DEFAULT_A)
{
A("hello world");
}
}
A(std::string str_)
{
str = str_;
flag = true;
}
std::string getStr()
{
return str;
}
bool getFlag()
{
return flag;
}
private:
bool flag;
std::string str;
};
int main()
{
A a(A::defaults::DEFAULT_A);
std::cout << a.getStr() << std::endl;
std::cout << a.getStr().size() << std::endl;
if(a.getFlag())
{
std::cout << "true" << std::endl;
}
else
{
std::cout << "false" << std::endl;
}
}
My compile and run code: g++ --std=c++14 main.cpp && ./a.out
Compiling on/with gcc version 5.2.1 20151010 (Ubuntu 5.2.1-22ubuntu2)
Output: [with line numbers]
1:
2: 0
3: true
(That first line is a blank line.)
*The only thing which is different from this example and the code I was working on today is that the flag in the code I wrote earlier was false not true, but this is still NOT what I expected it to be - I expected it to be true. And also, the string wasn't blank, but it did contain "nonsense values" - at least not what I expected it to contain.
What have I done wrong here? My guess is it's something really obvious, but I just can't spot it. (Perhaps I've been working on the same problem for too long now?)
Edit:
Can I do this to correct the problem?
A(defaults default_val)
{
if(default_val == defaults::DEFAULT_A)
{
*this = A("hello world");
}
}
To call a constructor from another constructor (a delegating constructor), you need to do it in the initializer list of the constructor:
A(defaults default_val) : A("hello world") {
.. more stuff to do after alling the delegate ctor
The problem is that this just allows you to make an unconditional call of the delegate ctor (though you can use ?: in the argument(s) to have their value be conditional.) There's no way to conditionally call the delegate.
If you always want to call the other constructor, you can move the logic that figures out the correct argument into another function.
Then you can use a delegating constructor as follows:
#include <cassert>
#include <iostream>
#include <string>
struct A
{
enum class defaults { first, second };
A(defaults default_)
: A(DefaultToString(default_))
{ }
A(const std::string & str)
: s_(str)
{ }
static std::string DefaultToString(defaults default_)
{
switch (default_)
{
case defaults::first: return "first";
case defaults::second: return "second";
}
assert(false);
}
std::string s_;
};
int main()
{
A a1(A::defaults::first);
A a2(A::defaults::second);
A a3("other");
std::cout << a1.s_ << " " << a2.s_ << " " << a3.s_ << "\n";
return 0;
}
A(defaults default_val)
{
if(default_val == defaults::DEFAULT_A)
{
A("hello world");
}
}
If this if statement is true, you're not calling this object's other constructor.
Here, A("hello world") constructs a new temporary object, using the given this constructor ... which is immediately destroyed, because this is a temporary object.
You may use helper function for your delegating constructor:
namespace {
A MakeA(defaults default_val)
{
return (default_val == defaults::DEFAULT_A) ?
A("hello world") :
A(/* other constructor*/);
}
}
A::A(defaults default_val) : A(MakeA(default_val)) {}
As currently, you create a unused temporary.