/**
 *
 * TODO: Remove xrpl::basic_semaphore (and this file) and use
 * std::counting_semaphore.
 *
 * Background:
 * - PR: https://github.com/XRPLF/rippled/pull/5512/files
 * - std::counting_semaphore had a bug fixed in both GCC and Clang:
 *     * GCC PR 104928: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104928
 *     * LLVM PR 79265: https://github.com/llvm/llvm-project/pull/79265
 *
 * GCC:
 * According to GCC Bugzilla PR104928
 * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104928#c15), the fix is
 * scheduled for inclusion in GCC 16.0 (see comment #15, Target
 * Milestone: 16.0). It is not included in GCC 14.x or earlier, and there is no
 * indication that it will be backported to GCC 13.x or 14.x branches.
 *
 * Clang:
 * The fix for is included in Clang 19.1.0+
 *
 * Once the minimum compiler version is updated to > GCC 16.0 or Clang 19.1.0,
 * we can remove this file.
 *
 * WARNING: Avoid using std::counting_semaphore until the minimum compiler
 * version is updated.
 */

#pragma once

#include <condition_variable>
#include <mutex>

namespace xrpl {

template <class Mutex, class CondVar>
class basic_semaphore
{
private:
    Mutex m_mutex;
    CondVar m_cond;
    std::size_t m_count;

public:
    using size_type = std::size_t;

    /** Create the semaphore, with an optional initial count.
        If unspecified, the initial count is zero.
    */
    explicit basic_semaphore(size_type count = 0) : m_count(count)
    {
    }

    /** Increment the count and unblock one waiting thread. */
    void
    notify()
    {
        std::lock_guard lock{m_mutex};
        ++m_count;
        m_cond.notify_one();
    }

    /** Block until notify is called. */
    void
    wait()
    {
        std::unique_lock lock{m_mutex};
        while (m_count == 0)
            m_cond.wait(lock);
        --m_count;
    }

    /** Perform a non-blocking wait.
        @return `true` If the wait would be satisfied.
    */
    bool
    try_wait()
    {
        std::lock_guard lock{m_mutex};
        if (m_count == 0)
            return false;
        --m_count;
        return true;
    }
};

using semaphore = basic_semaphore<std::mutex, std::condition_variable>;

}  // namespace xrpl
