Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discussion for Understanding the promise type #3

Open
lewissbaker opened this issue Sep 5, 2018 · 10 comments
Open

Discussion for Understanding the promise type #3

lewissbaker opened this issue Sep 5, 2018 · 10 comments

Comments

@lewissbaker
Copy link
Owner

No description provided.

@kghost
Copy link

kghost commented Sep 6, 2018

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 ?

@lewissbaker
Copy link
Owner Author

lewissbaker commented Sep 10, 2018

@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.

@RainerGrimm
Copy link

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);
}

...
};
`

@lewissbaker
Copy link
Owner Author

Fixed. Thanks @RainerGrimm!

@d-karl
Copy link

d-karl commented Nov 17, 2019

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

@cadenzasong
Copy link

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 ..."?

@cadenzasong
Copy link

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.

@lhprojects
Copy link

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.

@lewissbaker
Copy link
Owner Author

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.

@lhprojects
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants