diff options
author | 2022-02-12 04:10:39 +1100 | |
---|---|---|
committer | 2022-02-12 04:10:39 +1100 | |
commit | a18509185569b8e637c7e129caab5eb75e4adff9 (patch) | |
tree | 4e312575e03db0cb0fec4d90a8bb91d831629130 /src/lib/util/notifier.h | |
parent | f0fc52d32c4933b76bfc7ba91838b1b29b962595 (diff) |
Addressed some Lua scripting pitfalls. (#9294)
Addressed pure virtual function call crash on end of emulation session
if you haven't explicitly removed all address space taps, memory
corruption on end of emulation session if you haven't explicitly removed
all address space change notifiers, and symbol being garbage-collected
out from under you while you have parsed expressions or other symbol
tables that depend on them.
Removed the copy constructor for parsed expressions as the underlying
C++ copy constructor appears to be broken, and simplified symbol table
constructors. Also made symbol table add methods return the new entry
to avoid the need for an extra lookup.
Fixed breakpoint/watchpoint objects being inappropriately copied into
the tables returned by bplist() and wplist(), allowing the enabled
property to be modifiable for breakpoint and watchpoint objects in Lua.
Fixed drivers and devices causing a new memory pass-through handler to
be allocated on each soft reset, and fixed multiple instances of taps
being installed in the event the machine is reset before the tap is
removed.
Added classes for managing broadcast subscriptions, and adapted address
spaces to use this for change notifications.
Diffstat (limited to 'src/lib/util/notifier.h')
-rw-r--r-- | src/lib/util/notifier.h | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/src/lib/util/notifier.h b/src/lib/util/notifier.h new file mode 100644 index 00000000000..b5dfaaa0f3c --- /dev/null +++ b/src/lib/util/notifier.h @@ -0,0 +1,240 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/// \file +/// \brief Simple broadcast notifier +/// +/// Classes for managing subscriptions and calling multiple listener +/// functions for notifications. +#ifndef MAME_LIB_UTIL_NOTIFIER_H +#define MAME_LIB_UTIL_NOTIFIER_H + +#pragma once + +#include "delegate.h" + +#include <cstddef> +#include <memory> +#include <utility> +#include <vector> + + +namespace util { + +/// \brief A subscription to a notification source +/// +/// Class for representing a subscription to a notifier. Automatically +/// unsubscribes on destruction, if explicitly reset, if assigned to, or +/// when taking ownership of a different subscription by move +/// assignment. +/// \sa notifier +class notifier_subscription +{ +public: + /// \brief Create an empty subscription + /// + /// Initialises an instance not referring to a subscription. + notifier_subscription() noexcept : m_token(), m_live(nullptr), m_index(0U) { } + + /// \brief Transfer ownership of a subscription + /// + /// Transfers ownership of a subscription to a new instance. + /// \param [in,out] that The subscription to transfer ownership + /// from. Will no longer refer to a subscription after ownership + /// is transferred away. + notifier_subscription(notifier_subscription &&that) noexcept : + m_token(std::move(that.m_token)), + m_live(that.m_live), + m_index(that.m_index) + { + that.m_token.reset(); + } + + /// \brief Unsubscribe and destroy a subscription + /// + /// Unsubscribes if the subscription is active and cleans up the + /// subscription instance. + ~notifier_subscription() noexcept + { + auto token(m_token.lock()); + if (token) + (*m_live)[m_index] = false; + } + + /// \brief Swap two subscriptions + /// + /// Exchanges ownership of subscriptions between two instances. + /// \param [in,out] that The subscription to exchange ownership + /// with. + void swap(notifier_subscription &that) noexcept + { + using std::swap; + swap(m_token, that.m_token); + swap(m_live, that.m_live); + swap(m_index, that.m_index); + } + + /// \brief Unsubscribe from notifications + /// + /// If the instance refers to an active subscription, cancel it so + /// no future notifications will be received. + void reset() noexcept + { + auto token(m_token.lock()); + if (token) + (*m_live)[m_index] = false; + m_token.reset(); + } + + /// \brief Test whether a subscription is active + /// + /// Tests whether a subscription is active. A subscription will be + /// inactive if it is default constructed, reset, transferred away, + /// or if the underlying notifier is destructed. + /// \return True if the subscription is active, false otherwise. + explicit operator bool() const noexcept { return bool(m_token.lock()); } + + /// \brief Transfer ownership of a subscription + /// + /// Transfers ownership of a subscription to an existing instance. + /// If the subscription is active, it will be cancelled before it + /// takes ownership of the other subscription. + /// \param [in,out] that The subscription to transfer ownership + /// from. Will no longer refer to a subscription after ownership + /// is transferred away. + /// \return A reference to the instance that ownership was + /// transferred to. + notifier_subscription &operator=(notifier_subscription &&that) noexcept + { + { + auto token(m_token.lock()); + if (token) + (*m_live)[m_index] = false; + } + m_token = std::move(that.m_token); + m_live = that.m_live; + m_index = that.m_index; + that.m_token.reset(); + return *this; + } + +protected: + notifier_subscription( + std::shared_ptr<int> const &token, + std::vector<bool> &live, + std::vector<bool>::size_type index) : + m_token(token), + m_live(&live), + m_index(index) + { + } + +private: + notifier_subscription(notifier_subscription const &) = delete; + notifier_subscription &operator=(notifier_subscription const &) = delete; + + std::weak_ptr<int> m_token; + std::vector<bool> *m_live; + std::vector<bool>::size_type m_index; +}; + + +/// \brief Broadcast notifier +/// +/// Calls multiple listener functions. Allows listeners to subscribe +/// and unsubscribe. Subscriptions are managed using +/// \c notifier_subscription instances. +/// \tparam Params Argument types for the listener functions. +/// \sa notifier_subscription +template <typename... Params> +class notifier +{ +public: + /// \brief Listener delegate type + /// + /// The delegate type used to represent listener functions. + using delegate_type = delegate<void (Params...)>; + + /// \brief Create a new notifier + /// + /// Creates a new notifier with no initial subscribers. + notifier() : m_token(std::make_shared<int>(0)) { } + + /// \brief Destroy a notifier + /// + /// Destroys a notifier, causing any subscriptions to become + /// inactive. + ~notifier() noexcept { m_token.reset(); } + + /// \brief Add a listener + /// + /// Adds a listener function subscription, returning an object for + /// managing the subscription. + /// \param [in] listener The function to be called on notifications. + /// \return A subscription object. Destroy the object or call its + /// \c reset member function to unsubscribe. + notifier_subscription subscribe(delegate_type &&listener) + { + struct subscription_impl : notifier_subscription + { + subscription_impl(notifier &host, std::size_t index) noexcept : + notifier_subscription(host.m_token, host.m_live, index) + { + } + }; + for (std::size_t i = 0U; m_listeners.size() > i; ++i) + { + if (!m_live[i]) + { + m_live[i] = true; + m_listeners[i] = std::move(listener); + return subscription_impl(*this, i); + } + } + m_live.emplace_back(true); + try + { + m_listeners.emplace_back(std::move(listener)); + } + catch (...) + { + m_live.pop_back(); + throw; + } + return subscription_impl(*this, m_listeners.size() - 1); + } + + /// \brief Call listeners + /// + /// Calls all active listener functions. + /// \param [in] args Arguments to pass to the listener functions. + void operator()(Params... args) const + { + for (std::size_t i = 0U; m_listeners.size() > i; ++i) + { + if (m_live[i]) + m_listeners[i](args...); + } + } + +private: + notifier(notifier const &) = delete; + notifier &operator=(notifier const &) = delete; + + std::shared_ptr<int> m_token; + std::vector<bool> m_live; + std::vector<delegate_type> m_listeners; +}; + + +/// \brief Swap two notifier subscriptions +/// +/// Exchanges ownership of two notifier subscriptions. Allows the +/// swappable idiom to be used with notifier subscriptions. +/// \param [in,out] x Takes ownership of the subscription from \p y. +/// \param [in,out] y Takes ownership of the subscription from \p x. +/// \sa notifier_subscription +inline void swap(notifier_subscription &x, notifier_subscription &y) noexcept { x.swap(y); } + +} // namespace util + +#endif // MAME_LIB_UTIL_NOTIFIER_H |