--- /dev/null
+/* Loosely-coupled notifications via the Publish-Subscribe pattern.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#define INCLUDE_LIST
+#define INCLUDE_STRING
+#include "config.h"
+
+#include "system.h"
+#include "coretypes.h"
+
+#include "pub-sub.h"
+
+\f
+#if CHECKING_P
+
+#include "selftest.h"
+
+namespace selftest {
+
+/* Selftests. */
+
+// A topic for use in selftests
+
+namespace snafu {
+
+struct paper_jam {};
+
+struct out_of_paper
+{
+ int tray;
+};
+
+struct ink_low
+{
+ std::string color;
+};
+
+struct subscriber
+{
+ virtual void on_message (const paper_jam &m) = 0;
+ virtual void on_message (const out_of_paper &m) = 0;
+ virtual void on_message (const ink_low &m) = 0;
+};
+
+} // namespace snafu
+
+static void
+test_example ()
+{
+ struct logger : public snafu::subscriber
+ {
+ void on_message (const snafu::paper_jam &) final override
+ {
+ m_log += "paper jam\n";
+ }
+ void on_message (const snafu::out_of_paper &m) final override
+ {
+ m_log += "out of paper (tray " + std::to_string (m.tray) + ")\n";
+ }
+ void on_message (const snafu::ink_low &m) final override
+ {
+ m_log += "ink low: " + m.color + "\n";
+ }
+
+ std::string m_log;
+ };
+
+ pub_sub::channel<snafu::subscriber> printer_a;
+ pub_sub::channel<snafu::subscriber> printer_b;
+ pub_sub::channel<snafu::subscriber> printer_c;
+
+ // No subscribers yet
+ ASSERT_EQ (printer_a.get_if_active (), nullptr);
+ ASSERT_EQ (printer_b.get_if_active (), nullptr);
+ ASSERT_EQ (printer_c.get_if_active (), nullptr);
+
+ // Subscribers to individual channels
+ logger log_a;
+ logger log_b;
+ logger log_c;
+ printer_a.add_subscriber (log_a);
+ printer_b.add_subscriber (log_b);
+ printer_c.add_subscriber (log_c);
+
+ // A subscriber to all channels
+ logger log_all;
+ printer_a.add_subscriber (log_all);
+ printer_b.add_subscriber (log_all);
+ printer_c.add_subscriber (log_all);
+
+ // The channels now have subscribers
+ ASSERT_EQ (printer_a.get_if_active (), &printer_a);
+ ASSERT_EQ (printer_b.get_if_active (), &printer_b);
+ ASSERT_EQ (printer_c.get_if_active (), &printer_c);
+
+ // Publish a message to each channel
+ printer_a.publish (snafu::paper_jam {});
+ printer_b.publish (snafu::out_of_paper {1});
+ printer_c.publish (snafu::ink_low {"cyan"});
+
+ // Verify that the subscribers got the messages they were meant to
+ ASSERT_EQ (log_a.m_log, "paper jam\n");
+ ASSERT_EQ (log_b.m_log, "out of paper (tray 1)\n");
+ ASSERT_EQ (log_c.m_log, "ink low: cyan\n");
+ ASSERT_EQ (log_all.m_log,
+ "paper jam\n"
+ "out of paper (tray 1)\n"
+ "ink low: cyan\n");
+}
+
+/* Run all of the selftests within this file. */
+
+void
+pub_sub_cc_tests ()
+{
+ test_example ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
--- /dev/null
+/* Loosely-coupled notifications via the Publish-Subscribe pattern.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_PUB_SUB_H
+#define GCC_PUB_SUB_H
+
+namespace pub_sub {
+
+template <typename Subscriber>
+class channel
+{
+public:
+ using subscriber = Subscriber;
+
+ // A node within the std::list
+ using subscription = typename std::list<subscriber *>::iterator;
+
+ /* Return this if this channel has subscribers, or nullptr if
+ there are none. */
+ const channel *
+ get_if_active () const
+ {
+ if (m_subscribers.empty ())
+ return nullptr;
+ return this;
+ }
+
+ template <typename Message>
+ void publish (const Message &m) const
+ {
+ for (auto sub : m_subscribers)
+ sub->on_message (m);
+ }
+
+ subscription
+ add_subscriber (subscriber &s)
+ {
+ return m_subscribers.insert (m_subscribers.end (), &s);
+ }
+ void unsubscribe (subscription s)
+ {
+ m_subscribers.remove (s);
+ }
+
+private:
+ std::list<subscriber *> m_subscribers;
+};
+
+} // namespace pub_sub
+
+#endif /* GCC_PUB_SUB_H */