/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 2018 Danny Robson <danny@nerdcruft.net>
 */

#ifndef CRUFT_UTIL_THREAD_EVENT_HPP
#define CRUFT_UTIL_THREAD_EVENT_HPP

#include <atomic>

namespace cruft::thread {
    /// a reusable synchronisation object that allows threads to wait until
    /// notify is called.
    ///
    /// there is no internal state so it is easy to create races between wait
    /// and notify calls. this makes the class mostly suitable for recurring
    /// events.
    ///
    /// the user should ensure no callers are waiting at destruction time
    /// otherwise they may remain blocked indefinitely.
    ///
    /// the address of the object is important so it must _never_ be
    /// relocated in any manner if any caller may be waiting. it may be safe
    /// to do so if there are no callers waiting (but the relevant functions
    /// are deleted for safety anyway).
    class event {
    public:
        event ();

        event (const event&) = delete;
        event (event&&) = delete;
        event& operator= (const event&) = delete;
        event& operator= (event&&) = delete;

        /// block until notified
        void wait (void);

        /// wake all threads that are waiting
        int notify_one (void);

        int notify_all (void);

        /// wait `count' threads that are waiting
        int notify (int count);

    private:
        alignas (4) std::atomic<int> value;
    };
}

#endif