unique_lock


template<class Strand>
class basic_unique_lock;

using unique_lock = basic_unique_lock<boost::asio::io_context::strand>;

template<class Strand>
class basic_unique_lock
{
public:
    basic_unique_lock(basic_mutex<Strand>& mutex,
          typename basic_fiber<Strand>::this_fiber this_fiber)
        : mutex_(&mutex)
        , owns_lock_(true)
    {
        mutex_->lock(this_fiber);
    }

    basic_unique_lock(basic_unique_lock&& o)
        : mutex_(o.mutex_)
        , owns_lock_(o.owns_lock_)
    {
        o.mutex_ = nullptr;
    }

    basic_unique_lock& operator=(basic_unique_lock&& o)
    {
        if (mutex_ && owns_lock_)
            mutex_->unlock();

        mutex_ = o.mutex_;
        owns_lock_ = o.owns_lock_;
        o.mutex_ = nullptr;
        return *this;
    }

    ~basic_unique_lock()
    {
        if (!mutex_) // moved
            return;

        if (owns_lock_)
            mutex_->unlock();
    }

    void lock(typename basic_fiber<Strand>::this_fiber this_fiber)
    {
        if (!mutex_)
            throw std::system_error{std::errc::operation_not_permitted};
        if (owns_lock_)
            throw std::system_error(std::errc::resource_deadlock_would_occur);

        mutex_->lock(this_fiber);
        owns_lock_ = true;
    }

    void unlock()
    {
        if (!mutex_ || !owns_lock_)
            throw std::system_error{std::errc::operation_not_permitted};

        mutex_->unlock();
        owns_lock_ = false;
    }

    basic_mutex<Strand>* release() noexcept
    {
        auto m = mutex_;
        mutex_ = nullptr;
        return m;
    }

    basic_mutex<Strand>* mutex() const noexcept
    {
        return mutex_;
    }

    bool owns_lock() const noexcept
    {
        return owns_lock_;
    }

private:
    basic_mutex<Strand> *mutex_;
    bool owns_lock_;
};