lewissbaker / lewissbaker.github.io

Lewis Baker's Blog

Home Page:https://lewissbaker.github.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Discussion for Understanding the promise type

lewissbaker opened this issue · comments

Discussion for Understanding the promise type

Can co-routine help me writing cps style functions ?

I'm trying implement a async program by weaving call-stack to cps style continuation. The most annoying part I found is convert normal function to cps function, which looks like:

void function(arguments..., std::function<void(R)> return) {
    body1...
    otherfunction1(arguments..., [capture by std::move](otherfunction1_return_value) -> {
        body2
        otherfunction2(arguments..., [capture by std::move](otherfunction2_return_value) -> {
            body3
            return(my_return_value)
        }
    }
}

will co-routine be able to help me writing functions like this more easily ?

@kghost Yes, coroutines will be able to help you write functions like this more easily. That is one of their primary use-cases.

Instead of passing the continuation in as a parameter to the function, coroutines implicitly provide "the rest of the coroutine" as the continuation when you co_await something.

So your example above would become:

task<R> function(arguments...) {
  body1...
  auto otherfunction1_return_value = co_await otherfunction1(arguments...);
  body2...
  auto otherfunction2_return_value = co_await otherfunction2(arguments...);
  body3...
  co_return my_return_value;
}

The state of the operation is kept as local variables in the coroutine frame which lives until the operation completes, so there is no need to move state into the next continuation/lambda with the coroutine approach.

Hello, you forget the return value in operator new in the following type:
`struct my_promise_type
{
void* operator new(std::size_t size)
{
void* ptr = my_custom_allocate(size);
if (!ptr) throw std::bad_alloc{};
}

void operator delete(void* ptr, std::size_t size)
{
my_custom_free(ptr, size);
}

...
};
`

Fixed. Thanks @RainerGrimm!

Just wanted to drop a quick thanks for the write-up. It has been immensely helpful.

Thanks a lot for putting up the coroutines series to demystify the internals of coroutines.

nit: in the "some_coroutine " code example in "Obtaining the return object" section, should the comment "It's constructor takes..." be "Its ..."?

One Q:
"Note that even if you customise the memory allocation strategy for a coroutine, the compiler is still allowed to elide the call to your memory allocator."

In the case where the call is elided, is it possible the calling function is calling regular "operator new" for the coroutine activation frame on the heap? I'm wondering with custom memory allocator, whether there is some kind of real-time guarantee for applications cannot do heap allocation on real-time thread. Thanks.

you said:
"Note that it is undefined behaviour to resume() a coroutine that is suspended at the final_suspend point. The only thing you can do with a coroutine suspended here is destroy() it.".

But it is not allowed to destroy (using destroy()) the coroutine in its final suspend's await_suspend function, right?
I am not sure if a coroutine is already suspended in the function await_suspend.
I met some weird things when I tried to destroy() the coroutine before await_suspend return.

The coroutine is not considered suspended until execution reaches the await_suspend() method. So it is undefinded behaviour if you try to destroy() it before that. E.g. in the final_suspend() method.

The coroutine is not considered suspended until execution reaches the await_suspend() method. So it is undefinded behaviour if you try to destroy() it before that. E.g. in the final_suspend() method.

my code is like this

struct FinalAwaiter : std::suspend_always {

	std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) {
		auto continuum = m_h.promise().m_continuum;

		// we will never resume this coroutine
		// the coroutine is `detached` from any task
		// we destroy the coroutine by my ourselves
		m_h.destroy();   // <- cause double destruct of local variables

		if (continuum) {
                        // co_wait
			return continuum;
		}
               // somebody directly .resume()
		return std::noop_coroutine();
	}
};

I'm destroy() ing the coroutine in (not before) await_suspend.
I guess I have reached the await_suspend, but I hadn't finished it.
PS: I'm using Visual Studio 2019 (16.8.4).
PS2: Your articles are really really helpful. I learn a lot from them.