File pthread.h

File List > coroutine > pthread.h

Go to the documentation of this file.

#ifndef COROUTINE_PTHREAD_UTILITY_H
#define COROUTINE_PTHREAD_UTILITY_H
#if __has_include(<pthread.h>)
#include <pthread.h>
#else
#error "expect <pthread.h> for this file"
#endif
#include <system_error>

#include <coroutine/return.h>

namespace coro {

class continue_on_pthread final {
    static uint32_t spawn(pthread_t& tid, const pthread_attr_t* attr,
                          coroutine_handle<void> coro) noexcept(false);
    static void* on_pthread(void* ptr) noexcept(false);

  private:
    pthread_t* const ptr;
    const pthread_attr_t* const attr;

  public:
    bool await_ready() const noexcept {
        return false;
    }
    void await_resume() noexcept {
    }
    void await_suspend(coroutine_handle<void> coro) noexcept(false) {
        if (int ec = spawn(*ptr, attr, coro))
            throw std::system_error{ec, std::system_category(),
                                    "pthread_create"};
    }

  public:
    continue_on_pthread(pthread_t& tid, const pthread_attr_t* attr)
        : ptr{&tid}, attr{attr} {
    }
};

class pthread_spawn_promise {
  public:
    pthread_t tid{};

  public:
    constexpr auto initial_suspend() noexcept {
        return suspend_never{};
    }
    constexpr void return_void() noexcept {
    }
    void unhandled_exception() noexcept(false) {
        throw;
    }

    auto await_transform(const pthread_attr_t* attr) noexcept(false) {
        if (tid) // already created.
            throw std::logic_error{"pthread's spawn must be used once"};

        // provide the address at this point
        return continue_on_pthread{tid, attr};
    }
    inline auto await_transform(pthread_attr_t* attr) noexcept(false) {
        return await_transform(static_cast<const pthread_attr_t*>(attr));
    }

    template <typename Awaitable>
    decltype(auto) await_transform(Awaitable&& a) noexcept {
        return std::forward<Awaitable&&>(a);
    }
};

class pthread_joiner final {
    friend class promise_type;

  public:
    class promise_type final : public pthread_spawn_promise {
      public:
        constexpr auto final_suspend() noexcept {
            return suspend_always{};
        }
        auto get_return_object() noexcept {
            return pthread_joiner{this};
        }
    };

  private:
    pthread_spawn_promise* promise;

  private:
    explicit pthread_joiner(promise_type* p) noexcept(false);

  public:
    ~pthread_joiner() noexcept(false);

    pthread_joiner(const pthread_joiner&) = delete;
    pthread_joiner& operator=(const pthread_joiner&) = delete;
    pthread_joiner(pthread_joiner&&) = default;
    pthread_joiner& operator=(pthread_joiner&&) = default;

    operator pthread_t() const noexcept {
        return promise->tid;
    }
};

class pthread_detacher final {
    friend class promise_type;

  public:
    class promise_type final : public pthread_spawn_promise {
      public:
        // detacher doesn't care about the coroutine frame's life cycle
        // it does nothing after `co_return`
        constexpr auto final_suspend() noexcept {
            return suspend_never{};
        }
        auto get_return_object() noexcept {
            return pthread_detacher{this};
        }
    };

  private:
    pthread_spawn_promise* promise;

  private:
    explicit pthread_detacher(promise_type* p) noexcept(false);

  public:
    ~pthread_detacher() noexcept(false);
    pthread_detacher(const pthread_detacher&) = delete;
    pthread_detacher& operator=(const pthread_detacher&) = delete;
    pthread_detacher(pthread_detacher&&) = default;
    pthread_detacher& operator=(pthread_detacher&&) = default;

    operator pthread_t() const noexcept {
        return promise->tid;
    }
};

} // namespace coro

#endif // COROUTINE_PTHREAD_UTILITY_H