C++/WinRT中的IAsyncInfo是采用coroutine来实现的。

https://github.com/microsoft/cppwinrt/blob/master/strings/base_coroutine_foundation.h#L354定义了promise_base这个模板结构:

    template <typename Derived, typename AsyncInterface, typename TProgress = void>
    struct promise_base : implements<Derived, AsyncInterface, Windows::Foundation::IAsyncInfo>
    {
        ...
    }

promise_base是IAsyncAction, IAsyncOperation等等的promise_type:

    template <typename... Args>
    struct coroutine_traits<winrt::Windows::Foundation::IAsyncAction, Args...>
    {
        struct promise_type final : winrt::impl::promise_base<promise_type, winrt::Windows::Foundation::IAsyncAction>
        {
            void return_void() const noexcept
            {
            }
        };
    };
...

继续看promise_base,首先实现了Windows::Foundation::IAsyncInfo,也就是说这个promise_base其实是一个IInspectable,自带有引用计数管理。当对应的coroutine被创建时,会在堆上创建这个promise_base,并将其引用计数设置为1。

promise_base实现了IAsyncInfo的Completed,Id,Status,ErrorCode等辖属,以及Cancel,Close,GetResults等方法。

可以看到get_return_object是直接返回了AsyncInterface这个类型:

        AsyncInterface get_return_object() const noexcept
        {
            return *this;
        }

因为promise_base实现了AsyncInterface,所以在返回AsyncInterface这个投射类型的时候,只是返回了一个智能指针,并增加相应的引用计数而已。

然后我们可以看到promise_base改写了Release方法:

        unsigned long __stdcall Release() noexcept
        {
            uint32_t const remaining = this->subtract_reference();

            if (remaining == 0)
            {
                std::atomic_thread_fence(std::memory_order_acquire);
                coroutine_handle<Derived>::from_promise(*static_cast<Derived*>(this)).destroy();
            }

            return remaining;
        }

同时注意到它的final_suspend返回的awaiter是这样的:


        struct final_suspend_awaiter
        {
            promise_base* promise;

            bool await_ready() const noexcept
            {
                return false;
            }

            void await_resume() const noexcept
            {
            }

            bool await_suspend(coroutine_handle<>) const noexcept
            {
                promise->set_completed();
                uint32_t const remaining = promise->subtract_reference();

                if (remaining == 0)
                {
                    std::atomic_thread_fence(std::memory_order_acquire);
                }

                return remaining > 0;
            }
        };

Release核final_suspend两者调用的先后顺序不可知,因为这两者的实现必须考虑这个状况。并且因为Release和final_suspend可能在不同的线程上执行,所以还需要采用原子操作来避免增加引用计数的时候带来的竞争问题。

promise_base中不少地方都采用了Slim Reader/Writer (SRW) Locks,比如Completed的实现中在操作相应状态的时候就采用了slimlock。另外说一句,Completed只允许一个回调函数。

unhandled_exception()的时候会把hresult_canceled 异常当作cancel事件。异常会被保存在环境变量m_exception 中,若使用ErrorCode获取错误码是,会将异常重新抛出并转化为错误码。

promise_base相关的awaiter类型在https://github.com/microsoft/cppwinrt/blob/master/strings/base_coroutine_foundation.h#L131:

    template <typename Async>
    struct await_adapter : enable_await_cancellation
    {
        ...
    }

其await_suspend实现如下:

        void await_suspend(coroutine_handle<> handle)
        {
            auto extend_lifetime = async;
            async.Completed([this, handler = disconnect_aware_handler{ handle }](auto&&, auto operation_status) mutable
            {
                status = operation_status;
                handler();
            });
        }

值得注意的是其中的disconnect_aware_handler:

    struct disconnect_aware_handler
    {
        disconnect_aware_handler(coroutine_handle<> handle) noexcept
            : m_handle(handle) { }

        disconnect_aware_handler(disconnect_aware_handler&& other) noexcept
            : m_context(std::move(other.m_context))
            , m_handle(std::exchange(other.m_handle, {})) { }

        ~disconnect_aware_handler()
        {
            if (m_handle) Complete();
        }

        void operator()()
        {
            Complete();
        }

    private:
        resume_apartment_context m_context;
        coroutine_handle<> m_handle;

        void Complete()
        {
            resume_apartment(m_context, std::exchange(m_handle, {}));
        }
    };

disconnect_aware_handler会确保coroutine的执行结果和co_await操作是在同一个apartment_context里面,也就是同一线程上。

参考