// // Copyright 2016 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // signal_utils: // Helper classes for tracking dependent state changes between objects. // These changes are signaled to the dependent class via channels. // See design document: // https://docs.google.com/document/d/15Edfotqg6_l1skTEL8ADQudF_oIdNa7i8Po43k6jMd4/ #ifndef LIBANGLE_SIGNAL_UTILS_H_ #define LIBANGLE_SIGNAL_UTILS_H_ #include #include "common/angleutils.h" #include "common/debug.h" namespace angle { // Interface that the depending class inherits from. template class SignalReceiver { public: virtual ~SignalReceiver() = default; virtual void signal(ChannelID channelID, MessageT... message) = 0; }; template class ChannelBinding; // The host class owns the channel. It uses the channel to fire signals to the receiver. template class BroadcastChannel final : NonCopyable { public: BroadcastChannel(); ~BroadcastChannel(); void signal(MessageT... message) const; void reset(); bool empty() const; private: // Only the ChannelBinding class should add or remove receivers. friend class ChannelBinding; void addReceiver(ChannelBinding *receiver); void removeReceiver(ChannelBinding *receiver); std::vector *> mReceivers; }; template BroadcastChannel::BroadcastChannel() { } template BroadcastChannel::~BroadcastChannel() { reset(); } template void BroadcastChannel::addReceiver( ChannelBinding *receiver) { ASSERT(std::find(mReceivers.begin(), mReceivers.end(), receiver) == mReceivers.end()); mReceivers.push_back(receiver); } template void BroadcastChannel::removeReceiver( ChannelBinding *receiver) { auto iter = std::find(mReceivers.begin(), mReceivers.end(), receiver); ASSERT(iter != mReceivers.end()); mReceivers.erase(iter); } template void BroadcastChannel::signal(MessageT... message) const { if (mReceivers.empty()) return; for (const auto *receiver : mReceivers) { receiver->signal(message...); } } template void BroadcastChannel::reset() { for (auto receiver : mReceivers) { receiver->onChannelClosed(); } mReceivers.clear(); } template bool BroadcastChannel::empty() const { return mReceivers.empty(); } // The dependent class keeps bindings to the host's BroadcastChannel. template class ChannelBinding final { public: ChannelBinding(SignalReceiver *receiver, ChannelID channelID); ~ChannelBinding(); ChannelBinding(const ChannelBinding &other) = default; ChannelBinding &operator=(const ChannelBinding &other) = default; void bind(BroadcastChannel *channel); void reset(); void signal(MessageT... message) const; void onChannelClosed(); private: BroadcastChannel *mChannel; SignalReceiver *mReceiver; ChannelID mChannelID; }; template ChannelBinding::ChannelBinding( SignalReceiver *receiver, ChannelID channelID) : mChannel(nullptr), mReceiver(receiver), mChannelID(channelID) { ASSERT(receiver); } template ChannelBinding::~ChannelBinding() { reset(); } template void ChannelBinding::bind(BroadcastChannel *channel) { ASSERT(mReceiver); if (mChannel) { mChannel->removeReceiver(this); } mChannel = channel; if (mChannel) { mChannel->addReceiver(this); } } template void ChannelBinding::reset() { bind(nullptr); } template void ChannelBinding::signal(MessageT... message) const { mReceiver->signal(mChannelID, message...); } template void ChannelBinding::onChannelClosed() { mChannel = nullptr; } } // namespace angle #endif // LIBANGLE_SIGNAL_UTILS_H_